diff options
-rw-r--r-- | cmd/sbc_harness/config/config.h | 2 | ||||
-rw-r--r-- | gdb-helpers/libcr.py | 11 | ||||
-rw-r--r-- | libcr/coroutine.c | 49 | ||||
-rw-r--r-- | libcr/include/libcr/coroutine.h | 21 | ||||
-rw-r--r-- | libcr_ipc/CMakeLists.txt | 1 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/chan.h | 6 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/mutex.h | 4 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/owned_mutex.h | 79 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/rpc.h | 6 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/select.h | 1 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/sema.h | 5 | ||||
-rw-r--r-- | libhw/rp2040_include/libhw/w5500.h | 2 | ||||
-rw-r--r-- | libhw/w5500.c | 136 | ||||
-rw-r--r-- | libhw/w5500_ll.h | 67 | ||||
-rw-r--r-- | libmisc/include/libmisc/log.h | 12 |
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); |