drivers/watchdog: fix capture automonitor notifier context

Provide per-instance capture automonitor lookup for watchdog lower halves
that pass callback context, and avoid selecting an unrelated watchdog when
legacy lower halves provide no context. Update STM32 WWDG lower halves to pass
their instance context so multiple watchdog devices remain distinguishable.

Signed-off-by: hanzhijian <hanzhijian@zepp.com>
Assisted-by: OpenAI Codex
This commit is contained in:
hanzhijian 2026-07-15 14:41:42 +08:00 committed by Xiang Xiao
parent 20c280724a
commit 2bf7d987ed
6 changed files with 206 additions and 10 deletions

View file

@ -299,7 +299,7 @@ static int stm32_interrupt(int irq, void *context, void *arg)
* upon return.
*/
priv->handler(irq, context, arg);
priv->handler(irq, context, priv);
}
/* The EWI interrupt is cleared by writing '0' to the EWIF bit in the

View file

@ -299,7 +299,7 @@ static int stm32_interrupt(int irq, void *context, void *arg)
* upon return.
*/
priv->handler(irq, context, arg);
priv->handler(irq, context, priv);
}
/* The EWI interrupt is cleared by writing '0' to the EWIF bit in the

View file

@ -298,7 +298,7 @@ static int stm32_interrupt(int irq, void *context, void *arg)
* upon return.
*/
priv->handler(irq, context, arg);
priv->handler(irq, context, priv);
}
/* The EWI interrupt is cleared by writing '0' to the EWIF bit in the

View file

@ -0,0 +1,77 @@
#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_NSH_CMDOPT_HEXDUMP is not set
CONFIG_ALLOW_MIT_COMPONENTS=y
CONFIG_ARCH="sim"
CONFIG_ARCH_BOARD="sim"
CONFIG_ARCH_BOARD_SIM=y
CONFIG_ARCH_CHIP="sim"
CONFIG_ARCH_SIM=y
CONFIG_BOARDCTL_APP_SYMTAB=y
CONFIG_BOARDCTL_POWEROFF=y
CONFIG_BOARD_LOOPSPERMSEC=0
CONFIG_BOOT_RUNFROMEXTSRAM=y
CONFIG_BUILTIN=y
CONFIG_DEBUG_ASSERTIONS=y
CONFIG_DEBUG_ASSERTIONS_EXPRESSION=y
CONFIG_DEBUG_FEATURES=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_DEV_GPIO=y
CONFIG_DEV_LOOP=y
CONFIG_ETC_FATDEVNO=2
CONFIG_ETC_ROMFS=y
CONFIG_ETC_ROMFSDEVNO=1
CONFIG_FAT_LCNAMES=y
CONFIG_FAT_LFN=y
CONFIG_FS_BINFS=y
CONFIG_FS_FAT=y
CONFIG_FS_HOSTFS=y
CONFIG_FS_PROCFS=y
CONFIG_FS_RAMMAP=y
CONFIG_FS_ROMFS=y
CONFIG_GPIO_LOWER_HALF=y
CONFIG_IDLETHREAD_STACKSIZE=4096
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_IOEXPANDER=y
CONFIG_IOEXPANDER_DUMMY=y
CONFIG_LIBC_ENVPATH=y
CONFIG_LIBC_EXECFUNCS=y
CONFIG_LIBC_LOCALE=y
CONFIG_LIBC_LOCALE_CATALOG=y
CONFIG_LIBC_LOCALE_GETTEXT=y
CONFIG_LIBC_MAX_EXITFUNS=1
CONFIG_LIBC_NUMBERED_ARGS=y
CONFIG_NDEBUG=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FILE_APPS=y
CONFIG_NSH_READLINE=y
CONFIG_PATH_INITIAL="/bin"
CONFIG_PIPES=y
CONFIG_PSEUDOFS_ATTRIBUTES=y
CONFIG_PSEUDOFS_FILE=y
CONFIG_PSEUDOFS_SOFTLINKS=y
CONFIG_READLINE_TABCOMPLETION=y
CONFIG_RTC=y
CONFIG_RTC_ARCH=y
CONFIG_RTC_DRIVER=y
CONFIG_SCHED_BACKTRACE=y
CONFIG_SCHED_EVENTS=y
CONFIG_SCHED_HAVE_PARENT=y
CONFIG_SCHED_WAITPID=y
CONFIG_SIM_HOSTFS=y
CONFIG_SIM_WALLTIME_SIGNAL=y
CONFIG_START_MONTH=6
CONFIG_START_YEAR=2008
CONFIG_SYSTEM_DUMPSTACK=y
CONFIG_SYSTEM_NSH=y
CONFIG_TESTING_CMOCKA=y
CONFIG_TESTING_DRIVER_TEST=y
CONFIG_WATCHDOG=y
CONFIG_WATCHDOG_AUTOMONITOR=y
CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE=y
CONFIG_WATCHDOG_TIMEOUT_NOTIFIER=y

View file

@ -42,6 +42,7 @@
#include <nuttx/panic_notifier.h>
#include <nuttx/power/pm.h>
#include <nuttx/mutex.h>
#include <nuttx/spinlock.h>
#include <nuttx/wdog.h>
#include <nuttx/wqueue.h>
#include <nuttx/timers/oneshot.h>
@ -99,6 +100,9 @@ struct watchdog_upperhalf_s
struct notifier_block nb;
#endif
#ifdef CONFIG_WATCHDOG_AUTOMONITOR
# if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE)
FAR struct watchdog_upperhalf_s *capture_next;
# endif
# if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
FAR struct oneshot_lowerhalf_s *oneshot;
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER)
@ -153,24 +157,129 @@ static const struct file_operations g_wdogops =
static ATOMIC_NOTIFIER_HEAD(g_watchdog_notifier_list);
#endif
#ifdef CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE
/* Active capture automonitor instances. Lower halves that pass an argument
* can identify their upper-half directly or through the lower-half pointer.
* A NULL argument is accepted only when this list contains one instance.
*/
static FAR struct watchdog_upperhalf_s *g_watchdog_capture_list;
static spinlock_t g_watchdog_capture_lock = SP_UNLOCKED;
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
#if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE)
#ifdef CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE
static void watchdog_capture_add(FAR struct watchdog_upperhalf_s *upper)
{
irqstate_t flags = spin_lock_irqsave(&g_watchdog_capture_lock);
upper->capture_next = g_watchdog_capture_list;
g_watchdog_capture_list = upper;
spin_unlock_irqrestore(&g_watchdog_capture_lock, flags);
}
static void watchdog_capture_remove(FAR struct watchdog_upperhalf_s *upper)
{
FAR struct watchdog_upperhalf_s **cursor;
irqstate_t flags = spin_lock_irqsave(&g_watchdog_capture_lock);
cursor = &g_watchdog_capture_list;
while (*cursor != NULL)
{
if (*cursor == upper)
{
*cursor = upper->capture_next;
upper->capture_next = NULL;
break;
}
cursor = &(*cursor)->capture_next;
}
spin_unlock_irqrestore(&g_watchdog_capture_lock, flags);
}
static FAR struct watchdog_upperhalf_s *
watchdog_capture_find(FAR void *arg)
{
FAR struct watchdog_upperhalf_s *upper;
FAR struct watchdog_upperhalf_s *match = NULL;
irqstate_t flags = spin_lock_irqsave(&g_watchdog_capture_lock);
if (arg == NULL)
{
for (upper = g_watchdog_capture_list; upper != NULL;
upper = upper->capture_next)
{
if (match != NULL)
{
/* More than one NULL-context instance cannot be identified. */
match = NULL;
break;
}
match = upper;
}
}
else
{
for (upper = g_watchdog_capture_list; upper != NULL;
upper = upper->capture_next)
{
if (arg == upper || arg == upper->lower)
{
match = upper;
break;
}
}
}
spin_unlock_irqrestore(&g_watchdog_capture_lock, flags);
return match;
}
static int watchdog_automonitor_capture(int irq, FAR void *context,
FAR void *arg)
{
FAR struct watchdog_upperhalf_s *upper = arg;
FAR struct watchdog_lowerhalf_s *lower = upper->lower;
FAR struct watchdog_upperhalf_s *upper = watchdog_capture_find(arg);
FAR struct watchdog_lowerhalf_s *lower;
/* A stop operation can race with a pending interrupt. Do not dereference
* a removed association after automonitor has been stopped.
*/
if (upper == NULL)
{
return 0;
}
lower = upper->lower;
if (upper->monitor)
{
/* Reload the hardware watchdog before dispatching optional
* notifications. The EWI-to-reset interval is short, and notifier
* callbacks must not delay the keepalive operation.
*/
lower->ops->keepalive(lower);
#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
/* The capture callback is entered from the watchdog timeout
* interrupt. Notifier callbacks must be safe in interrupt context.
*/
watchdog_automonitor_timeout();
#endif
}
return 0;
}
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
static void
watchdog_automonitor_oneshot(FAR struct oneshot_lowerhalf_s *oneshot,
@ -267,6 +376,7 @@ watchdog_automonitor_start(FAR struct watchdog_upperhalf_s *upper)
if (!upper->monitor)
{
# if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE)
watchdog_capture_add(upper);
lower->ops->capture(lower, watchdog_automonitor_capture);
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
struct timespec ts =
@ -311,6 +421,7 @@ static void watchdog_automonitor_stop(FAR struct watchdog_upperhalf_s *upper)
lower->ops->stop(lower);
# if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE)
lower->ops->capture(lower, NULL);
watchdog_capture_remove(upper);
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
ONESHOT_CANCEL(upper->oneshot, NULL);
# elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER)
@ -762,7 +873,12 @@ void watchdog_notifier_chain_unregister(FAR struct notifier_block *nb)
void watchdog_automonitor_timeout(void)
{
atomic_notifier_call_chain(&g_watchdog_notifier_list, action, data);
/* The action identifies the automonitor keepalive mechanism selected at
* build time. There is no source-specific payload for this event.
*/
atomic_notifier_call_chain(&g_watchdog_notifier_list,
WATCHDOG_NOTIFIER_ACTION, NULL);
}
#endif /* CONFIG_WATCHDOG_TIMEOUT_NOTIFIER */

View file

@ -258,9 +258,12 @@ void watchdog_notifier_chain_unregister(FAR struct notifier_block *nb);
* Name: watchdog_automonitor_timeout
*
* Description:
* This function can be called in the watchdog timeout interrupt handler.
* If so, callbacks on the watchdog timer notify chain are called when the
* watchdog timer times out.
* Notify callbacks registered on the watchdog timeout notifier chain.
* The action identifies the automonitor keepalive mechanism selected by
* CONFIG_WATCHDOG_AUTOMONITOR_BY_*. The callback data argument is NULL.
*
* This function is intended to be called from a watchdog timeout interrupt
* handler. Callback functions must obey interrupt-context restrictions.
*
****************************************************************************/