Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion testing/drivers/drivertest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ if(CONFIG_TESTING_DRIVER_TEST)
drivertest_posix_timer.c)
endif()

if(CONFIG_WATCHDOG AND CONFIG_BOARDCTL_RESET_CAUSE)
if(CONFIG_WATCHDOG AND (CONFIG_BOARDCTL_RESET_CAUSE
OR CONFIG_WATCHDOG_TIMEOUT_NOTIFIER))
nuttx_add_application(
NAME
cmocka_driver_watchdog
Expand Down
2 changes: 1 addition & 1 deletion testing/drivers/drivertest/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ endif
endif

ifneq ($(CONFIG_WATCHDOG),)
ifneq ($(CONFIG_BOARDCTL_RESET_CAUSE),)
ifneq ($(CONFIG_BOARDCTL_RESET_CAUSE)$(CONFIG_WATCHDOG_TIMEOUT_NOTIFIER),)
MAINSRC += drivertest_watchdog.c
PROGNAME += cmocka_driver_watchdog
endif
Expand Down
235 changes: 234 additions & 1 deletion testing/drivers/drivertest/drivertest_watchdog.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,11 @@
#include <stdint.h>
#include <cmocka.h>
#include <time.h>
#include <pthread.h>

#include <nuttx/arch.h>
#include <nuttx/notifier.h>
#include <nuttx/nuttx.h>
#include <nuttx/timers/watchdog.h>

/****************************************************************************
Expand Down Expand Up @@ -88,9 +91,17 @@ struct wdg_state_s
uint32_t deviation;
int test_case;
bool test_getstatus;
#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
unsigned int notifier_calls;
unsigned long notifier_action[8];
FAR void *notifier_data[8];
int notifier_id[8];
#endif
};

#ifdef CONFIG_BOARDCTL_RESET_CAUSE
static sem_t g_semaphore;
#endif

/****************************************************************************
* Private Data
Expand All @@ -100,6 +111,8 @@ static sem_t g_semaphore;
* Private Functions
****************************************************************************/

#ifdef CONFIG_BOARDCTL_RESET_CAUSE

/****************************************************************************
* Name: get_timestamp
****************************************************************************/
Expand Down Expand Up @@ -163,6 +176,8 @@ static int wdg_init(FAR struct wdg_state_s *state)
return dev_fd;
}

#endif /* CONFIG_BOARDCTL_RESET_CAUSE */

/****************************************************************************
* Name: show_usage
****************************************************************************/
Expand Down Expand Up @@ -281,6 +296,8 @@ static void parse_commandline(FAR struct wdg_state_s *wdg_state, int argc,
}
}

#ifdef CONFIG_BOARDCTL_RESET_CAUSE

/****************************************************************************
* Name: capture_callback
****************************************************************************/
Expand All @@ -291,6 +308,214 @@ static int capture_callback(int irq, FAR void *context, FAR void *arg)
return OK;
}

#endif /* CONFIG_BOARDCTL_RESET_CAUSE */

#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER

struct watchdog_notifier_test_nb_s
Comment thread
xiaoxiang781216 marked this conversation as resolved.
{
struct notifier_block nb;
int id;
};

static FAR struct wdg_state_s *g_notifier_test_state;

static int watchdog_notifier_test_callback(FAR struct notifier_block *nb,
unsigned long action,
FAR void *data)
{
FAR struct watchdog_notifier_test_nb_s *test_nb;
unsigned int index;

test_nb = container_of(nb, struct watchdog_notifier_test_nb_s, nb);
index = g_notifier_test_state->notifier_calls;
if (index < 8)
{
g_notifier_test_state->notifier_action[index] = action;
g_notifier_test_state->notifier_data[index] = data;
g_notifier_test_state->notifier_id[index] = test_nb->id;
}

g_notifier_test_state->notifier_calls++;
return OK;
}

static unsigned long watchdog_notifier_test_expected_action(void)
{
#if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
return WATCHDOG_KEEPALIVE_BY_ONESHOT;
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER)
return WATCHDOG_KEEPALIVE_BY_TIMER;
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WDOG)
return WATCHDOG_KEEPALIVE_BY_WDOG;
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WORKER)
return WATCHDOG_KEEPALIVE_BY_WORKER;
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE)
return WATCHDOG_KEEPALIVE_BY_CAPTURE;
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_IDLE)
return WATCHDOG_KEEPALIVE_BY_IDLE;
#else
# error "An automonitor source must be selected"
#endif
}

static void drivertest_watchdog_notifier(FAR void **state)
{
FAR struct wdg_state_s *wdg_state = *state;

struct watchdog_notifier_test_nb_s low =
{
.nb =
{
.notifier_call = watchdog_notifier_test_callback,
.priority = 10
},
.id = 1
};

struct watchdog_notifier_test_nb_s high =
{
.nb =
{
.notifier_call = watchdog_notifier_test_callback,
.priority = 20
},
.id = 2
};

unsigned long expected_action;

UNUSED(state);
expected_action = watchdog_notifier_test_expected_action();
wdg_state->notifier_calls = 0;
g_notifier_test_state = wdg_state;

/* Registration is priority ordered, and duplicate registration of the
* same notifier must not result in a duplicate callback.
*/

watchdog_notifier_chain_register(&low.nb);
watchdog_notifier_chain_register(&low.nb);
watchdog_notifier_chain_register(&high.nb);

watchdog_automonitor_timeout();

assert_int_equal(wdg_state->notifier_calls, 2);
assert_int_equal(wdg_state->notifier_id[0], high.id);
assert_int_equal(wdg_state->notifier_id[1], low.id);
assert_int_equal(wdg_state->notifier_action[0], expected_action);
assert_int_equal(wdg_state->notifier_action[1], expected_action);
assert_null(wdg_state->notifier_data[0]);
assert_null(wdg_state->notifier_data[1]);

/* Every timeout notification is delivered to all currently registered
* callbacks.
*/

watchdog_automonitor_timeout();
assert_int_equal(wdg_state->notifier_calls, 4);
assert_int_equal(wdg_state->notifier_id[2], high.id);
assert_int_equal(wdg_state->notifier_id[3], low.id);

/* Unregistering one callback removes only that callback. */

watchdog_notifier_chain_unregister(&high.nb);
watchdog_automonitor_timeout();
assert_int_equal(wdg_state->notifier_calls, 5);
assert_int_equal(wdg_state->notifier_id[4], low.id);

watchdog_notifier_chain_unregister(&low.nb);
watchdog_automonitor_timeout();
assert_int_equal(wdg_state->notifier_calls, 5);

g_notifier_test_state = NULL;
}

struct watchdog_notifier_race_s
{
struct watchdog_notifier_test_nb_s nb;
volatile bool stop;
};

static volatile unsigned int g_notifier_race_callbacks;

static int watchdog_notifier_race_callback(FAR struct notifier_block *nb,
unsigned long action,
FAR void *data)
{
UNUSED(nb);
UNUSED(action);
UNUSED(data);
g_notifier_race_callbacks++;
return OK;
}

static FAR void *watchdog_notifier_race_worker(FAR void *arg)
{
FAR struct watchdog_notifier_race_s *race = arg;
unsigned int count;

for (count = 0; count < 2000 && !race->stop; count++)
{
watchdog_notifier_chain_register(&race->nb.nb);
watchdog_automonitor_timeout();
watchdog_notifier_chain_unregister(&race->nb.nb);
}

return NULL;
}

static void drivertest_watchdog_notifier_race(FAR void **state)
{
struct watchdog_notifier_race_s race =
{
.nb =
{
.nb =
{
.notifier_call = watchdog_notifier_race_callback,
.priority = 10
},
.id = 3
}
};

pthread_t thread;
unsigned int count;
int ret;

UNUSED(state);
g_notifier_race_callbacks = 0;
watchdog_notifier_chain_register(&race.nb.nb);
ret = pthread_create(&thread, NULL, watchdog_notifier_race_worker, &race);
assert_int_equal(ret, 0);
usleep(1000);

/* Interleave timeout delivery with registration and unregistration from
* another task. The test is successful if the notifier chain remains
* usable and no callback observes a non-NULL payload.
*/

for (count = 0; count < 2000; count++)
{
watchdog_automonitor_timeout();
if ((count & 0x3f) == 0)
{
usleep(1000);
}
}

race.stop = true;
ret = pthread_join(thread, NULL);
assert_int_equal(ret, 0);
watchdog_notifier_chain_unregister(&race.nb.nb);
assert_true(g_notifier_race_callbacks > 0);
}

#endif /* CONFIG_WATCHDOG_TIMEOUT_NOTIFIER */

#ifdef CONFIG_BOARDCTL_RESET_CAUSE

/****************************************************************************
* Name: drivertest_watchdog_feeding
*
Expand Down Expand Up @@ -520,6 +745,8 @@ static void drivertest_watchdog_api(FAR void **state)
assert_return_code(ret, OK);
}

#endif /* CONFIG_BOARDCTL_RESET_CAUSE */

/****************************************************************************
* Public Functions
****************************************************************************/
Expand All @@ -545,11 +772,17 @@ int main(int argc, FAR char *argv[])

const struct CMUnitTest tests[] =
{
#ifdef CONFIG_BOARDCTL_RESET_CAUSE
cmocka_unit_test_prestate(drivertest_watchdog_feeding, &wdg_state),
cmocka_unit_test_prestate(drivertest_watchdog_interrupts, &wdg_state),
cmocka_unit_test_prestate(drivertest_watchdog_loop, &wdg_state),
#if !defined(CONFIG_ARCH_ARMV7A) || !defined(CONFIG_ARCH_HAVE_TRUSTZONE)
cmocka_unit_test_prestate(drivertest_watchdog_api, &wdg_state)
cmocka_unit_test_prestate(drivertest_watchdog_api, &wdg_state),
#endif

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#endif
#endif

#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
    cmocka_unit_test_prestate(drivertest_watchdog_notifier, &wdg_state),

#endif
#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
cmocka_unit_test_prestate(drivertest_watchdog_notifier, &wdg_state),
cmocka_unit_test_prestate(drivertest_watchdog_notifier_race, &wdg_state)
#endif
};

Expand Down
Loading