diff --git a/drivers/dose/Makefile.dep b/drivers/dose/Makefile.dep index c483c0e946..753019f117 100644 --- a/drivers/dose/Makefile.dep +++ b/drivers/dose/Makefile.dep @@ -3,6 +3,10 @@ FEATURES_REQUIRED += periph_uart FEATURES_OPTIONAL += periph_uart_collision FEATURES_OPTIONAL += periph_uart_rxstart_irq +ifneq (,$(filter dose_watchdog,$(USEMODULE))) + FEATURES_REQUIRED += periph_timer_periodic +endif + USEMODULE += eui_provider USEMODULE += iolist USEMODULE += netdev_eth diff --git a/drivers/dose/Makefile.include b/drivers/dose/Makefile.include index 2c4778ddef..c25fbeaa9b 100644 --- a/drivers/dose/Makefile.include +++ b/drivers/dose/Makefile.include @@ -1,2 +1,4 @@ USEMODULE_INCLUDES_dose := $(LAST_MAKEFILEDIR)/include USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_dose) + +PSEUDOMODULES += dose_watchdog diff --git a/drivers/dose/dose.c b/drivers/dose/dose.c index d499b4ae63..0f08611e42 100644 --- a/drivers/dose/dose.c +++ b/drivers/dose/dose.c @@ -24,6 +24,7 @@ #include "dose.h" #include "random.h" #include "irq.h" +#include "periph/timer.h" #include "net/eui_provider.h" #include "net/netdev/eth.h" @@ -107,21 +108,77 @@ static inline void _disable_sense(dose_t *ctx) #endif } -static dose_signal_t state_transit_blocked(dose_t *ctx, dose_signal_t signal) -{ - uint32_t backoff; - (void) signal; +#ifdef MODULE_DOSE_WATCHDOG +static unsigned _watchdog_users; +static dose_t *_dose_base; +static uint8_t _dose_numof; - if (ctx->state == DOSE_STATE_RECV) { - /* We got here from RECV state. The driver's thread has to look - * if this frame should be processed. By queuing NETDEV_EVENT_ISR, - * the netif thread will call _isr at some time. */ - SETBIT(ctx->flags, DOSE_FLAG_RECV_BUF_DIRTY); - netdev_trigger_event_isr(&ctx->netdev); +static inline void _watchdog_start(void) +{ + if (_watchdog_users) { + return; } - /* Enable interrupt for start bit sensing */ - _enable_sense(ctx); + _watchdog_users++; + timer_start(DOSE_TIMER_DEV); +} + +static inline void _watchdog_stop(void) +{ + if (_watchdog_users == 0 || --_watchdog_users) { + return; + } + + timer_stop(DOSE_TIMER_DEV); +} + +static void _dose_watchdog_cb(void *arg, int chan) +{ + (void) chan; + (void) arg; + + for (unsigned i = 0; i < _dose_numof; ++i) { + dose_t *ctx = &_dose_base[i]; + + switch (ctx->state) { + case DOSE_STATE_RECV: + if (ctx->recv_buf_ptr_last != ctx->recv_buf_ptr) { + ctx->recv_buf_ptr_last = ctx->recv_buf_ptr; + break; + } + + if (ctx->flags & DOSE_FLAG_RECV_BUF_DIRTY) { + break; + } + + /* fall-through */ + case DOSE_STATE_BLOCKED: + state(&_dose_base[i], DOSE_SIGNAL_XTIMER); + break; + default: + break; + } + } +} + +static void _watchdog_init(unsigned timeout_us) +{ + timer_init(DOSE_TIMER_DEV, US_PER_SEC, _dose_watchdog_cb, NULL); + timer_set_periodic(DOSE_TIMER_DEV, 0, timeout_us, TIM_FLAG_RESET_ON_MATCH); + timer_stop(DOSE_TIMER_DEV); +} + +static inline void _set_random_backoff(dose_t *ctx) +{ + (void) ctx; +} + +#else +static inline void _watchdog_start(void) {} +static inline void _watchdog_stop(void) {} +static inline void _set_random_backoff(dose_t *ctx) +{ + uint32_t backoff; /* The timeout will bring us back into IDLE state by a random time. * If we entered this state from RECV state, the random time lays @@ -136,6 +193,27 @@ static dose_signal_t state_transit_blocked(dose_t *ctx, dose_signal_t signal) backoff = random_uint32_range(1 * ctx->timeout_base, 2 * ctx->timeout_base); } xtimer_set(&ctx->timeout, backoff); +} +#endif + +static dose_signal_t state_transit_blocked(dose_t *ctx, dose_signal_t signal) +{ + (void) signal; + + if (ctx->state == DOSE_STATE_RECV) { + /* We got here from RECV state. The driver's thread has to look + * if this frame should be processed. By queuing NETDEV_EVENT_ISR, + * the netif thread will call _isr at some time. */ + SETBIT(ctx->flags, DOSE_FLAG_RECV_BUF_DIRTY); + netdev_trigger_event_isr(&ctx->netdev); + } else if (ctx->state == DOSE_STATE_INIT) { + _watchdog_start(); + } + + /* Enable interrupt for start bit sensing */ + _enable_sense(ctx); + + _set_random_backoff(ctx); return DOSE_SIGNAL_NONE; } @@ -145,6 +223,8 @@ static dose_signal_t state_transit_idle(dose_t *ctx, dose_signal_t signal) (void) ctx; (void) signal; + _watchdog_stop(); + return DOSE_SIGNAL_NONE; } @@ -156,6 +236,7 @@ static dose_signal_t state_transit_recv(dose_t *ctx, dose_signal_t signal) /* We freshly entered this state. Thus, no start bit sensing is required * anymore. Disable RX Start IRQs during the transmission. */ _disable_sense(ctx); + _watchdog_start(); } if (signal == DOSE_SIGNAL_UART) { @@ -182,7 +263,7 @@ static dose_signal_t state_transit_recv(dose_t *ctx, dose_signal_t signal) } } - if (rc == DOSE_SIGNAL_NONE) { + if (rc == DOSE_SIGNAL_NONE && !IS_ACTIVE(MODULE_DOSE_WATCHDOG)) { /* No signal is returned. We stay in the RECV state. */ xtimer_set(&ctx->timeout, ctx->timeout_base); } @@ -197,6 +278,7 @@ static dose_signal_t state_transit_send(dose_t *ctx, dose_signal_t signal) if (ctx->state != DOSE_STATE_SEND) { /* Disable RX Start IRQs during the transmission. */ _disable_sense(ctx); + _watchdog_start(); } /* Don't trace any END octets ... the timeout or the END signal @@ -287,6 +369,9 @@ static void clear_recv_buf(dose_t *ctx) unsigned irq_state = irq_disable(); ctx->recv_buf_ptr = 0; +#ifdef MODULE_DOSE_WATCHDOG + ctx->recv_buf_ptr_last = -1; +#endif CLRBIT(ctx->flags, DOSE_FLAG_RECV_BUF_DIRTY); CLRBIT(ctx->flags, DOSE_FLAG_END_RECEIVED); CLRBIT(ctx->flags, DOSE_FLAG_ESC_RECEIVED); @@ -723,4 +808,14 @@ void dose_setup(dose_t *ctx, const dose_params_t *params, uint8_t index) DEBUG("dose timeout set to %" PRIu32 " µs\n", ctx->timeout_base); ctx->timeout.callback = _isr_xtimer; ctx->timeout.arg = ctx; + +#ifdef MODULE_DOSE_WATCHDOG + if (index >= _dose_numof) { + _dose_numof = index + 1; + } + if (index == 0) { + _dose_base = ctx; + _watchdog_init(ctx->timeout_base * 2); + } +#endif /* MODULE_DOSE_WATCHDOG */ } diff --git a/drivers/include/dose.h b/drivers/include/dose.h index 3ba955ccfe..04c7933712 100644 --- a/drivers/include/dose.h +++ b/drivers/include/dose.h @@ -154,6 +154,9 @@ typedef struct { mutex_t state_mtx; /**< Is unlocked every time a state is (re)entered */ uint8_t recv_buf[DOSE_FRAME_LEN]; /**< Receive buffer for incoming frames */ size_t recv_buf_ptr; /**< Index of the next empty octet of the recveive buffer */ +#if defined(MODULE_DOSE_WATCHDOG) || DOXYGEN + size_t recv_buf_ptr_last; /**< Last value of recv_buf_ptr when the watchdog visited */ +#endif #if !defined(MODULE_PERIPH_UART_RXSTART_IRQ) || DOXYGEN gpio_t sense_pin; /**< GPIO to sense for start bits on the UART's rx line */ #endif