From 8a131cb7e73df9bb15e0cbac25d310821553f4ec Mon Sep 17 00:00:00 2001 From: cuiziwei Date: Fri, 3 Feb 2023 14:51:56 +0800 Subject: [PATCH] testing/drivertest: add timer driver test Signed-off-by: cuiziwei --- testing/drivertest/Makefile | 6 + testing/drivertest/drivertest_timer.c | 308 ++++++++++++++++++++++++++ 2 files changed, 314 insertions(+) create mode 100644 testing/drivertest/drivertest_timer.c diff --git a/testing/drivertest/Makefile b/testing/drivertest/Makefile index 004cab21c..a1e735a6c 100644 --- a/testing/drivertest/Makefile +++ b/testing/drivertest/Makefile @@ -34,4 +34,10 @@ MAINSRC += drivertest_rtc.c PROGNAME += cmocka_driver_rtc endif +ifneq ($(CONFIG_TIMER),) +MAINSRC += drivertest_timer.c +PROGNAME += cmocka_driver_timer +endif + + include $(APPDIR)/Application.mk diff --git a/testing/drivertest/drivertest_timer.c b/testing/drivertest/drivertest_timer.c new file mode 100644 index 000000000..676964b1e --- /dev/null +++ b/testing/drivertest/drivertest_timer.c @@ -0,0 +1,308 @@ +/**************************************************************************** + * apps/testing/drivertest/drivertest_timer.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TIMER_DEFAULT_DEVPATH "/dev/timer0" +#define TIMER_DEFAULT_INTERVAL 1000000 +#define TIMER_DEFAULT_NSAMPLES 20 +#define TIMER_DEFAULT_SIGNO 17 +#define TIMER_DEFAULT_RANGE 1 + +#define OPTARG_TO_VALUE(value, type, base) \ + do \ + { \ + FAR char *ptr; \ + value = (type)strtoul(optarg, &ptr, base); \ + if (*ptr != '\0') \ + { \ + printf("Parameter error: -%c %s\n", ch, optarg); \ + show_usage(argv[0], timer_state, EXIT_FAILURE); \ + } \ + } while (0) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct timer_state_s +{ + char devpath[PATH_MAX]; + uint32_t interval; + uint32_t nsamples; + uint32_t signo; + uint32_t range; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: get_timestamp + ****************************************************************************/ + +static uint32_t get_timestamp(void) +{ + struct timespec ts; + uint32_t ms; + clock_gettime(CLOCK_MONOTONIC, &ts); + ms = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + return ms; +} + +/**************************************************************************** + * Name: show_usage + ****************************************************************************/ + +static void show_usage(FAR const char *progname, + FAR struct timer_state_s *timer_state, int exitcode) +{ + printf("Usage: %s" + " -d -i -n -r -s \n", + progname); + printf(" [-d devpath] selects the TIMER device.\n" + " Default: %s Current: %s\n", + TIMER_DEFAULT_DEVPATH, timer_state->devpath); + printf(" [-i interval] timer interval in microseconds.\n" + " Default: %d Current: %" PRIu32 "\n", + TIMER_DEFAULT_INTERVAL, timer_state->interval); + printf(" [-n nsamples] timer samples will be collected numbers.\n" + " Default: %d Current: %" PRIu32 "\n", + TIMER_DEFAULT_NSAMPLES, timer_state->nsamples); + printf(" [-r range] the max range of timer delay.\n" + " Default: %d Current: %" PRIu32 "\n", + TIMER_DEFAULT_RANGE, timer_state->range); + printf(" [-s signo] used to notify the test of timer expiration events.\n" + " Default: %d Current: %" PRIu32 "\n", + TIMER_DEFAULT_SIGNO, timer_state->signo); + printf(" [-h] = Shows this message and exits\n"); + + exit(exitcode); +} + +/**************************************************************************** + * Name: parse_commandline + ****************************************************************************/ + +static void parse_commandline(FAR struct timer_state_s *timer_state, + int argc, FAR char **argv) +{ + int ch; + int converted; + + while ((ch = getopt(argc, argv, "d:i:n:r:s:h")) != ERROR) + { + switch (ch) + { + case 'd': + strncpy(timer_state->devpath, optarg, + sizeof(timer_state->devpath)); + timer_state->devpath[sizeof(timer_state->devpath) - 1] = '\0'; + break; + + case 'i': + OPTARG_TO_VALUE(converted, uint32_t, 10); + if (converted < 1 || converted > INT_MAX) + { + printf("signal out of range: %d\n", converted); + show_usage(argv[0], timer_state, EXIT_FAILURE); + } + + timer_state->interval = (uint32_t)converted; + break; + + case 'n': + OPTARG_TO_VALUE(converted, uint32_t, 10); + if (converted < 1 || converted > INT_MAX) + { + printf("signal out of range: %d\n", converted); + show_usage(argv[0], timer_state, EXIT_FAILURE); + } + + timer_state->nsamples = (uint32_t)converted; + break; + + case 'r': + OPTARG_TO_VALUE(converted, uint32_t, 10); + if (converted < 1 || converted > INT_MAX) + { + printf("signal out of range: %d\n", converted); + show_usage(argv[0], timer_state, EXIT_FAILURE); + } + + timer_state->range = (uint32_t)converted; + break; + + case 's': + OPTARG_TO_VALUE(converted, uint32_t, 10); + if (converted < 1 || converted > INT_MAX) + { + printf("signal out of range: %d\n", converted); + show_usage(argv[0], timer_state, EXIT_FAILURE); + } + + timer_state->signo = (uint32_t)converted; + break; + + case '?': + printf("Unsupported option: %s\n", optarg); + show_usage(argv[0], timer_state, EXIT_FAILURE); + break; + } + } +} + +/**************************************************************************** + * Name: test_case_timer + ****************************************************************************/ + +static void test_case_timer(FAR void **state) +{ + int i; + int fd; + int ret; + uint32_t range; + uint32_t tim; + struct sigaction act; + struct timer_notify_s notify; + FAR struct timer_state_s *timer_state; + + timer_state = (FAR struct timer_state_s *)*state; + + /* Open the timer device */ + + fd = open(timer_state->devpath, O_RDONLY); + assert_true(fd > 0); + + /* Show the timer status before setting the timer interval */ + + ret = ioctl(fd, TCIOC_SETTIMEOUT, timer_state->interval); + assert_return_code(ret, OK); + + act.sa_sigaction = NULL; + act.sa_flags = SA_SIGINFO; + sigfillset(&act.sa_mask); + sigdelset(&act.sa_mask, timer_state->signo); + + ret = sigaction(timer_state->signo, &act, NULL); + assert_in_range(ret, 0, timer_state->signo); + + /* Register a callback for notifications using the configured signal. + * NOTE: If no callback is attached, the timer stop at the first interrupt. + */ + + notify.pid = getpid(); + notify.periodic = true; + notify.event.sigev_notify = SIGEV_SIGNAL; + notify.event.sigev_signo = timer_state->signo; + notify.event.sigev_value.sival_ptr = NULL; + + ret = ioctl(fd, TCIOC_NOTIFICATION, (unsigned long)((uintptr_t)¬ify)); + assert_return_code(ret, OK); + + /* Start the timer */ + + ret = ioctl(fd, TCIOC_START, 0); + assert_return_code(ret, OK); + + /* Set the timer interval */ + + for (i = 0; i < timer_state->nsamples; i++) + { + tim = get_timestamp(); + usleep(2 * timer_state->interval); + tim = get_timestamp() - tim; + range = timer_state->interval / 1000 - tim; + assert_in_range(range, 0, timer_state->range); + } + + /* Stop the timer */ + + ret = ioctl(fd, TCIOC_STOP, 0); + assert_return_code(ret, OK); + + /* Detach the signal handler */ + + act.sa_handler = SIG_DFL; + sigaction(timer_state->signo, &act, NULL); + + /* Close the timer driver */ + + close(fd); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: drivertest_timer_main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct timer_state_s timer_state = + { + .devpath = TIMER_DEFAULT_DEVPATH, + .interval = TIMER_DEFAULT_INTERVAL, + .nsamples = TIMER_DEFAULT_NSAMPLES, + .range = TIMER_DEFAULT_RANGE, + .signo = TIMER_DEFAULT_SIGNO + }; + + parse_commandline(&timer_state, argc, argv); + + const struct CMUnitTest tests[] = + { + cmocka_unit_test_prestate(test_case_timer, &timer_state) + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +}