This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 2bf7d987edaa75b83aab87064bb671f0903e896d
Author: hanzhijian <[email protected]>
AuthorDate: Wed Jul 15 14:41:42 2026 +0800

    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 <[email protected]>
    Assisted-by: OpenAI Codex
---
 arch/arm/src/common/stm32/stm32_wwdg_m0_v1.c       |   2 +-
 arch/arm/src/common/stm32/stm32_wwdg_m3m4_v1.c     |   2 +-
 arch/arm/src/stm32h7/stm32_wwdg.c                  |   2 +-
 .../sim/sim/configs/watchdog-notifier/defconfig    |  77 +++++++++++++
 drivers/timers/watchdog.c                          | 124 ++++++++++++++++++++-
 include/nuttx/timers/watchdog.h                    |   9 +-
 6 files changed, 206 insertions(+), 10 deletions(-)

diff --git a/arch/arm/src/common/stm32/stm32_wwdg_m0_v1.c 
b/arch/arm/src/common/stm32/stm32_wwdg_m0_v1.c
index 9ec66a9fa92..afc791ec595 100644
--- a/arch/arm/src/common/stm32/stm32_wwdg_m0_v1.c
+++ b/arch/arm/src/common/stm32/stm32_wwdg_m0_v1.c
@@ -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
diff --git a/arch/arm/src/common/stm32/stm32_wwdg_m3m4_v1.c 
b/arch/arm/src/common/stm32/stm32_wwdg_m3m4_v1.c
index 8d8ccdac2b0..c18fa665f22 100644
--- a/arch/arm/src/common/stm32/stm32_wwdg_m3m4_v1.c
+++ b/arch/arm/src/common/stm32/stm32_wwdg_m3m4_v1.c
@@ -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
diff --git a/arch/arm/src/stm32h7/stm32_wwdg.c 
b/arch/arm/src/stm32h7/stm32_wwdg.c
index 7a4c29aeb9a..d0a35e6ca73 100644
--- a/arch/arm/src/stm32h7/stm32_wwdg.c
+++ b/arch/arm/src/stm32h7/stm32_wwdg.c
@@ -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
diff --git a/boards/sim/sim/sim/configs/watchdog-notifier/defconfig 
b/boards/sim/sim/sim/configs/watchdog-notifier/defconfig
new file mode 100644
index 00000000000..df6350b258c
--- /dev/null
+++ b/boards/sim/sim/sim/configs/watchdog-notifier/defconfig
@@ -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
diff --git a/drivers/timers/watchdog.c b/drivers/timers/watchdog.c
index 4ddeb6ac640..923672a107d 100644
--- a/drivers/timers/watchdog.c
+++ b/drivers/timers/watchdog.c
@@ -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 */
 
diff --git a/include/nuttx/timers/watchdog.h b/include/nuttx/timers/watchdog.h
index 6b1b2c15abe..8fe3cc9e3b8 100644
--- a/include/nuttx/timers/watchdog.h
+++ b/include/nuttx/timers/watchdog.h
@@ -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.
  *
  ****************************************************************************/
 

Reply via email to