summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/sbc_harness/config/config.h2
-rw-r--r--gdb-helpers/libcr.py11
-rw-r--r--libcr/coroutine.c49
-rw-r--r--libcr/include/libcr/coroutine.h21
-rw-r--r--libcr_ipc/CMakeLists.txt1
-rw-r--r--libcr_ipc/include/libcr_ipc/chan.h6
-rw-r--r--libcr_ipc/include/libcr_ipc/mutex.h4
-rw-r--r--libcr_ipc/include/libcr_ipc/owned_mutex.h79
-rw-r--r--libcr_ipc/include/libcr_ipc/rpc.h6
-rw-r--r--libcr_ipc/include/libcr_ipc/select.h1
-rw-r--r--libcr_ipc/include/libcr_ipc/sema.h5
-rw-r--r--libhw/rp2040_include/libhw/w5500.h2
-rw-r--r--libhw/w5500.c136
-rw-r--r--libhw/w5500_ll.h67
-rw-r--r--libmisc/include/libmisc/log.h12
15 files changed, 311 insertions, 91 deletions
diff --git a/cmd/sbc_harness/config/config.h b/cmd/sbc_harness/config/config.h
index daa01dd..2b6a6cf 100644
--- a/cmd/sbc_harness/config/config.h
+++ b/cmd/sbc_harness/config/config.h
@@ -27,6 +27,8 @@
#define CONFIG_W5500_DEBUG 1 /* bool */
+#define CONFIG_W5500_LL_DEBUG 0 /* bool */
+
/* 9P *************************************************************************/
#define CONFIG_9P_PORT 564
diff --git a/gdb-helpers/libcr.py b/gdb-helpers/libcr.py
index c07b679..3ffafce 100644
--- a/gdb-helpers/libcr.py
+++ b/gdb-helpers/libcr.py
@@ -26,6 +26,14 @@ def gdb_unregister_unwinder(
gdb.invalidate_cached_frames()
+def gdb_is_on_os() -> bool:
+ try:
+ gdb.execute("info proc", to_string=True)
+ return True
+ except gdb.error:
+ return False
+
+
class gdb_JmpBuf:
"""Our own in-Python GDB-specific implementation of `jmp_buf`"""
@@ -105,7 +113,8 @@ class CrGlobals:
self._breakpoint.enabled = True
gdb.execute(f"call (void)cr_gdb_readjmp({env_ptr_expr})")
self._breakpoint.enabled = False
- gdb.execute("queue-signal SIGWINCH")
+ if gdb_is_on_os():
+ gdb.execute("queue-signal SIGWINCH")
return self._breakpoint.env
def _on_cont(self, event: gdb.Event) -> None:
diff --git a/libcr/coroutine.c b/libcr/coroutine.c
index a947ae9..aa23d58 100644
--- a/libcr/coroutine.c
+++ b/libcr/coroutine.c
@@ -165,7 +165,7 @@
#define _CR_SIG_GDB SIGWINCH
#endif
- bool cr_is_in_intrhandler(void) {
+ bool cr_plat_is_in_intrhandler(void) {
sigset_t cur_mask;
sigfillset(&cur_mask);
sigprocmask(0, NULL, &cur_mask);
@@ -179,7 +179,7 @@
return false;
}
static inline bool _cr_plat_are_interrupts_enabled(void) {
- assert(!cr_is_in_intrhandler());
+ assert(!cr_plat_is_in_intrhandler());
sigset_t cur_mask;
sigfillset(&cur_mask);
sigprocmask(0, NULL, &cur_mask);
@@ -187,7 +187,7 @@
}
static inline void cr_plat_wait_for_interrupt(void) {
- assert(!cr_is_in_intrhandler());
+ assert(!cr_plat_is_in_intrhandler());
assert(!_cr_plat_are_interrupts_enabled());
sigset_t set;
sigemptyset(&set);
@@ -197,14 +197,14 @@
sigprocmask(SIG_SETMASK, &set, NULL);
}
bool _cr_plat_save_and_disable_interrupts(void) {
- assert(!cr_is_in_intrhandler());
+ assert(!cr_plat_is_in_intrhandler());
sigset_t all, old;
sigfillset(&all);
sigprocmask(SIG_SETMASK, &all, &old);
return !sigismember(&old, _CR_SIG_SENTINEL);
}
void _cr_plat_enable_interrupts(void) {
- assert(!cr_is_in_intrhandler());
+ assert(!cr_plat_is_in_intrhandler());
assert(!_cr_plat_are_interrupts_enabled());
sigset_t zero;
sigemptyset(&zero);
@@ -224,7 +224,7 @@
#endif
}
#elif __ARM_ARCH_6M__ && __ARM_EABI__
- bool cr_is_in_intrhandler(void) {
+ bool cr_plat_is_in_intrhandler(void) {
uint32_t isr_number;
asm volatile ("mrs %0, ipsr"
: /* %0 */"=l"(isr_number)
@@ -232,7 +232,7 @@
return isr_number != 0;
}
ALWAYS_INLINE static bool _cr_plat_are_interrupts_enabled(void) {
- assert(!cr_is_in_intrhandler());
+ assert(!cr_plat_is_in_intrhandler());
uint32_t primask;
asm volatile ("mrs %0, PRIMASK"
: /* %0 */"=l"(primask)
@@ -241,7 +241,7 @@
}
ALWAYS_INLINE static void cr_plat_wait_for_interrupt(void) {
- assert(!cr_is_in_intrhandler());
+ assert(!cr_plat_is_in_intrhandler());
assert(!_cr_plat_are_interrupts_enabled());
asm volatile ("wfi\n"
"cpsie i\n"
@@ -250,13 +250,13 @@
:::"memory");
}
bool _cr_plat_save_and_disable_interrupts(void) {
- assert(!cr_is_in_intrhandler());
+ assert(!cr_plat_is_in_intrhandler());
bool were_enabled = _cr_plat_are_interrupts_enabled();
asm volatile ("cpsid i");
return were_enabled;
}
void _cr_plat_enable_interrupts(void) {
- assert(!cr_is_in_intrhandler());
+ assert(!cr_plat_is_in_intrhandler());
assert(!_cr_plat_are_interrupts_enabled());
asm volatile ("cpsie i");
}
@@ -633,8 +633,14 @@ void coroutine_main(void) {
}
bool saved = cr_save_and_disable_interrupts();
assert(saved);
- assert(!cr_is_in_intrhandler());
+ assert(!cr_plat_is_in_intrhandler());
coroutine_running = 0;
+#if CONFIG_COROUTINE_GDB
+ /* Some pointless call to prevent cr_gdb_readjmp() from
+ * getting pruned out of the firmware image. */
+ if (coroutine_table[0].state != CR_NONE)
+ cr_gdb_readjmp(&coroutine_table[0].env);
+#endif
while (coroutine_cnt) {
cid_t next;
while ( !((next = coroutine_ringbuf_pop())) ) {
@@ -697,7 +703,7 @@ static inline void _cr_yield() {
void cr_yield(void) {
debugf("cid=%zu: cr_yield()", coroutine_running);
- assert(!cr_is_in_intrhandler());
+ assert(!cr_plat_is_in_intrhandler());
assert_cid_state(coroutine_running, state == CR_RUNNING);
bool saved = cr_save_and_disable_interrupts();
@@ -709,7 +715,7 @@ void cr_yield(void) {
void cr_pause_and_yield(void) {
debugf("cid=%zu: cr_pause_and_yield()", coroutine_running);
- assert(!cr_is_in_intrhandler());
+ assert(!cr_plat_is_in_intrhandler());
assert_cid_state(coroutine_running, state == CR_RUNNING);
bool saved = cr_save_and_disable_interrupts();
@@ -720,7 +726,7 @@ void cr_pause_and_yield(void) {
[[noreturn]] void cr_exit(void) {
debugf("cid=%zu: cr_exit()", coroutine_running);
- assert(!cr_is_in_intrhandler());
+ assert(!cr_plat_is_in_intrhandler());
assert_cid_state(coroutine_running, state == CR_RUNNING);
(void)cr_save_and_disable_interrupts();
@@ -737,7 +743,7 @@ static void _cr_unpause(cid_t cid) {
void cr_unpause(cid_t cid) {
debugf("cr_unpause(%zu)", cid);
- assert(!cr_is_in_intrhandler());
+ assert(!cr_plat_is_in_intrhandler());
assert_cid_state(coroutine_running, state == CR_RUNNING);
bool saved = cr_save_and_disable_interrupts();
@@ -747,17 +753,26 @@ void cr_unpause(cid_t cid) {
void cr_unpause_from_intrhandler(cid_t cid) {
debugf("cr_unpause_from_intrhandler(%zu)", cid);
- assert(cr_is_in_intrhandler());
+ assert(cr_plat_is_in_intrhandler());
_cr_unpause(cid);
}
cid_t cr_getcid(void) {
- assert(!cr_is_in_intrhandler());
+ assert(!cr_plat_is_in_intrhandler());
assert_cid_state(coroutine_running, state == CR_RUNNING);
return coroutine_running;
}
+void cr_assert_in_coroutine(void) {
+ assert(!cr_plat_is_in_intrhandler());
+ assert_cid_state(coroutine_running, state == CR_RUNNING);
+}
+
+void cr_assert_in_intrhandler(void) {
+ assert(cr_plat_is_in_intrhandler());
+}
+
/* cr_cid_info() **************************************************************/
#if CONFIG_COROUTINE_MEASURE_STACK
diff --git a/libcr/include/libcr/coroutine.h b/libcr/include/libcr/coroutine.h
index f6c5e14..eb5828b 100644
--- a/libcr/include/libcr/coroutine.h
+++ b/libcr/include/libcr/coroutine.h
@@ -164,6 +164,27 @@ bool cr_is_in_intrhandler(void);
*/
void cr_unpause_from_intrhandler(cid_t);
+/**
+ * cr_assert_in_coroutine() asserts that it is being called from a
+ * running coroutine.
+ */
+#ifdef NDEBUG
+#define cr_assert_in_coroutine() ((void)0)
+#else
+void cr_assert_in_coroutine(void);
+#endif
+
+
+/**
+ * cr_assert_in_intrhandler() asserts that it is being called from an
+ * interrupt handler.
+ */
+#ifdef NDEBUG
+#define cr_assert_in_intrhandler() ((void)0)
+#else
+void cr_assert_in_intrhandler(void);
+#endif
+
/* answering questions about coroutines ***************************************/
/* While the following are defined here unconditionally, the
diff --git a/libcr_ipc/CMakeLists.txt b/libcr_ipc/CMakeLists.txt
index 2a1c747..3ab56ab 100644
--- a/libcr_ipc/CMakeLists.txt
+++ b/libcr_ipc/CMakeLists.txt
@@ -13,6 +13,7 @@ set(ipc_tests
chan
#select
#mutex
+ #owned_mutex
#rpc
#sema
)
diff --git a/libcr_ipc/include/libcr_ipc/chan.h b/libcr_ipc/include/libcr_ipc/chan.h
index 3c4095c..da4f08e 100644
--- a/libcr_ipc/include/libcr_ipc/chan.h
+++ b/libcr_ipc/include/libcr_ipc/chan.h
@@ -11,7 +11,7 @@
#include <stddef.h> /* for size_t */
#include <string.h> /* for memcpy */
-#include <libcr/coroutine.h> /* for cid_t, cr_unpause(), cr_pause_and_yield() */
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
#include <libcr_ipc/_linkedlist.h>
@@ -80,21 +80,25 @@
} NAME##_t; \
\
static inline void NAME##_send(NAME##_t *ch, VAL_T val) { \
+ cr_assert_in_coroutine(); \
_cr_chan_xfer(_CR_CHAN_SENDER, &ch->core, &val, sizeof(val)); \
} \
\
static inline VAL_T NAME##_recv(NAME##_t *ch) { \
+ cr_assert_in_coroutine(); \
VAL_T val; \
_cr_chan_xfer(_CR_CHAN_RECVER, &ch->core, &val, sizeof(val)); \
return val; \
} \
\
static inline bool NAME##_can_send(NAME##_t *ch) { \
+ cr_assert_in_coroutine(); \
return ch->core.waiters.front && \
ch->core.waiter_typ == _CR_CHAN_RECVER; \
} \
\
static inline bool NAME##_can_recv(NAME##_t *ch) { \
+ cr_assert_in_coroutine(); \
return ch->core.waiters.front && \
ch->core.waiter_typ == _CR_CHAN_SENDER; \
}
diff --git a/libcr_ipc/include/libcr_ipc/mutex.h b/libcr_ipc/include/libcr_ipc/mutex.h
index eee4f01..cba8c19 100644
--- a/libcr_ipc/include/libcr_ipc/mutex.h
+++ b/libcr_ipc/include/libcr_ipc/mutex.h
@@ -9,7 +9,7 @@
#include <stdbool.h> /* for bool */
-#include <libcr/coroutine.h> /* for cid_t, cr_unpause(), cr_pause_and_yield() */
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
#include <libcr_ipc/_linkedlist.h>
@@ -40,6 +40,7 @@ typedef struct {
*/
static inline void cr_mutex_lock(cr_mutex_t *mu) {
assert(mu);
+ cr_assert_in_coroutine();
if (!mu->locked) /* non-blocking fast-path */
mu->locked = true;
@@ -63,6 +64,7 @@ static inline void cr_mutex_lock(cr_mutex_t *mu) {
*/
static inline void cr_mutex_unlock(cr_mutex_t *mu) {
assert(mu);
+ cr_assert_in_coroutine();
assert(mu->locked);
if (mu->waiters.front) {
diff --git a/libcr_ipc/include/libcr_ipc/owned_mutex.h b/libcr_ipc/include/libcr_ipc/owned_mutex.h
new file mode 100644
index 0000000..c66240f
--- /dev/null
+++ b/libcr_ipc/include/libcr_ipc/owned_mutex.h
@@ -0,0 +1,79 @@
+/* libcr_ipc/owned_mutex.h - Owned mutexes for libcr
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBCR_IPC_OWNED_MUTEX_H_
+#define _LIBCR_IPC_OWNED_MUTEX_H_
+
+#include <stdbool.h> /* for bool */
+
+#include <libcr/coroutine.h> /* for cid_t, cr_unpause(), cr_pause_and_yield() */
+
+#include <libcr_ipc/_linkedlist.h>
+
+struct _cr_owned_mutex_waiter {
+ _cr_ipc_sll_node;
+ cid_t cid;
+};
+
+/**
+ * A cr_owned_mutex_t is a fair mutex that tracks not just whether it
+ * is locked, but which coroutine has it locked.
+ *
+ * None of the methods have `_from_intrhandler` variants because (1)
+ * an interrupt handler can't block, so it shouldn't ever lock a mutex
+ * because that can block; and (2) if it can't lock a mutex in the
+ * first place, then it has no business unlocking one.
+ */
+typedef struct {
+ cid_t owner;
+ _cr_ipc_sll_root waiters;
+} cr_owned_mutex_t;
+
+/**
+ * Lock the mutex. Blocks if it is already locked.
+ *
+ * @runs_in coroutine
+ * @cr_pauses maybe
+ * @cr_yields maybe
+ */
+static inline void cr_owned_mutex_lock(cr_owned_mutex_t *mu) {
+ assert(mu);
+
+ if (!mu->owner) /* non-blocking fast-path */
+ mu->owner = cr_getcid();
+ else { /* blocking slow-path */
+ struct _cr_owned_mutex_waiter self = {
+ .cid = cr_getcid(),
+ };
+ _cr_ipc_sll_push_to_rear(&mu->waiters, &self);
+ cr_pause_and_yield();
+ }
+ assert(mu->owner == cr_getcid());
+}
+
+/**
+ * Unlock the mutex. Unblocks a coroutine that is blocked on
+ * cr_owned_mutex_lock(). Must be the called from the same coroutine
+ * that called cr_owned_mutex_lock().
+ *
+ * @runs_in coroutine
+ * @cr_pauses never
+ * @cr_yields never
+ */
+static inline void cr_owned_mutex_unlock(cr_owned_mutex_t *mu) {
+ assert(mu);
+
+ assert(mu->owner == cr_getcid());
+ if (mu->waiters.front) {
+ cid_t waiter = _cr_ipc_sll_node_cast(struct _cr_owned_mutex_waiter, mu->waiters.front)->cid;
+ cr_unpause(waiter);
+ mu->owner = waiter;
+ _cr_ipc_sll_pop_from_front(&mu->waiters);
+ } else
+ mu->owner = 0;
+}
+
+#endif /* _LIBCR_IPC_OWNED_MUTEX_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/rpc.h b/libcr_ipc/include/libcr_ipc/rpc.h
index 0d47514..0d6d25e 100644
--- a/libcr_ipc/include/libcr_ipc/rpc.h
+++ b/libcr_ipc/include/libcr_ipc/rpc.h
@@ -9,7 +9,7 @@
#include <stdbool.h> /* for bool */
-#include <libcr/coroutine.h> /* for cid_t, cr_unpause(), cr_pause_and_yield() */
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
#include <libcr_ipc/_linkedlist.h>
@@ -112,6 +112,7 @@
} NAME##_t; \
\
static inline RESP_T NAME##_send_req(NAME##_t *ch, REQ_T req) { \
+ cr_assert_in_coroutine(); \
RESP_T resp; \
struct _##NAME##_waiting_req self = { \
.req = &req, \
@@ -126,6 +127,7 @@
} \
\
static inline NAME##_req_t NAME##_recv_req(NAME##_t *ch) { \
+ cr_assert_in_coroutine(); \
if (!ch->waiting_reqs.front) { \
struct _##NAME##_waiting_resp self = { \
.cid = cr_getcid(), \
@@ -146,10 +148,12 @@
} \
\
static inline bool NAME##_can_recv_req(NAME##_t *ch) { \
+ cr_assert_in_coroutine(); \
return ch->waiting_reqs.front != NULL; \
} \
\
static inline void NAME##_send_resp(NAME##_req_t req, RESP_T resp) { \
+ cr_assert_in_coroutine(); \
*(req._resp) = resp; \
cr_unpause(req._requester); \
cr_yield(); \
diff --git a/libcr_ipc/include/libcr_ipc/select.h b/libcr_ipc/include/libcr_ipc/select.h
index 788bf53..ee49cca 100644
--- a/libcr_ipc/include/libcr_ipc/select.h
+++ b/libcr_ipc/include/libcr_ipc/select.h
@@ -109,6 +109,7 @@ static size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) {
assert(arg_cnt);
assert(arg_vec);
+ cr_assert_in_coroutine();
for (size_t i = 0; i < arg_cnt; i++) {
switch (_cr_select_getclass(arg_vec[i])) {
diff --git a/libcr_ipc/include/libcr_ipc/sema.h b/libcr_ipc/include/libcr_ipc/sema.h
index db840fe..1064182 100644
--- a/libcr_ipc/include/libcr_ipc/sema.h
+++ b/libcr_ipc/include/libcr_ipc/sema.h
@@ -7,7 +7,7 @@
#ifndef _LIBCR_IPC_SEMA_H_
#define _LIBCR_IPC_SEMA_H_
-#include <libcr/coroutine.h> /* for cid_t, cr_unpause(), cr_pause_and_yield() */
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
#include <libcr_ipc/_linkedlist.h>
@@ -35,6 +35,7 @@ typedef struct {
*/
static inline void cr_sema_signal(cr_sema_t *sema) {
assert(sema);
+ cr_assert_in_coroutine();
bool saved = cr_save_and_disable_interrupts();
sema->cnt++;
@@ -53,6 +54,7 @@ static inline void cr_sema_signal(cr_sema_t *sema) {
*/
static inline void cr_sema_signal_from_intrhandler(cr_sema_t *sema) {
assert(sema);
+ cr_assert_in_intrhandler();
sema->cnt++;
if (sema->waiters.front) {
@@ -71,6 +73,7 @@ static inline void cr_sema_signal_from_intrhandler(cr_sema_t *sema) {
*/
static inline void cr_sema_wait(cr_sema_t *sema) {
assert(sema);
+ cr_assert_in_coroutine();
bool saved = cr_save_and_disable_interrupts();
if (!sema->cnt) {
diff --git a/libhw/rp2040_include/libhw/w5500.h b/libhw/rp2040_include/libhw/w5500.h
index 80366a0..3cae620 100644
--- a/libhw/rp2040_include/libhw/w5500.h
+++ b/libhw/rp2040_include/libhw/w5500.h
@@ -41,7 +41,6 @@ struct _w5500_socket {
_w5500_sockintr_ch_t write_ch; /* MODE_{TCP,UDP} */
bool list_open, read_open, write_open; /* MODE_TCP */
- cr_mutex_t cmd_mu;
END_PRIVATE(LIBHW_W5500_H)
};
@@ -59,6 +58,7 @@ struct w5500 {
struct _w5500_socket sockets[8];
struct _w5500_socket *free;
cr_sema_t intr;
+ cr_mutex_t mu;
END_PRIVATE(LIBHW_W5500_H)
};
diff --git a/libhw/w5500.c b/libhw/w5500.c
index e675ae9..f36b9cf 100644
--- a/libhw/w5500.c
+++ b/libhw/w5500.c
@@ -225,49 +225,55 @@ static COROUTINE w5500_irq_cr(void *_chip) {
cr_begin();
for (;;) {
- while (!gpio_get(chip->pin_intr)) {
- debugf("w5500_irq_cr(): gpio low");
-
- uint8_t chipintr = w5500ll_read_common_reg(chip->spidev, chip_interrupt);
- if (chipintr)
- 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: case W5500_MODE_UDP:
- uint8_t listen_bits = sockintr & SOCKINTR_CONN,
- send_bits = sockintr & (SOCKINTR_SEND_OK|SOCKINTR_SEND_TIMEOUT),
- recv_bits = sockintr & (SOCKINTR_RECV_DAT|SOCKINTR_RECV_FIN);
-
- if (listen_bits) {
- debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->listen_sema", socknum);
- cr_sema_signal(&socket->listen_sema);
- }
- if (recv_bits) {
- debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->read_sema", socknum);
- cr_sema_signal(&socket->read_sema);
- }
- if (send_bits) {
- debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->write_ch", socknum);
- _w5500_sockintr_ch_send(&socket->write_ch, send_bits);
- }
- break;
- }
+ cr_mutex_lock(&chip->mu);
+ bool had_intr = false;
+
+ uint8_t chipintr = w5500ll_read_common_reg(chip->spidev, chip_interrupt);
+ n_debugf(W5500_LL, "w5500_irq_cr(): chipintr=%"PRIu8, chipintr);
+ had_intr = had_intr || (chipintr != 0);
+ if (chipintr)
+ w5500ll_write_common_reg(chip->spidev, chip_interrupt, 0xFF);
+
+ for (uint8_t socknum = 0; socknum < 8; socknum++) {
+ struct _w5500_socket *socket = &chip->sockets[socknum];
- w5500ll_write_sock_reg(chip->spidev, socknum, interrupt, sockintr);
+ uint8_t sockintr = w5500ll_read_sock_reg(chip->spidev, socknum, interrupt);
+ n_debugf(W5500_LL, "w5500_irq_cr(): sockintr[%"PRIu8"]=%"PRIu8, socknum, sockintr);
+ had_intr = had_intr || (sockintr != 0);
+
+ switch (socket->mode) {
+ case W5500_MODE_NONE:
+ break;
+ case W5500_MODE_TCP: case W5500_MODE_UDP:
+ uint8_t listen_bits = sockintr & SOCKINTR_CONN,
+ send_bits = sockintr & (SOCKINTR_SEND_OK|SOCKINTR_SEND_TIMEOUT),
+ recv_bits = sockintr & (SOCKINTR_RECV_DAT|SOCKINTR_RECV_FIN);
+
+ if (listen_bits) {
+ debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->listen_sema", socknum);
+ cr_sema_signal(&socket->listen_sema);
+ }
+ if (recv_bits) {
+ debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->read_sema", socknum);
+ cr_sema_signal(&socket->read_sema);
+ }
+ if (send_bits) {
+ debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->write_ch", socknum);
+ _w5500_sockintr_ch_send(&socket->write_ch, send_bits);
+ }
+ break;
}
+
+ w5500ll_write_sock_reg(chip->spidev, socknum, interrupt, sockintr);
}
- cr_sema_wait(&chip->intr);
- debugf("w5500_irq_cr(): sema signalled");
+
+ cr_mutex_unlock(&chip->mu);
+
+ if (!had_intr && gpio_get(chip->pin_intr)) {
+ debugf("w5500_irq_cr(): looks like all interrupts have been processed, sleeping...");
+ cr_sema_wait(&chip->intr);
+ } else
+ cr_yield();
}
cr_end();
@@ -290,11 +296,9 @@ static inline void w5500_socket_cmd(struct _w5500_socket *socket, uint8_t cmd) {
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) {
@@ -363,15 +367,6 @@ void _w5500_init(struct w5500 *chip,
};
}
- /* Validate that SPI works correctly. */
- for (uint16_t a = 0; a < 0x100; a++) {
- w5500ll_write_sock_reg(chip->spidev, 0, mode, a);
- uint8_t b = w5500ll_read_sock_reg(chip->spidev, 0, mode);
- if (b != a)
- errorf("SPI to W5500 does not appear to be functional: wrote:%d != read:%d", a, b);
- }
- w5500ll_write_sock_reg(chip->spidev, 0, mode, 0);
-
/* 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);
@@ -433,20 +428,28 @@ static void w5500_post_reset(struct w5500 *chip) {
}
void w5500_hard_reset(struct w5500 *chip) {
+ cr_mutex_lock(&chip->mu);
+
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);
+
+ cr_mutex_unlock(&chip->mu);
}
void w5500_soft_reset(struct w5500 *chip) {
+ cr_mutex_lock(&chip->mu);
+
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);
+
+ cr_mutex_unlock(&chip->mu);
}
static struct net_eth_addr w5500_if_hwaddr(implements_net_iface *_chip) {
@@ -460,9 +463,13 @@ static void _w5500_if_up(implements_net_iface *_chip, struct net_iface_config cf
struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip);
assert(chip);
+ cr_mutex_lock(&chip->mu);
+
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);
+
+ cr_mutex_unlock(&chip->mu);
}
static void w5500_if_up(implements_net_iface *_chip, struct net_iface_config cfg) {
@@ -526,6 +533,8 @@ static implements_net_stream_conn *w5500_if_tcp_dial(implements_net_iface *_chip
socket->read_open = socket->write_open = true;
restart:
+ cr_mutex_lock(&chip->mu);
+
/* Mimics socket.c:socket(). */
w5500_socket_close(socket);
w5500ll_write_sock_reg(chip->spidev, socknum, mode, SOCKMODE_TCP);
@@ -538,6 +547,7 @@ static implements_net_stream_conn *w5500_if_tcp_dial(implements_net_iface *_chip
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);
+ cr_mutex_unlock(&chip->mu);
for (;;) {
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
debugf("tcp_dial(): state=%s", w5500_state_str(state));
@@ -574,12 +584,14 @@ static implements_net_packet_conn *w5500_if_udp_conn(implements_net_iface *_chip
socket->read_deadline_ns = 0;
/* Mimics socket.c:socket(). */
+ cr_mutex_lock(&chip->mu);
w5500_socket_close(socket);
w5500ll_write_sock_reg(chip->spidev, socknum, mode, SOCKMODE_UDP);
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_UDP)
cr_yield();
+ cr_mutex_unlock(&chip->mu);
return &socket->implements_net_packet_conn;
}
@@ -595,6 +607,8 @@ static implements_net_stream_conn *w5500_tcplist_accept(implements_net_stream_li
return NULL;
}
+ cr_mutex_lock(&chip->mu);
+
/* Mimics socket.c:socket(). */
w5500_socket_close(socket);
w5500ll_write_sock_reg(chip->spidev, socknum, mode, SOCKMODE_TCP);
@@ -605,6 +619,7 @@ static implements_net_stream_conn *w5500_tcplist_accept(implements_net_stream_li
/* Mimics socket.c:listen(). */
w5500_socket_cmd(socket, CMD_LISTEN);
+ cr_mutex_unlock(&chip->mu);
for (;;) {
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
debugf("tcp_listener.accept() => state=%s", w5500_state_str(state));
@@ -668,8 +683,10 @@ static ssize_t w5500_tcp_write(implements_net_stream_conn *_socket, void *buf, s
debugf(" => soft closed");
return -NET_ECLOSED;
}
+ cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
if (state != STATE_TCP_ESTABLISHED && state != STATE_TCP_CLOSE_WAIT) {
+ cr_mutex_unlock(&chip->mu);
debugf(" => hard closed");
return -NET_ECLOSED;
}
@@ -677,6 +694,7 @@ static ssize_t w5500_tcp_write(implements_net_stream_conn *_socket, void *buf, s
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_mutex_unlock(&chip->mu);
cr_yield();
continue;
}
@@ -690,6 +708,8 @@ static ssize_t w5500_tcp_write(implements_net_stream_conn *_socket, void *buf, s
/* Submit the queue. */
w5500_socket_cmd(socket, CMD_SEND);
+
+ cr_mutex_unlock(&chip->mu);
switch (_w5500_sockintr_ch_recv(&socket->write_ch)) {
case SOCKINTR_SEND_OK:
debugf(" => sent %zu", freesize);
@@ -745,6 +765,7 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si
debugf(" => recv timeout");
return -NET_ERECV_TIMEOUT;
}
+ cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
switch (state) {
case STATE_TCP_CLOSE_WAIT:
@@ -753,6 +774,7 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si
break; /* OK */
default:
VCALL(bootclock, del_trigger, &trigger);
+ cr_mutex_unlock(&chip->mu);
debugf(" => hard closed");
return -NET_ECLOSED;
}
@@ -763,10 +785,12 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si
break;
if (state == STATE_TCP_CLOSE_WAIT) {
VCALL(bootclock, del_trigger, &trigger);
+ cr_mutex_unlock(&chip->mu);
debugf(" => EOF");
return 0;
}
+ cr_mutex_unlock(&chip->mu);
cr_sema_wait(&socket->read_sema);
}
assert(avail);
@@ -781,6 +805,7 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si
w5500_socket_cmd(socket, CMD_RECV);
/* Return. */
VCALL(bootclock, del_trigger, &trigger);
+ cr_mutex_unlock(&chip->mu);
return avail;
}
@@ -792,6 +817,7 @@ static int w5500_tcp_close(implements_net_stream_conn *_socket, bool rd, bool wr
socket->read_open = false;
if (wr && socket->write_open) {
+ cr_mutex_lock(&chip->mu);
w5500_socket_cmd(socket, CMD_DISCON);
while (socket->write_open) {
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
@@ -807,6 +833,7 @@ static int w5500_tcp_close(implements_net_stream_conn *_socket, bool rd, bool wr
break;
}
}
+ cr_mutex_unlock(&chip->mu);
}
w5500_tcp_maybe_free(chip, socket);
@@ -829,8 +856,10 @@ static ssize_t w5500_udp_sendto(implements_net_packet_conn *_socket, void *buf,
}
for (;;) {
+ cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
if (state != STATE_UDP) {
+ cr_mutex_unlock(&chip->mu);
debugf(" => closed");
return -NET_ECLOSED;
}
@@ -841,6 +870,7 @@ static ssize_t w5500_udp_sendto(implements_net_packet_conn *_socket, void *buf,
break;
/* Wait for more buffer space. */
+ cr_mutex_unlock(&chip->mu);
cr_yield();
}
@@ -854,6 +884,7 @@ static ssize_t w5500_udp_sendto(implements_net_packet_conn *_socket, void *buf,
/* Submit the queue. */
w5500_socket_cmd(socket, CMD_SEND);
+ cr_mutex_unlock(&chip->mu);
switch (_w5500_sockintr_ch_recv(&socket->write_ch)) {
case SOCKINTR_SEND_OK:
debugf(" => sent");
@@ -901,6 +932,7 @@ static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf
debugf(" => recv timeout");
return -NET_ERECV_TIMEOUT;
}
+ cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
if (state != STATE_UDP) {
VCALL(bootclock, del_trigger, &trigger);
@@ -913,6 +945,7 @@ static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf
/* We have data to read. */
break;
+ cr_mutex_unlock(&chip->mu);
cr_sema_wait(&socket->read_sema);
}
assert(avail >= 8);
@@ -941,6 +974,7 @@ static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf
w5500_socket_cmd(socket, CMD_RECV);
/* Return. */
VCALL(bootclock, del_trigger, &trigger);
+ cr_mutex_unlock(&chip->mu);
return len;
}
diff --git a/libhw/w5500_ll.h b/libhw/w5500_ll.h
index c70da0d..25aa6b5 100644
--- a/libhw/w5500_ll.h
+++ b/libhw/w5500_ll.h
@@ -20,18 +20,26 @@
#include <libhw/generic/net.h> /* for struct net_eth_addr, struct net_ip4_addr */
#include <libhw/generic/spi.h> /* for implements_spi */
+/* Config *********************************************************************/
+
+#include "config.h"
+
+#ifndef CONFIG_W5500_LL_DEBUG
+ #error config.h must define CONFIG_W5500_LL_DEBUG
+#endif
+
/* Low-level protocol built on SPI frames. ***********************************/
/* A u8 control byte has 3 parts: block-ID, R/W, and operating-mode. */
/* Part 1: Block ID. */
-#define CTL_MASK_BLOCK 0b11111000
-#define _CTL_BLOCK_RES 0b00000
-#define _CTL_BLOCK_REG 0b01000
-#define _CTL_BLOCK_TX 0b10000
-#define _CTL_BLOCK_RX 0b11000
+#define CTL_MASK_BLOCK 0b11111000
+#define _CTL_BLOCK_RES 0b00000 /* chip-wide registers on socknum=0, REServed on socknum>=1 */
+#define _CTL_BLOCK_REG 0b01000 /* socknum-specific registers */
+#define _CTL_BLOCK_TX 0b10000 /* socknum-specific transmit buffer */
+#define _CTL_BLOCK_RX 0b11000 /* socknum-specific receive buffer */
#define CTL_BLOCK_SOCK(n,part) (((n)<<5)|(_CTL_BLOCK_##part))
-#define CTL_BLOCK_COMMON_REG CTL_BLOCK_SOCK(0,RES)
+#define CTL_BLOCK_COMMON_REG CTL_BLOCK_SOCK(0,RES)
/* Part 2: R/W. */
#define CTL_MASK_RW 0b100
@@ -40,20 +48,42 @@
/* 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
+#define CTL_OM_VDM 0b00 /* variable-length data mode */
+#define CTL_OM_FDM1 0b01 /* fixed-length data mode: 1 byte data length */
+#define CTL_OM_FDM2 0b10 /* fixed-length data mode: 2 byte data length */
+#define CTL_OM_FDM4 0b11 /* fixed-length data mode: 4 byte data length */
+
+#if CONFIG_W5500_LL_DEBUG
+static char *_ctl_block_part_strs[] = {
+ "RES",
+ "REG",
+ "TX",
+ "RX",
+};
+#define PRI_ctl_block "CTL_BLOCK_SOCK(%d, %s)"
+#define ARG_ctl_block(b) (((b)>>5) & 0b111), _ctl_block_part_strs[((b)>>3)&0b11]
+#endif
/* Even though SPI is a full-duplex protocol, the W5500's spiframe on top of it is only half-duplex.
* Lame. */
static inline void
-w5500ll_write(implements_spi *spidev, uint16_t addr, uint8_t block, void *data, size_t data_len) {
+#if CONFIG_W5500_LL_DEBUG
+#define w5500ll_write(...) _w5500ll_write(__func__, __VA_ARGS__)
+_w5500ll_write(const char *func,
+#else
+w5500ll_write(
+#endif
+ 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);
+#if CONFIG_W5500_LL_DEBUG
+ n_debugf(W5500_LL,
+ "%s(): w5500ll_write(spidev, addr=%#04x, block="PRI_ctl_block", data, data_len=%zu)",
+ func, addr, ARG_ctl_block(block), data_len);
+#endif
uint8_t header[3] = {
(uint8_t)((addr >> 8) & 0xFF),
@@ -68,11 +98,22 @@ w5500ll_write(implements_spi *spidev, uint16_t addr, uint8_t block, void *data,
}
static inline void
-w5500ll_read(implements_spi *spidev, uint16_t addr, uint8_t block, void *data, size_t data_len) {
+#if CONFIG_W5500_LL_DEBUG
+#define w5500ll_read(...) _w5500ll_read(__func__, __VA_ARGS__)
+_w5500ll_read(const char *func,
+#else
+w5500ll_read(
+#endif
+ 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);
+#if CONFIG_W5500_LL_DEBUG
+ n_debugf(W5500_LL,
+ "%s(): w5500ll_read(spidev, addr=%#04x, block="PRI_ctl_block", data, data_len=%zu)",
+ func, addr, ARG_ctl_block(block), data_len);
+#endif
uint8_t header[3] = {
(uint8_t)((addr >> 8) & 0xFF),
@@ -222,7 +263,7 @@ static_assert(sizeof(struct w5500ll_block_sock_reg) == 0x30);
#define SOCKINTR_SEND_TIMEOUT ((uint8_t)1<<3) /* ARP or TCP */
#define SOCKINTR_RECV_DAT ((uint8_t)1<<2) /* received data */
#define SOCKINTR_RECV_FIN ((uint8_t)1<<1) /* received FIN */
-#define SOCKINTR_CONN ((uint8_t)1<<1) /* first for SYN, then when SOCKMODE_ESTABLISHED */
+#define SOCKINTR_CONN ((uint8_t)1<<0) /* first for SYN, then when SOCKMODE_ESTABLISHED */
#define STATE_CLOSED ((uint8_t)0x00)
diff --git a/libmisc/include/libmisc/log.h b/libmisc/include/libmisc/log.h
index c1e4a05..b4f5461 100644
--- a/libmisc/include/libmisc/log.h
+++ b/libmisc/include/libmisc/log.h
@@ -25,10 +25,14 @@
[[format(printf, 1, 2)]] int _log_printf(const char *format, ...);
-#define errorf(fmt, ...) do { _log_printf("error: " _LOG_STR(LOG_NAME) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
-#define infof(fmt, ...) do { _log_printf("info : " _LOG_STR(LOG_NAME) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
-#define debugf(fmt, ...) do { if (_LOG_CAT3(CONFIG_, LOG_NAME, _DEBUG) && !_LOG_NDEBUG) \
- _log_printf("debug: " _LOG_STR(LOG_NAME) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
+#define n_errorf(nam, fmt, ...) do { _log_printf("error: " _LOG_STR(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
+#define n_infof(nam, fmt, ...) do { _log_printf("info : " _LOG_STR(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
+#define n_debugf(nam, fmt, ...) do { if (_LOG_CAT3(CONFIG_, nam, _DEBUG) && !_LOG_NDEBUG) \
+ _log_printf("debug: " _LOG_STR(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
+
+#define errorf(fmt, ...) n_errorf(LOG_NAME, fmt, __VA_ARGS__)
+#define infof(fmt, ...) n_infof(LOG_NAME, fmt, __VA_ARGS__)
+#define debugf(fmt, ...) n_debugf(LOG_NAME, fmt, __VA_ARGS__)
const char *const_byte_str(uint8_t b);