4343#include <stdint.h>
4444#include <cmocka.h>
4545#include <time.h>
46+ #include <pthread.h>
4647
4748#include <nuttx/arch.h>
49+ #include <nuttx/notifier.h>
50+ #include <nuttx/nuttx.h>
4851#include <nuttx/timers/watchdog.h>
4952
5053/****************************************************************************
@@ -88,18 +91,55 @@ struct wdg_state_s
8891 uint32_t deviation ;
8992 int test_case ;
9093 bool test_getstatus ;
94+ #ifdef CONFIG_BOARDCTL_RESET_CAUSE
95+ sem_t semaphore ;
96+ #endif
97+ #ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
98+ unsigned int notifier_calls ;
99+ unsigned long notifier_action [8 ];
100+ FAR void * notifier_data [8 ];
101+ int notifier_id [8 ];
102+ #endif
103+ };
104+
105+ #ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
106+
107+ struct watchdog_notifier_test_nb_s
108+ {
109+ struct notifier_block nb ;
110+ FAR struct wdg_state_s * state ;
111+ int id ;
91112};
92113
93- static sem_t g_semaphore ;
114+ struct watchdog_notifier_race_s
115+ {
116+ struct notifier_block nb ;
117+ volatile bool stop ;
118+ volatile unsigned int callbacks ;
119+ };
120+
121+ #endif
94122
95123/****************************************************************************
96124 * Private Data
97125 ****************************************************************************/
98126
127+ #ifdef CONFIG_BOARDCTL_RESET_CAUSE
128+
129+ /* WDIOC_CAPTURE does not accept a caller-provided callback argument. Keep
130+ * the active test state while the capture handler is installed.
131+ */
132+
133+ static FAR struct wdg_state_s * g_capture_test_state ;
134+
135+ #endif
136+
99137/****************************************************************************
100138 * Private Functions
101139 ****************************************************************************/
102140
141+ #ifdef CONFIG_BOARDCTL_RESET_CAUSE
142+
103143/****************************************************************************
104144 * Name: get_timestamp
105145 ****************************************************************************/
@@ -163,6 +203,8 @@ static int wdg_init(FAR struct wdg_state_s *state)
163203 return dev_fd ;
164204}
165205
206+ #endif /* CONFIG_BOARDCTL_RESET_CAUSE */
207+
166208/****************************************************************************
167209 * Name: show_usage
168210 ****************************************************************************/
@@ -281,16 +323,206 @@ static void parse_commandline(FAR struct wdg_state_s *wdg_state, int argc,
281323 }
282324}
283325
326+ #ifdef CONFIG_BOARDCTL_RESET_CAUSE
327+
284328/****************************************************************************
285329 * Name: capture_callback
286330 ****************************************************************************/
287331
288332static int capture_callback (int irq , FAR void * context , FAR void * arg )
289333{
290- sem_post (& g_semaphore );
334+ DEBUGASSERT (g_capture_test_state != NULL );
335+ sem_post (& g_capture_test_state -> semaphore );
336+ return OK ;
337+ }
338+
339+ #endif /* CONFIG_BOARDCTL_RESET_CAUSE */
340+
341+ #ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
342+
343+ static int watchdog_notifier_test_callback (FAR struct notifier_block * nb ,
344+ unsigned long action ,
345+ FAR void * data )
346+ {
347+ FAR struct watchdog_notifier_test_nb_s * test_nb ;
348+ unsigned int index ;
349+
350+ test_nb = container_of (nb , struct watchdog_notifier_test_nb_s , nb );
351+ index = test_nb -> state -> notifier_calls ;
352+ if (index < 8 )
353+ {
354+ test_nb -> state -> notifier_action [index ] = action ;
355+ test_nb -> state -> notifier_data [index ] = data ;
356+ test_nb -> state -> notifier_id [index ] = test_nb -> id ;
357+ }
358+
359+ test_nb -> state -> notifier_calls ++ ;
291360 return OK ;
292361}
293362
363+ static unsigned long watchdog_notifier_test_expected_action (void )
364+ {
365+ #if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT )
366+ return WATCHDOG_KEEPALIVE_BY_ONESHOT ;
367+ #elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER )
368+ return WATCHDOG_KEEPALIVE_BY_TIMER ;
369+ #elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WDOG )
370+ return WATCHDOG_KEEPALIVE_BY_WDOG ;
371+ #elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WORKER )
372+ return WATCHDOG_KEEPALIVE_BY_WORKER ;
373+ #elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE )
374+ return WATCHDOG_KEEPALIVE_BY_CAPTURE ;
375+ #elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_IDLE )
376+ return WATCHDOG_KEEPALIVE_BY_IDLE ;
377+ #else
378+ # error "An automonitor source must be selected"
379+ #endif
380+ }
381+
382+ static void drivertest_watchdog_notifier (FAR void * * state )
383+ {
384+ FAR struct wdg_state_s * wdg_state = * state ;
385+
386+ struct watchdog_notifier_test_nb_s low =
387+ {
388+ .nb =
389+ {
390+ .notifier_call = watchdog_notifier_test_callback ,
391+ .priority = 10
392+ },
393+ .state = wdg_state ,
394+ .id = 1
395+ };
396+
397+ struct watchdog_notifier_test_nb_s high =
398+ {
399+ .nb =
400+ {
401+ .notifier_call = watchdog_notifier_test_callback ,
402+ .priority = 20
403+ },
404+ .state = wdg_state ,
405+ .id = 2
406+ };
407+
408+ unsigned long expected_action ;
409+
410+ expected_action = watchdog_notifier_test_expected_action ();
411+ wdg_state -> notifier_calls = 0 ;
412+
413+ /* Registration is priority ordered, and duplicate registration of the
414+ * same notifier must not result in a duplicate callback.
415+ */
416+
417+ watchdog_notifier_chain_register (& low .nb );
418+ watchdog_notifier_chain_register (& low .nb );
419+ watchdog_notifier_chain_register (& high .nb );
420+
421+ watchdog_automonitor_timeout ();
422+
423+ assert_int_equal (wdg_state -> notifier_calls , 2 );
424+ assert_int_equal (wdg_state -> notifier_id [0 ], high .id );
425+ assert_int_equal (wdg_state -> notifier_id [1 ], low .id );
426+ assert_int_equal (wdg_state -> notifier_action [0 ], expected_action );
427+ assert_int_equal (wdg_state -> notifier_action [1 ], expected_action );
428+ assert_null (wdg_state -> notifier_data [0 ]);
429+ assert_null (wdg_state -> notifier_data [1 ]);
430+
431+ /* Every timeout notification is delivered to all currently registered
432+ * callbacks.
433+ */
434+
435+ watchdog_automonitor_timeout ();
436+ assert_int_equal (wdg_state -> notifier_calls , 4 );
437+ assert_int_equal (wdg_state -> notifier_id [2 ], high .id );
438+ assert_int_equal (wdg_state -> notifier_id [3 ], low .id );
439+
440+ /* Unregistering one callback removes only that callback. */
441+
442+ watchdog_notifier_chain_unregister (& high .nb );
443+ watchdog_automonitor_timeout ();
444+ assert_int_equal (wdg_state -> notifier_calls , 5 );
445+ assert_int_equal (wdg_state -> notifier_id [4 ], low .id );
446+
447+ watchdog_notifier_chain_unregister (& low .nb );
448+ watchdog_automonitor_timeout ();
449+ assert_int_equal (wdg_state -> notifier_calls , 5 );
450+ }
451+
452+ static int watchdog_notifier_race_callback (FAR struct notifier_block * nb ,
453+ unsigned long action ,
454+ FAR void * data )
455+ {
456+ FAR struct watchdog_notifier_race_s * race ;
457+
458+ race = container_of (nb , struct watchdog_notifier_race_s , nb );
459+ UNUSED (action );
460+ UNUSED (data );
461+ race -> callbacks ++ ;
462+ return OK ;
463+ }
464+
465+ static FAR void * watchdog_notifier_race_worker (FAR void * arg )
466+ {
467+ FAR struct watchdog_notifier_race_s * race = arg ;
468+ unsigned int count ;
469+
470+ for (count = 0 ; count < 2000 && !race -> stop ; count ++ )
471+ {
472+ watchdog_notifier_chain_register (& race -> nb );
473+ watchdog_automonitor_timeout ();
474+ watchdog_notifier_chain_unregister (& race -> nb );
475+ }
476+
477+ return NULL ;
478+ }
479+
480+ static void drivertest_watchdog_notifier_race (FAR void * * state )
481+ {
482+ struct watchdog_notifier_race_s race =
483+ {
484+ .nb =
485+ {
486+ .notifier_call = watchdog_notifier_race_callback ,
487+ .priority = 10
488+ }
489+ };
490+
491+ pthread_t thread ;
492+ unsigned int count ;
493+ int ret ;
494+
495+ UNUSED (state );
496+ watchdog_notifier_chain_register (& race .nb );
497+ ret = pthread_create (& thread , NULL , watchdog_notifier_race_worker , & race );
498+ assert_int_equal (ret , 0 );
499+ usleep (1000 );
500+
501+ /* Interleave timeout delivery with registration and unregistration from
502+ * another task. The test is successful if the notifier chain remains
503+ * usable and continues to invoke the callback.
504+ */
505+
506+ for (count = 0 ; count < 2000 ; count ++ )
507+ {
508+ watchdog_automonitor_timeout ();
509+ if ((count & 0x3f ) == 0 )
510+ {
511+ usleep (1000 );
512+ }
513+ }
514+
515+ race .stop = true;
516+ ret = pthread_join (thread , NULL );
517+ assert_int_equal (ret , 0 );
518+ watchdog_notifier_chain_unregister (& race .nb );
519+ assert_true (race .callbacks > 0 );
520+ }
521+
522+ #endif /* CONFIG_WATCHDOG_TIMEOUT_NOTIFIER */
523+
524+ #ifdef CONFIG_BOARDCTL_RESET_CAUSE
525+
294526/****************************************************************************
295527 * Name: drivertest_watchdog_feeding
296528 *
@@ -493,9 +725,10 @@ static void drivertest_watchdog_api(FAR void **state)
493725
494726 /* Test capture. */
495727
496- ret = sem_init (& g_semaphore , 0 , 0 );
728+ ret = sem_init (& wdg_state -> semaphore , 0 , 0 );
497729 assert_return_code (ret , OK );
498730
731+ g_capture_test_state = wdg_state ;
499732 watchdog_capture .newhandler = capture_callback ;
500733 ret = ioctl (dev_fd , WDIOC_CAPTURE , & watchdog_capture );
501734 assert_return_code (ret , OK );
@@ -504,12 +737,14 @@ static void drivertest_watchdog_api(FAR void **state)
504737
505738 up_udelay (2 * wdg_state -> timeout * 1000 );
506739
507- sem_wait (& g_semaphore );
508- sem_destroy (& g_semaphore );
740+ sem_wait (& wdg_state -> semaphore );
509741
510742 watchdog_capture .newhandler = watchdog_capture .oldhandler ;
511743 ret = ioctl (dev_fd , WDIOC_CAPTURE , & watchdog_capture );
512744 assert_return_code (ret , OK );
745+ g_capture_test_state = NULL ;
746+
747+ sem_destroy (& wdg_state -> semaphore );
513748
514749 /* Then stop pinging */
515750
@@ -520,6 +755,8 @@ static void drivertest_watchdog_api(FAR void **state)
520755 assert_return_code (ret , OK );
521756}
522757
758+ #endif /* CONFIG_BOARDCTL_RESET_CAUSE */
759+
523760/****************************************************************************
524761 * Public Functions
525762 ****************************************************************************/
@@ -545,11 +782,18 @@ int main(int argc, FAR char *argv[])
545782
546783 const struct CMUnitTest tests [] =
547784 {
785+ #ifdef CONFIG_BOARDCTL_RESET_CAUSE
548786 cmocka_unit_test_prestate (drivertest_watchdog_feeding , & wdg_state ),
549787 cmocka_unit_test_prestate (drivertest_watchdog_interrupts , & wdg_state ),
550788 cmocka_unit_test_prestate (drivertest_watchdog_loop , & wdg_state ),
551789#if !defined (CONFIG_ARCH_ARMV7A ) || !defined (CONFIG_ARCH_HAVE_TRUSTZONE )
552- cmocka_unit_test_prestate (drivertest_watchdog_api , & wdg_state )
790+ cmocka_unit_test_prestate (drivertest_watchdog_api , & wdg_state ),
791+ #endif
792+ #endif
793+
794+ #ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
795+ cmocka_unit_test_prestate (drivertest_watchdog_notifier , & wdg_state ),
796+ cmocka_unit_test_prestate (drivertest_watchdog_notifier_race , & wdg_state )
553797#endif
554798 };
555799
0 commit comments