Skip to content
Merged
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),)
Comment thread
xiaoxiang781216 marked this conversation as resolved.
MAINSRC += drivertest_watchdog.c
PROGNAME += cmocka_driver_watchdog
endif
Expand Down
256 changes: 250 additions & 6 deletions 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,18 +91,55 @@ struct wdg_state_s
uint32_t deviation;
int test_case;
bool test_getstatus;
#ifdef CONFIG_BOARDCTL_RESET_CAUSE
sem_t semaphore;
#endif
#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_WATCHDOG_TIMEOUT_NOTIFIER

struct watchdog_notifier_test_nb_s
{
struct notifier_block nb;
FAR struct wdg_state_s *state;
int id;
};

static sem_t g_semaphore;
struct watchdog_notifier_race_s
{
struct notifier_block nb;
volatile bool stop;
volatile unsigned int callbacks;
};

#endif

/****************************************************************************
* Private Data
****************************************************************************/

#ifdef CONFIG_BOARDCTL_RESET_CAUSE

/* WDIOC_CAPTURE does not accept a caller-provided callback argument. Keep
* the active test state while the capture handler is installed.
*/

static FAR struct wdg_state_s *g_capture_test_state;

#endif

/****************************************************************************
* Private Functions
****************************************************************************/

#ifdef CONFIG_BOARDCTL_RESET_CAUSE

/****************************************************************************
* Name: get_timestamp
****************************************************************************/
Expand Down Expand Up @@ -163,6 +203,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,16 +323,206 @@ static void parse_commandline(FAR struct wdg_state_s *wdg_state, int argc,
}
}

#ifdef CONFIG_BOARDCTL_RESET_CAUSE

/****************************************************************************
* Name: capture_callback
****************************************************************************/

static int capture_callback(int irq, FAR void *context, FAR void *arg)
{
sem_post(&g_semaphore);
DEBUGASSERT(g_capture_test_state != NULL);
sem_post(&g_capture_test_state->semaphore);
return OK;
}

#endif /* CONFIG_BOARDCTL_RESET_CAUSE */

#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER

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 = test_nb->state->notifier_calls;
if (index < 8)
{
test_nb->state->notifier_action[index] = action;
test_nb->state->notifier_data[index] = data;
test_nb->state->notifier_id[index] = test_nb->id;
}

test_nb->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
},
.state = wdg_state,
.id = 1
};

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

unsigned long expected_action;

expected_action = watchdog_notifier_test_expected_action();
wdg_state->notifier_calls = 0;

/* 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);
}

static int watchdog_notifier_race_callback(FAR struct notifier_block *nb,
unsigned long action,
FAR void *data)
{
FAR struct watchdog_notifier_race_s *race;

race = container_of(nb, struct watchdog_notifier_race_s, nb);
UNUSED(action);
UNUSED(data);
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);
watchdog_automonitor_timeout();
watchdog_notifier_chain_unregister(&race->nb);
}

return NULL;
}

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

pthread_t thread;
unsigned int count;
int ret;

UNUSED(state);
watchdog_notifier_chain_register(&race.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 continues to invoke the callback.
*/

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);
assert_true(race.callbacks > 0);
}

#endif /* CONFIG_WATCHDOG_TIMEOUT_NOTIFIER */

#ifdef CONFIG_BOARDCTL_RESET_CAUSE

/****************************************************************************
* Name: drivertest_watchdog_feeding
*
Expand Down Expand Up @@ -493,9 +725,10 @@ static void drivertest_watchdog_api(FAR void **state)

/* Test capture. */

ret = sem_init(&g_semaphore, 0, 0);
ret = sem_init(&wdg_state->semaphore, 0, 0);
assert_return_code(ret, OK);

g_capture_test_state = wdg_state;
watchdog_capture.newhandler = capture_callback;
ret = ioctl(dev_fd, WDIOC_CAPTURE, &watchdog_capture);
assert_return_code(ret, OK);
Expand All @@ -504,12 +737,14 @@ static void drivertest_watchdog_api(FAR void **state)

up_udelay(2 * wdg_state->timeout * 1000);

sem_wait(&g_semaphore);
sem_destroy(&g_semaphore);
sem_wait(&wdg_state->semaphore);

watchdog_capture.newhandler = watchdog_capture.oldhandler;
ret = ioctl(dev_fd, WDIOC_CAPTURE, &watchdog_capture);
assert_return_code(ret, OK);
g_capture_test_state = NULL;

sem_destroy(&wdg_state->semaphore);

/* Then stop pinging */

Expand All @@ -520,6 +755,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 +782,18 @@ 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
Comment thread
acassis marked this conversation as resolved.
#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