On 03.08.20 08:04, chensong wrote:
> This a tool to benchmark the latency of GPIO driver,
> it's able to run 2 kinds of benchmark test:
> 
> 1, loopback mode
> 1) apply 2 gpio pins by calling service in gpio RTDM driver
>    like gpio-bcm2835 and gpio-core.c, one is as output,
>    the other is as interrupt
> 2) call write_rt to send a pulse from output
> 3) call read_rt to get timestamps recorded in driver (inner loop)
> 4) also record timespace in user space(outer_loop)
>    outer_loop is inner_loop plus overhead of event wakeup
> 5) ftrace enable/disable
> 
> 2, react mode
> 1) apply 2 gpio pins by calling service in gpio RTDM driver
>    like gpio-bcm2835 and gpio-core.c, one is as ourput,
>    the other is as interrupt
> 2) call read_rt to wait for a pulse from latency box
> 3) call write_rt to send a signal back to latency box
>    as a reaction
> 4) latency box calculates the diff and makes the histogram
> 
> e.g.:
> 1) react mode:
>    gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 1 -l 1000
> 2) loopback mode:
>    gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 0 -l 1000 -h 100 -b 50
> 
> CC: Jan Kiszka <[email protected]>
> CC: Greg Gallagher <[email protected]>
> 
> Signed-off-by: chensong <[email protected]>
> ---
>  configure.ac                     |   1 +
>  doc/asciidoc/man1/gpiobench.adoc |  70 +++++
>  testsuite/Makefile.am            |   3 +-
>  testsuite/gpiobench/Makefile.am  |  18 ++
>  testsuite/gpiobench/gpiobench.c  | 654 
> +++++++++++++++++++++++++++++++++++++++
>  5 files changed, 745 insertions(+), 1 deletion(-)
>  create mode 100644 doc/asciidoc/man1/gpiobench.adoc
>  create mode 100644 testsuite/gpiobench/Makefile.am
>  create mode 100644 testsuite/gpiobench/gpiobench.c
> 
> diff --git a/configure.ac b/configure.ac
> index 29fefab..164c449 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -939,6 +939,7 @@ AC_CONFIG_FILES([ \
>       testsuite/latency/Makefile \
>       testsuite/switchtest/Makefile \
>       testsuite/gpiotest/Makefile \
> +     testsuite/gpiobench/Makefile \
>       testsuite/spitest/Makefile \
>       testsuite/smokey/Makefile \
>       testsuite/smokey/arith/Makefile \
> diff --git a/doc/asciidoc/man1/gpiobench.adoc 
> b/doc/asciidoc/man1/gpiobench.adoc
> new file mode 100644
> index 0000000..bd32ea8
> --- /dev/null
> +++ b/doc/asciidoc/man1/gpiobench.adoc
> @@ -0,0 +1,70 @@
> +// ** The above line should force tbl to be a preprocessor **
> +// Man page for gpiobench
> +//
> +// Copyright (C) 2020 song chen <[email protected]>
> +//
> +// You may distribute under the terms of the GNU General Public
> +// License as specified in the file COPYING that comes with the
> +// Xenomai distribution.
> +//
> +//
> +GPIOBENCH(1)
> +==========
> +:doctype: manpage
> +:revdate: 2020/08/03
> +:man source: Xenomai
> +:man version: {xenover}
> +:man manual: Xenomai Manual
> +
> +NAME
> +-----
> +gpiobench - Xenomai gpio latency benchmark
> +
> +SYNOPSIS
> +---------
> +// The general command line
> +*gpiobench* [ options ]
> +
> +DESCRIPTION
> +------------
> +*gpiobench* is part of the Xenomai test suite. It is a gpio latency
> +benchmark program.  The system must run a suitable Xenomai enabled kernel 
> with
> +the respective module (xeno_timerbench).
> +
> +OPTIONS
> +--------
> +*gpiobench* accepts the following options:
> +
> +*-h <histogram-size>*::
> +default = 100, increase if your last bucket is full
> +
> +*-l <num-of-loops>*::
> +default=1000, number of loops to run the test
> +
> +*-q <quiet>*::
> +print only a summary on exit
> +
> +*-m <test-mode>*::
> +0 = loopback (default), 1 = react
> +
> +*-c <pin-controller>*::
> +name of pin controller
> +
> +*-o <output-pin>*::
> +number of gpio pin as output
> +
> +*-i <interrupt-pin>*::
> +number of gpin pin as interrupt
> +
> +*-p <priority>*::
> +default = 99, task priority
> +
> +*-b <bracktrace>*::
> +default = 1000, send break trace command when latency > breaktrace
> +
> +
> +
> +AUTHOR
> +-------
> +*gpiobench* was written by song chen. This man page
> +was written by song chen.
> diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
> index 76d108e..4932f6d 100644
> --- a/testsuite/Makefile.am
> +++ b/testsuite/Makefile.am
> @@ -1,5 +1,5 @@
>  
> -SUBDIRS = latency smokey
> +SUBDIRS = latency smokey gpiobench
>  
>  if XENO_COBALT
>  SUBDIRS +=           \
> @@ -13,6 +13,7 @@ endif
>  DIST_SUBDIRS =               \
>       clocktest       \
>       gpiotest        \
> +     gpiobench   \
>       latency         \
>       smokey          \
>       spitest         \
> diff --git a/testsuite/gpiobench/Makefile.am b/testsuite/gpiobench/Makefile.am
> new file mode 100644
> index 0000000..cca3395
> --- /dev/null
> +++ b/testsuite/gpiobench/Makefile.am
> @@ -0,0 +1,18 @@
> +testdir = @XENO_TEST_DIR@
> +
> +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
> +
> +test_PROGRAMS = gpiobench
> +
> +gpiobench_SOURCES = gpiobench.c
> +
> +gpiobench_CPPFLAGS =                 \
> +     $(XENO_USER_CFLAGS)     \
> +     -I$(top_srcdir)/include
> +
> +gpiobench_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS)
> +
> +gpiobench_LDADD =                    \
> +     @XENO_CORE_LDADD@       \
> +     @XENO_USER_LDADD@       \
> +     -lpthread -lrt -lm
> diff --git a/testsuite/gpiobench/gpiobench.c b/testsuite/gpiobench/gpiobench.c
> new file mode 100644
> index 0000000..7f19837
> --- /dev/null
> +++ b/testsuite/gpiobench/gpiobench.c
> @@ -0,0 +1,654 @@
> +/*
> + * Copyright (C) 2020 Song Chen <[email protected]>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining
> + * a copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sublicense, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included
> + * in all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
> + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
> + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + */
> +#include <stdlib.h>
> +#include <math.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <error.h>
> +#include <signal.h>
> +#include <sched.h>
> +#include <time.h>
> +#include <sys/time.h>
> +#include <unistd.h>
> +#include <pthread.h>
> +#include <semaphore.h>
> +#include <sys/timerfd.h>
> +#include <xeno_config.h>
> +#include <rtdm/testing.h>
> +#include <rtdm/gpio.h>
> +#include <boilerplate/trace.h>
> +#include <xenomai/init.h>
> +#include <sys/mman.h>
> +#include <getopt.h>
> +
> +#define NS_PER_MS (1000000)
> +#define NS_PER_S (1000000000)
> +
> +#define DEFAULT_PRIO 99
> +#define VERSION_STRING "0.1"
> +#define GPIO_HIGH 1
> +#define GPIO_LOW  0
> +#define MAX_HIST             100
> +#define MAX_CYCLES 1000000
> +#define DEFAULT_LIMIT 1000
> +#define DEV_PATH    "/dev/rtdm/"
> +#define TRACING_ON  "/sys/kernel/debug/tracing/tracing_on"
> +#define TRACING_EVENTS  "/sys/kernel/debug/tracing/events/enable"
> +#define TRACE_MARKER  "/sys/kernel/debug/tracing/trace_marker"
> +#define ON  "1"
> +#define OFF "0"
> +
> +enum {
> +     MODE_LOOPBACK,
> +     MODE_REACT,
> +     MODE_ALL
> +};
> +
> +/* Struct for statistics */
> +struct test_stat {
> +     long inner_min;
> +     long inner_max;
> +     double inner_avg;
> +     long *inner_hist_array;
> +     long inner_hist_overflow;
> +     long outer_min;
> +     long outer_max;
> +     double outer_avg;
> +     long *outer_hist_array;
> +     long outer_hist_overflow;
> +};
> +
> +/* Struct for information */
> +struct test_info {
> +     unsigned long max_cycles;
> +     unsigned long total_cycles;
> +     unsigned long max_histogram;
> +     int mode;
> +     int prio;
> +     int quiet;
> +     int tracelimit;
> +     int fd_dev_intr;
> +     int fd_dev_out;
> +     char pin_controller[32];
> +     pthread_t gpio_task;
> +     int gpio_intr;
> +     int gpio_out;
> +     struct test_stat ts;
> +};
> +
> +struct test_info ti;
> +/* Print usage information */
> +static void display_help(void)
> +{
> +     printf("gpiobench V %s\n", VERSION_STRING);
> +     printf("Usage:\n"
> +            "gpiobench <options>\n\n"
> +
> +            "-b       --breaktrace=USEC  send break trace command when 
> latency > USEC\n"
> +            "                            default=1000\n"
> +            "-h       --histogram=US     dump a latency histogram to stdout 
> after the run\n"
> +            "                            US is the max time to be tracked in 
> microseconds,\n"
> +            "                            default=100\n"
> +            "-l       --loops            number of loops, default=1000\n"
> +            "-p       --prio             priority of highest prio thread, 
> defaults=99\n"
> +            "-q       --quiet            print only a summary on exit\n"
> +            "-o       --output           gpio port number for output, no 
> default value,\n"
> +            "                            must be specified\n"
> +            "-i       --intr             gpio port number as an interrupt, 
> no default value,\n"
> +            "                            must be specified\n"
> +            "-c       --pinctrl          gpio pin controller's name, no 
> default value,\n"
> +            "                            must be specified\n"
> +            "-m       --testmode         0 is loopback mode\n"
> +            "                            1 is react mode which works with a 
> latency box,\n"
> +            "                            default=0\n\n"
> +
> +            "e.g.     gpiobench -o 20 -i 21 -c pinctrl-bcm2835\n"
> +             );
> +}
> +
> +static void process_options(int argc, char *argv[])
> +{
> +     int c = 0;
> +     static const char optstring[] = "h:p:m:l:c:b:i:o:q";
> +
> +     struct option long_options[] = {
> +             { "bracetrace", required_argument, 0, 'b'},
> +             { "histogram", required_argument, 0, 'h'},
> +             { "loops", required_argument, 0, 'l'},
> +             { "prio", required_argument, 0, 'p'},
> +             { "quiet", no_argument, 0, 'q'},
> +             { "output", required_argument, 0, 'o'},
> +             { "intr", required_argument, 0, 'i'},
> +             { "pinctrl", required_argument, 0, 'c'},
> +             { "testmode", required_argument, 0, 'm'},
> +             { 0, 0, 0, 0},
> +     };
> +
> +     while ((c = getopt_long(argc, argv, optstring, long_options,
> +                                             NULL)) != -1) {
> +             switch (c) {
> +             case 'h':
> +                     ti.max_histogram = atoi(optarg);
> +                     break;
> +
> +             case 'p':
> +                     ti.prio = atoi(optarg);
> +                     break;
> +
> +             case 'l':
> +                     ti.max_cycles = atoi(optarg);
> +                     break;
> +
> +             case 'q':
> +                     ti.quiet = 1;
> +                     break;
> +
> +             case 'b':
> +                     ti.tracelimit = atoi(optarg);
> +                     break;
> +
> +             case 'i':
> +                     ti.gpio_intr = atoi(optarg);
> +                     break;
> +
> +             case 'o':
> +                     ti.gpio_out = atoi(optarg);
> +                     break;
> +
> +             case 'c':
> +                     strcpy(ti.pin_controller, optarg);
> +                     break;
> +
> +             case 'm':
> +                     ti.mode = atoi(optarg) >=
> +                             MODE_REACT ? MODE_REACT : MODE_LOOPBACK;
> +                     break;
> +
> +             default:
> +                     display_help();
> +                     exit(2);
> +             }
> +     }
> +
> +     if ((ti.gpio_out == -1) || (ti.gpio_intr == -1)
> +                             || (strlen(ti.pin_controller) == 0)) {
> +             display_help();
> +             exit(2);
> +     }
> +
> +     ti.prio = ti.prio > DEFAULT_PRIO ? DEFAULT_PRIO : ti.prio;
> +     ti.max_cycles = ti.max_cycles > MAX_CYCLES ? MAX_CYCLES : ti.max_cycles;
> +
> +     ti.max_histogram = ti.max_histogram > MAX_HIST ?
> +             MAX_HIST : ti.max_histogram;
> +     ti.ts.inner_hist_array = calloc(ti.max_histogram, sizeof(long));
> +     ti.ts.outer_hist_array = calloc(ti.max_histogram, sizeof(long));
> +}
> +
> +static int thread_msleep(unsigned int ms)
> +{
> +     struct timespec ts = {
> +             .tv_sec = (ms * NS_PER_MS) / NS_PER_S,
> +             .tv_nsec = (ms * NS_PER_MS) % NS_PER_S,
> +     };
> +
> +     return -nanosleep(&ts, NULL);
> +}
> +
> +static inline int64_t calc_us(struct timespec t)
> +{
> +     return (t.tv_sec * NS_PER_S + t.tv_nsec);
> +}
> +
> +static int setevent(char *event, char *val)
> +{
> +     int fd;
> +     int ret;
> +
> +     fd = open(event, O_WRONLY);
> +     if (fd < 0) {
> +             printf("unable to open %s\n", event);
> +             return -1;
> +     }
> +
> +     ret = write(fd, val, strlen(val));
> +     if (ret < 0) {
> +             printf("unable to write %s to %s\n", val, event);
> +             close(fd);
> +             return -1;
> +     }
> +
> +     close(fd);
> +     return 0;
> +}
> +
> +static void tracing(char *enable)
> +{
> +     setevent(TRACING_EVENTS, enable);
> +     setevent(TRACING_ON, enable);
> +}
> +
> +#define write_check(__fd, __buf, __len)                      \
> +     do {                                            \
> +             int __ret = write(__fd, __buf, __len);  \
> +             (void)__ret;                            \
> +     } while (0)
> +
> +#define TRACEBUFSIZ 1024
> +static __thread char tracebuf[TRACEBUFSIZ];
> +
> +static void tracemark(char *fmt, ...) __attribute__((format(printf, 1, 2)));
> +static void tracemark(char *fmt, ...)
> +{
> +     va_list ap;
> +     int len;
> +     int tracemark_fd;
> +
> +     tracemark_fd = open(TRACE_MARKER, O_WRONLY);
> +     if (tracemark_fd == -1) {
> +             printf("unable to open trace_marker file: %s\n", TRACE_MARKER);
> +             return;
> +     }
> +
> +     /* bail out if we're not tracing */
> +     /* or if the kernel doesn't support trace_mark */
> +     if (tracemark_fd < 0)
> +             return;
> +
> +     va_start(ap, fmt);
> +     len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap);
> +     va_end(ap);
> +     write_check(tracemark_fd, tracebuf, len);
> +
> +     close(tracemark_fd);
> +}
> +
> +static int rw_gpio(int value, int index)
> +{
> +     int ret;
> +     struct timespec timestamp;
> +     struct rtdm_gpio_readout rdo;
> +     uint64_t gpio_write, gpio_read, inner_diff, outer_diff;
> +
> +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
> +     gpio_write = calc_us(timestamp);
> +
> +     ret = write(ti.fd_dev_out, &value, sizeof(value));
> +     if (ret < 0) {
> +             printf("write GPIO, failed\n");
> +             return ret;
> +     }
> +
> +     ret = read(ti.fd_dev_intr, &rdo, sizeof(struct rtdm_gpio_readout));
> +     if (ret < 0) {
> +             printf("read GPIO, failed\n");
> +             return ret;
> +     }
> +
> +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
> +     gpio_read = calc_us(timestamp);
> +
> +     inner_diff = (rdo.timestamp - gpio_write) / 1000;
> +     outer_diff = (gpio_read - gpio_write) / 1000;
> +
> +     if (inner_diff < ti.ts.inner_min)
> +             ti.ts.inner_min = inner_diff;
> +     if (inner_diff > ti.ts.inner_max)
> +             ti.ts.inner_max = inner_diff;
> +     ti.ts.inner_avg += (double) inner_diff;
> +     if (inner_diff >= ti.max_histogram)
> +             ti.ts.inner_hist_overflow++;
> +     else
> +             ti.ts.inner_hist_array[inner_diff]++;
> +
> +     if (outer_diff < ti.ts.outer_min)
> +             ti.ts.outer_min = outer_diff;
> +     if (inner_diff > ti.ts.outer_max)
> +             ti.ts.outer_max = outer_diff;
> +     ti.ts.outer_avg += (double) outer_diff;
> +     if (outer_diff >= ti.max_histogram)
> +             ti.ts.outer_hist_overflow++;
> +     else
> +             ti.ts.outer_hist_array[outer_diff]++;
> +
> +     if (ti.quiet == 0)
> +             printf("index: %d, inner_diff: %8ld, outer_diff: %8ld\n",
> +                                     index, inner_diff, outer_diff);
> +
> +     return outer_diff;
> +}
> +
> +static void *run_gpiobench_loop(void *cookie)
> +{
> +     int i, ret;
> +
> +     printf("----rt task, gpio loop, test run----\n");
> +
> +     for (i = 0; i < ti.max_cycles; i++) {
> +             ti.total_cycles = i;
> +             /* send a high level pulse from gpio output pin and
> +              * receive an interrupt from the other gpio pin,
> +              * measuring the time elapsed between the two events
> +              */
> +             ret = rw_gpio(GPIO_HIGH, i);
> +             if (ret < 0) {
> +                     printf("RW GPIO, failed\n");
> +                     break;
> +             } else if (ret > ti.tracelimit) {
> +                     tracemark("hit latency threshold (%d > %d), index: %d",
> +                                             ret, ti.tracelimit, i);
> +                     break;
> +             }
> +
> +             /*take a break, nanosleep here will not jeopardize the latency*/
> +             thread_msleep(10);
> +
> +             ret = rw_gpio(GPIO_LOW, i);
> +             /* send a low level pulse from gpio output pin and
> +              * receive an interrupt from the other gpio pin,
> +              * measuring the time elapsed between the two events
> +              */
> +             if (ret < 0) {
> +                     printf("RW GPIO, failed\n");
> +                     break;
> +             } else if (ti.tracelimit && ret > ti.tracelimit) {
> +                     tracemark("hit latency threshold (%d > %d), index: %d",
> +                                             ret, ti.tracelimit, i);
> +                     break;
> +             }
> +
> +             /*take a break, nanosleep here will not jeopardize the latency*/
> +             thread_msleep(10);
> +     }
> +
> +     ti.ts.inner_avg /= (ti.total_cycles * 2);
> +     ti.ts.outer_avg /= (ti.total_cycles * 2);
> +
> +     return NULL;
> +}
> +
> +static void *run_gpiobench_react(void *cookie)
> +{
> +     int value, ret, i;
> +     struct rtdm_gpio_readout rdo;
> +
> +     printf("----rt task, gpio react, test run----\n");
> +
> +     for (i = 0; i < ti.max_cycles; i++) {
> +             /* received a pulse from latency box from one of
> +              * the gpio pin pair
> +              */
> +             ret = read(ti.fd_dev_intr, &rdo, sizeof(rdo));
> +             if (ret < 0) {
> +                     printf("RW GPIO read, failed\n");
> +                     break;
> +             }
> +
> +             if (ti.quiet == 0)
> +                     printf("idx: %d, received signal from latency box\n",
> +                                 i);
> +
> +             /* send a signal back from the other gpio pin
> +              * to latency box as the acknowledge,
> +              * latency box will measure the time elapsed
> +              * between the two events
> +              */
> +             value = GPIO_HIGH;
> +             ret = write(ti.fd_dev_out, &value, sizeof(value));
> +             if (ret < 0) {
> +                     printf("RW GPIO write, failed\n");
> +                     break;
> +             }
> +
> +             if (ti.quiet == 0)
> +                     printf("idx: %d, sent reaction to latency box\n", i);
> +     }
> +
> +     return NULL;
> +}
> +
> +static void setup_sched_parameters(pthread_attr_t *attr, int prio)
> +{
> +     struct sched_param p;
> +     int ret;
> +
> +     ret = pthread_attr_init(attr);
> +     if (ret) {
> +             printf("pthread_attr_init(), failed\n");
> +             return;
> +     }
> +
> +     ret = pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
> +     if (ret) {
> +             printf("pthread_attr_setinheritsched(), failed\n");
> +             return;
> +     }
> +
> +     ret = pthread_attr_setschedpolicy(attr,
> +                             prio ? SCHED_FIFO : SCHED_OTHER);
> +     if (ret) {
> +             printf("pthread_attr_setschedpolicy(), failed\n");
> +             return;
> +     }
> +
> +     p.sched_priority = prio;
> +     ret = pthread_attr_setschedparam(attr, &p);
> +     if (ret) {
> +             printf("pthread_attr_setschedparam(), failed\n");
> +             return;
> +     }
> +}
> +
> +static void init_ti(void)
> +{
> +     memset(&ti, 0, sizeof(struct test_info));
> +     ti.prio = DEFAULT_PRIO;
> +     ti.max_cycles = MAX_CYCLES;
> +     ti.total_cycles = MAX_CYCLES;
> +     ti.max_histogram = MAX_HIST;
> +     ti.tracelimit = DEFAULT_LIMIT;
> +     ti.quiet = 0;
> +     ti.gpio_out = -1;
> +     ti.gpio_intr = -1;
> +     ti.mode = MODE_LOOPBACK;
> +
> +     ti.ts.inner_min = ti.ts.outer_min = DEFAULT_LIMIT;
> +     ti.ts.inner_max = ti.ts.outer_max = 0;
> +     ti.ts.inner_avg = ti.ts.outer_avg = 0.0;
> +}
> +
> +static void print_hist(void)
> +{
> +     int i;
> +
> +     printf("\n");
> +     printf("# Inner Loop Histogram\n");
> +     printf("# Inner Loop latency is the latency in kernel space\n"
> +                "# between gpio_set_value and irq handler\n");
> +
> +     for (i = 0; i < ti.max_histogram; i++) {
> +             unsigned long curr_latency = ti.ts.inner_hist_array[i];
> +
> +             printf("%06d ", i);
> +             printf("%06lu\n", curr_latency);
> +     }
> +
> +     printf("# Total:");
> +     printf(" %09lu", ti.total_cycles);
> +     printf("\n");
> +
> +     printf("# Min Latencies:");
> +     printf(" %05lu", ti.ts.inner_min);
> +     printf("\n");
> +     printf("# Avg Latencies:");
> +     printf(" %05lf", ti.ts.inner_avg);
> +     printf("\n");
> +     printf("# Max Latencies:");
> +     printf(" %05lu", ti.ts.inner_max);
> +     printf("\n");
> +
> +     printf("\n");
> +     printf("\n");
> +
> +     printf("# Outer Loop Histogram\n");
> +     printf("# Outer Loop latency is the latency in user space\n"
> +                "# between write and read\n"
> +                "# Technically, outer loop latency is inner loop latercy\n"
> +                "# plus overhead of event wakeup\n");
> +
> +     for (i = 0; i < ti.max_histogram; i++) {
> +             unsigned long curr_latency = ti.ts.outer_hist_array[i];
> +
> +             printf("%06d ", i);
> +             printf("%06lu\n", curr_latency);
> +     }
> +
> +     printf("# Total:");
> +     printf(" %09lu", ti.total_cycles);
> +     printf("\n");
> +
> +     printf("# Min Latencies:");
> +     printf(" %05lu", ti.ts.outer_min);
> +     printf("\n");
> +     printf("# Avg Latencies:");
> +     printf(" %05lf", ti.ts.outer_avg);
> +     printf("\n");
> +     printf("# Max Latencies:");
> +     printf(" %05lu", ti.ts.outer_max);
> +     printf("\n");
> +}
> +
> +static void cleanup(void)
> +{
> +     int ret;
> +
> +     if (ti.tracelimit < DEFAULT_LIMIT)
> +             tracing(OFF);
> +
> +     ret = close(ti.fd_dev_out);
> +     if (ret < 0)
> +             printf("can't close gpio_out device\n");
> +
> +     ret = close(ti.fd_dev_intr);
> +     if (ret < 0)
> +             printf("can't close gpio_intr device\n");
> +
> +     if (ti.mode == MODE_LOOPBACK)
> +             print_hist();
> +
> +}
> +
> +static void cleanup_and_exit(int sig)
> +{
> +     printf("Signal %d received\n", sig);
> +     cleanup();
> +     exit(0);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +     struct sigaction sa __attribute__((unused));
> +     int ret = 0;
> +     pthread_attr_t tattr;
> +     int trigger, value;
> +     char dev_name[64];
> +
> +     init_ti();
> +
> +     process_options(argc, argv);
> +
> +     ret = mlockall(MCL_CURRENT|MCL_FUTURE);
> +     if (ret) {
> +             printf("mlockall failed\n");
> +             goto out;
> +     }
> +
> +     sprintf(dev_name, "%s%s/gpio%d",
> +                 DEV_PATH, ti.pin_controller, ti.gpio_out);
> +     ti.fd_dev_out = open(dev_name, O_RDWR);
> +     if (ti.fd_dev_out < 0) {
> +             printf("can't open %s\n", dev_name);
> +             goto out;
> +     }
> +
> +     if (ti.gpio_out) {
> +             value = 0;
> +             ret = ioctl(ti.fd_dev_out, GPIO_RTIOC_DIR_OUT, &value);
> +             if (ret) {
> +                     printf("ioctl gpio port output, failed\n");
> +                     goto out;
> +             }
> +     }
> +
> +     sprintf(dev_name, "%s%s/gpio%d",
> +                 DEV_PATH, ti.pin_controller, ti.gpio_intr);
> +     ti.fd_dev_intr = open(dev_name, O_RDWR);
> +     if (ti.fd_dev_intr < 0) {
> +             printf("can't open %s\n", dev_name);
> +             goto out;
> +     }
> +
> +     if (ti.gpio_intr) {
> +             trigger = GPIO_TRIGGER_EDGE_FALLING|GPIO_TRIGGER_EDGE_RISING;
> +             value = 1;
> +
> +             ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_IRQEN, &trigger);
> +             if (ret) {
> +                     printf("ioctl gpio port interrupt, failed\n");
> +                     goto out;
> +             }
> +
> +             ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_TS, &value);
> +             if (ret) {
> +                     printf("ioctl gpio port ts, failed\n");
> +                     goto out;
> +             }
> +     }
> +
> +     if (ti.tracelimit < DEFAULT_LIMIT)
> +             tracing(ON);
> +
> +     signal(SIGTERM, cleanup_and_exit);
> +     signal(SIGINT, cleanup_and_exit);
> +     setup_sched_parameters(&tattr, ti.prio);
> +
> +     if (ti.mode == MODE_LOOPBACK)
> +             ret = pthread_create(&ti.gpio_task, &tattr,
> +                                     run_gpiobench_loop, NULL);
> +     else
> +             ret = pthread_create(&ti.gpio_task, &tattr,
> +                                     run_gpiobench_react, NULL);
> +
> +     if (ret) {
> +             printf("pthread_create(gpiotask), failed\n");
> +             goto out;
> +     }
> +
> +     pthread_join(ti.gpio_task, NULL);
> +     pthread_attr_destroy(&tattr);
> +
> +out:
> +     cleanup();
> +     return 0;
> +}
> 

Looks good to me. Greg, any comments from your side?

Jan

-- 
Siemens AG, Corporate Technology, CT RDA IOT SES-DE
Corporate Competence Center Embedded Linux

Reply via email to