The TGPIO testcase includes the following contents: 1. tgpio_info: get a report of available TGPIO capabilities for a specified device. 2. tgpio_oneshot_output: demonstrating a TGPIO single-shot output scenario. the sample demonstrates how to trigger a TGPIO pin at the specific timestamp. in this sample, this timestamp is specified as an offset from the Base time 3. tgpio_periodic_output: demonstrating how to set a TGPIO pin to periodic output mode the sample generates a signal based on the specified period (1 second, for example). the sample generates a voltage change on the output pin at the specific timestamp. 4. tgpio_input: prints a timestamp when an edge is detected, indicating a change in the TGPIO input pin state.
Signed-off-by: Zqiang <[email protected]> --- configure.ac | 1 + include/rtdm/rt_ptp_clock.h | 24 ++ include/smokey/smokey.h | 10 + lib/smokey/helpers.c | 19 + testsuite/Makefile.am | 2 + testsuite/tgpiotest/Makefile.am | 19 + testsuite/tgpiotest/tgpiotest.c | 591 ++++++++++++++++++++++++++++++++ 7 files changed, 666 insertions(+) create mode 100644 include/rtdm/rt_ptp_clock.h create mode 100644 testsuite/tgpiotest/Makefile.am create mode 100644 testsuite/tgpiotest/tgpiotest.c diff --git a/configure.ac b/configure.ac index e7a1701..8a5b27b 100644 --- a/configure.ac +++ b/configure.ac @@ -971,6 +971,7 @@ AC_CONFIG_FILES([ \ testsuite/switchtest/Makefile \ testsuite/gpiotest/Makefile \ testsuite/gpiobench/Makefile \ + testsuite/tgpiotest/Makefile \ testsuite/spitest/Makefile \ testsuite/smokey/Makefile \ testsuite/smokey/arith/Makefile \ diff --git a/include/rtdm/rt_ptp_clock.h b/include/rtdm/rt_ptp_clock.h new file mode 100644 index 0000000..8cf8383 --- /dev/null +++ b/include/rtdm/rt_ptp_clock.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 Zqiang <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef _RTDM_PTP_H +#define _RTDM_PTP_H + +#include <rtdm/rtdm.h> +#include <rtdm/uapi/rt_ptp_clock.h> + +#endif diff --git a/include/smokey/smokey.h b/include/smokey/smokey.h index 185fd3a..9576cfc 100644 --- a/include/smokey/smokey.h +++ b/include/smokey/smokey.h @@ -31,6 +31,12 @@ #define do_fork vfork #endif +#define SMOKEY_ULONG(__name) { \ + .name = # __name, \ + .parser = smokey_ulong, \ + .matched = 0, \ + } + #define SMOKEY_INT(__name) { \ .name = # __name, \ .parser = smokey_int, \ @@ -65,6 +71,7 @@ struct smokey_arg { struct smokey_arg *arg); union { int n_val; + unsigned long long ln_val; char *s_val; size_t l_val; } u; @@ -112,6 +119,7 @@ struct smokey_test { #define SMOKEY_ARG_BOOL(__plugin, __arg) (!!SMOKEY_ARG_INT(__plugin, __arg)) #define SMOKEY_ARG_STRING(__plugin, __arg) (SMOKEY_ARG(__plugin, __arg)->u.s_val) #define SMOKEY_ARG_SIZE(__plugin, __arg) (SMOKEY_ARG(__plugin, __arg)->u.l_val) +#define SMOKEY_ARG_ULONG(__plugin, __arg) (SMOKEY_ARG(__plugin, __arg)->u.ln_val) #define smokey_arg_isset(__t, __name) (smokey_lookup_arg(__t, __name)->matched) #define smokey_arg_int(__t, __name) (smokey_lookup_arg(__t, __name)->u.n_val) @@ -221,6 +229,8 @@ void smokey_register_plugin(struct smokey_test *t); int smokey_int(const char *s, struct smokey_arg *arg); +int smokey_ulong(const char *s, struct smokey_arg *arg); + int smokey_bool(const char *s, struct smokey_arg *arg); int smokey_string(const char *s, struct smokey_arg *arg); diff --git a/lib/smokey/helpers.c b/lib/smokey/helpers.c index da84c86..4ba3a48 100644 --- a/lib/smokey/helpers.c +++ b/lib/smokey/helpers.c @@ -47,6 +47,25 @@ int smokey_int(const char *s, struct smokey_arg *arg) return ret; } +int smokey_ulong(const char *s, struct smokey_arg *arg) +{ + char *name, *p; + int ret; + + ret = sscanf(s, "%m[_a-z]=%m[^\n]", &name, &p); + if (ret != 2 || !(isdigit(*p) || *p == '-')) + return 0; + + ret = !strcmp(name, arg->name); + if (ret) + arg->u.ln_val = strtoull(p, NULL, 10); + + free(p); + free(name); + + return ret; +} + int smokey_bool(const char *s, struct smokey_arg *arg) { int ret; diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am index 4932f6d..6829f20 100644 --- a/testsuite/Makefile.am +++ b/testsuite/Makefile.am @@ -7,6 +7,7 @@ SUBDIRS += \ gpiotest \ spitest \ switchtest \ + tgpiotest \ xeno-test endif @@ -18,4 +19,5 @@ DIST_SUBDIRS = \ smokey \ spitest \ switchtest \ + tgpiotest \ xeno-test diff --git a/testsuite/tgpiotest/Makefile.am b/testsuite/tgpiotest/Makefile.am new file mode 100644 index 0000000..0a5b15c --- /dev/null +++ b/testsuite/tgpiotest/Makefile.am @@ -0,0 +1,19 @@ +testdir = @XENO_TEST_DIR@ + +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC) + +test_PROGRAMS = tgpiotest + +tgpiotest_SOURCES = tgpiotest.c + +tgpiotest_CPPFLAGS = \ + $(XENO_USER_CFLAGS) \ + -I$(top_srcdir)/include + +tgpiotest_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS) + +tgpiotest_LDADD = \ + ../../lib/smokey/libsmokey@[email protected] \ + @XENO_CORE_LDADD@ \ + @XENO_USER_LDADD@ \ + -lpthread -lrt diff --git a/testsuite/tgpiotest/tgpiotest.c b/testsuite/tgpiotest/tgpiotest.c new file mode 100644 index 0000000..d577cfe --- /dev/null +++ b/testsuite/tgpiotest/tgpiotest.c @@ -0,0 +1,591 @@ +/* + * Copyright (C) 2022 Zqiang <[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 <stdio.h> +#include <error.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <smokey/smokey.h> +#include <rtdm/rt_ptp_clock.h> +#include <stdint.h> +#include <signal.h> + +void signal_handler(int); +int running; +sem_t send_signal; +void signal_send(int); + +#define TIME_VAL (1000000) +#define TCC_NSEC_PER_SEC (1000000000U) +#define SLEEP_TIME_USEC (100) +#define SLEEP_TIME_SEC (20) +#define CROSSTIMESTAMP_SLEEP_TIME_USEC (1000) +#define TIME_BUFFER_SIZE (1024) + +static const char* functions[] = {"None", "External timestamp", + "Periodic signal"}; + +struct settings_t +{ + int pin; + int channel; + uint64_t start; + uint64_t period; +}; + +smokey_test_plugin(tgpio_oneshot_output, + SMOKEY_ARGLIST( + SMOKEY_STRING(device), + SMOKEY_INT(pin), + SMOKEY_INT(channel), + SMOKEY_ULONG(start), + ), + "TGPIO ONESHOT OUTPUT.\n" + "\tdevice=<device-path>.\n" + "\tpin=Specify the output pin index. Default: 0.\n" + "\tchannel=Specify the channel for the output pin. Default: 0.\n" + "\tstart=Specify the output delay in nanoseconds. Default: 1ms\n" +); + + +smokey_test_plugin(tgpio_periodic_output, + SMOKEY_ARGLIST( + SMOKEY_STRING(device), + SMOKEY_INT(pin), + SMOKEY_INT(channel), + SMOKEY_ULONG(period), + ), + "TGPIO PERIODIC OUTPUT.\n" + "\tdevice=<device-path>.\n" + "\tpin=Specify the output pin index. Default: 0.\n" + "\tchannel=Specify the channel for the output pin. Default: 0.\n" + "\tperiod=Specify the output period in nanoseconds. Default: 1ms\n" +); + +smokey_test_plugin(tgpio_input, + SMOKEY_ARGLIST( + SMOKEY_STRING(device), + SMOKEY_INT(pin), + SMOKEY_INT(channel), + SMOKEY_INT(mode), + ), + "TGPIO INPUT.\n" + "\tdevice=<device-path>.\n" + "\tpin=Specify the input pin index. Default: 0.\n" + "\tchannel=Specify the channel for the output pin. Default: 0.\n" + "\tmode=Specify interrupt or poll mode. 0:poll 1:interrupt.\n" + "\tnote: only pse-tgpio support interrupt mode.\n" +); + +smokey_test_plugin(tgpio_info, + SMOKEY_ARGLIST( + SMOKEY_STRING(device), + ), + "Get TGPIO info.\n" + "\tdevice=<device-path>." +); + +static uint64_t ptptime2ns(struct rt_ptp_clock_time time) +{ + return time.sec * TCC_NSEC_PER_SEC + time.nsec; +} + +static struct rt_ptp_clock_time ns2ptptime(uint64_t nsec) +{ + return (struct rt_ptp_clock_time) { + nsec / TCC_NSEC_PER_SEC, // seconds + (unsigned int)(nsec % TCC_NSEC_PER_SEC), // nanoseconds + 0 // reserved + }; +} + +static int run_tgpio_oneshot_output(struct smokey_test *t, int argc, + char *const argv[]) +{ + struct rt_ptp_sys_offset_precise cur_offset = {0}; + int fd, ret = 0; + const char *device = NULL; + + smokey_parse_args(t, argc, argv); + struct settings_t sample_setting = {.pin = 0, .channel = 0, + .start = TIME_VAL}; + + if (!SMOKEY_ARG_ISSET(tgpio_oneshot_output, device)) { + smokey_warning("missing device= specification"); + return -EINVAL; + } + + device = SMOKEY_ARG_STRING(tgpio_oneshot_output, device); + fd = open(device, O_RDONLY|O_NONBLOCK); + if (fd < 0) { + ret = -errno; + smokey_warning("cannot open device %s [%s]", + device, symerror(ret)); + return ret; + } + + if (SMOKEY_ARG_ISSET(tgpio_oneshot_output, pin)) + sample_setting.pin = SMOKEY_ARG_INT(tgpio_oneshot_output, pin); + + if (SMOKEY_ARG_ISSET(tgpio_oneshot_output, channel)) + sample_setting.channel = SMOKEY_ARG_INT(tgpio_oneshot_output, + channel); + + if (SMOKEY_ARG_ISSET(tgpio_oneshot_output, start)) + sample_setting.start = SMOKEY_ARG_ULONG(tgpio_oneshot_output, + start); + + struct rt_ptp_pin_desc desc = {0}; + desc.index = sample_setting.pin; + desc.func = 2; + desc.chan = sample_setting.channel; + ret = smokey_check_errno(ioctl(fd, RT_PTP_PIN_SETFUNC2, &desc)); + if (ret < 0) + goto end; + + ret = smokey_check_errno(ioctl(fd, RT_PTP_SYS_OFFSET_PRECISE2, + &cur_offset)); + if (ret < 0) + goto end; + + uint64_t art_cur_ns = ptptime2ns(cur_offset.device); + + /* Set up oneshot for this channel */ + struct rt_ptp_perout_request request = {0}; + request.start = ns2ptptime(art_cur_ns + sample_setting.start); + request.index = sample_setting.channel; + request.flags = RT_PTP_PEROUT_ONE_SHOT; // Set mode flag to one shot + ret = smokey_check_errno(ioctl(fd, RT_PTP_PEROUT_REQUEST2, &request)); + if (ret < 0) + goto end; + + smokey_warning("Single shot output requested on:\n" + "Pin %d, channel %d, delay %ld nsec\n", + sample_setting.pin, + sample_setting.channel, + sample_setting.start); +end: + request.start.sec = 0; + request.start.nsec = 0; + request.flags = 0; + ioctl(fd, RT_PTP_PEROUT_REQUEST2, &request); + desc.func = 0; + ioctl(fd, RT_PTP_PIN_SETFUNC2, &desc); + close(fd); + return ret; +} + +void signal_send(int dummy) +{ + sem_post(&send_signal); +} + +static int run_tgpio_periodic_output(struct smokey_test *t, int argc, + char * const argv[]) +{ + int fd, ret = 0; + const char *device = NULL; + struct rt_ptp_sys_offset_precise prev_offset = {}; + struct rt_ptp_sys_offset_precise cur_offset = {}; + + smokey_parse_args(t, argc, argv); + struct settings_t sample_setting = {.pin = 0, .channel = 0, + .period = TIME_VAL}; + + if (!SMOKEY_ARG_ISSET(tgpio_periodic_output, device)) { + smokey_warning("missing device= specification"); + return -EINVAL; + } + + device = SMOKEY_ARG_STRING(tgpio_periodic_output, device); + fd = open(device, O_RDONLY|O_NONBLOCK); + if (fd < 0) { + ret = -errno; + smokey_warning("cannot open device %s [%s]", + device, symerror(ret)); + return ret; + } + + if (SMOKEY_ARG_ISSET(tgpio_periodic_output, pin)) + sample_setting.pin = SMOKEY_ARG_INT(tgpio_periodic_output, pin); + + if (SMOKEY_ARG_ISSET(tgpio_periodic_output, channel)) + sample_setting.channel = SMOKEY_ARG_INT(tgpio_periodic_output, + channel); + + if (SMOKEY_ARG_ISSET(tgpio_periodic_output, period)) + sample_setting.period = SMOKEY_ARG_ULONG(tgpio_periodic_output, + period); + + /* Set up pin's channel and output mode */ + struct rt_ptp_pin_desc desc = {0}; + desc.index = sample_setting.pin; + desc.func = 2; + desc.chan = sample_setting.channel; + ret = smokey_check_errno(ioctl(fd, RT_PTP_PIN_SETFUNC2, &desc)); + if (ret < 0) + goto end; + + ret = smokey_check_errno(ioctl(fd, RT_PTP_SYS_OFFSET_PRECISE2, + &prev_offset)); + if (ret < 0) + goto end; + usleep(CROSSTIMESTAMP_SLEEP_TIME_USEC); + ret = smokey_check_errno(ioctl(fd, RT_PTP_SYS_OFFSET_PRECISE2, + &cur_offset)); + if (ret < 0) + goto end; + + uint64_t art_cur_ns = ptptime2ns(cur_offset.device); + uint64_t tsc_cur_ns = ptptime2ns(cur_offset.sys_realtime); + long double art_to_tsc_ratio = (long double)(art_cur_ns - + ptptime2ns(prev_offset.device)) / + (tsc_cur_ns - ptptime2ns(prev_offset.sys_realtime)); + + /* Base time */ + /* Measure offsets from the beginning of the current second + 2, + i.e., zero nanoseconds */ + uint64_t tsc_basetime = (cur_offset.sys_realtime.sec + 2) * + TCC_NSEC_PER_SEC; + int64_t delta = tsc_basetime - tsc_cur_ns; + uint64_t art_basetime = art_cur_ns + delta * art_to_tsc_ratio; + + /* Set up signal generation (period and start) for this channel + * Note: TGPIO PTP API sets the half-period of the signal */ + struct rt_ptp_perout_request request = {0}; + request.index = sample_setting.channel; + request.period = ns2ptptime(sample_setting.period); + request.start = ns2ptptime(art_basetime); + ret = smokey_check_errno(ioctl(fd, RT_PTP_PEROUT_REQUEST2, &request)); + if (ret < 0 ) + goto end; + + smokey_warning("Start periodic output.\n" + "Pin %d, channel %d, period %ld nsec\n" + "To interrupt, use Ctrl+C\n", + sample_setting.pin, + sample_setting.channel, + sample_setting.period); + + sem_init(&send_signal, 0, 0); + signal(SIGINT, signal_send); + + sem_wait(&send_signal); + + smokey_warning("\nDone\n"); + sem_destroy(&send_signal); +end: + /* Shut down TGPIO pin */ + request.start.sec = 0; + request.start.nsec = 0; + request.period.sec = 0; + request.period.nsec = 0; + ioctl(fd, RT_PTP_PEROUT_REQUEST2, &request); + desc.func = 0; + ioctl(fd, RT_PTP_PIN_SETFUNC2, &desc); + close(fd); + return ret; +} + + +static char* convert_to_human_readable(time_t seconds) +{ + static char time_string[TIME_BUFFER_SIZE] = {0}; + + struct tm* hr_time = localtime(&seconds); + strftime(time_string, TIME_BUFFER_SIZE, "%X", hr_time); + return time_string; +} + +void signal_handler(int dummy) +{ + running = 0; +} + +static int run_tgpio_input(struct smokey_test *t, int argc, char * const argv[]) +{ + const char *device = NULL; + int fd, ret = 0; + smokey_parse_args(t, argc, argv); + struct settings_t sample_setting = {.pin = 0, .channel = 0}; + struct rt_ptp_sys_offset_precise initial_offset = {0}; + struct rt_ptp_sys_offset_precise current_offset = {0}; + struct rt_ptp_event_count_tstamp events = {0}; + running = 1; + int mode_interrupt = 0; + struct rt_ptp_extts_event event; + + if (!SMOKEY_ARG_ISSET(tgpio_input, device)) { + smokey_warning("missing device= specification"); + return -EINVAL; + } + + device = SMOKEY_ARG_STRING(tgpio_input, device); + fd = open(device, O_RDONLY|O_NONBLOCK); + if (fd < 0) { + ret = -errno; + smokey_warning("cannot open device %s [%s]", + device, symerror(ret)); + return ret; + } + + if (SMOKEY_ARG_ISSET(tgpio_input, pin)) + sample_setting.pin = SMOKEY_ARG_INT(tgpio_input, pin); + + if (SMOKEY_ARG_ISSET(tgpio_input, channel)) + sample_setting.channel = SMOKEY_ARG_INT(tgpio_input, channel); + + if (SMOKEY_ARG_ISSET(tgpio_input, mode)) + mode_interrupt = SMOKEY_ARG_INT(tgpio_input, mode); + + if (!mode_interrupt) + signal(SIGINT, signal_handler); + + struct rt_ptp_pin_desc desc = {0}; + desc.index = sample_setting.pin; + desc.func = 1; /* Set external timestamp auxiliary function */ + desc.chan = sample_setting.channel; + ret = smokey_check_errno(ioctl(fd, RT_PTP_PIN_SETFUNC2, &desc)); + if (ret < 0) + goto end; + + /* Enable external timestamp (input) mode */ + struct rt_ptp_extts_request request = {0}; + request.index = sample_setting.channel; + /* Detects both rising and falling edges */ + request.flags = RT_PTP_ENABLE_FEATURE | RT_PTP_EXTTS_EDGES; + ret = smokey_check_errno(ioctl(fd, RT_PTP_EXTTS_REQUEST2, &request)); + if (ret < 0) + goto end; + + /* Set up channel for event count*/ + events.index = sample_setting.channel; + + /* Get current ART and TSC timestamp using HW cross-timestamping to + calculate relation between clocks */ + ret = smokey_check_errno(ioctl(fd, RT_PTP_SYS_OFFSET_PRECISE2, + &initial_offset)); + if (ret < 0) + goto end; + + smokey_warning("Start input sample.\n" + "Pin %d, channel %d\n" + "To interrupt, use Ctrl+C\n", + sample_setting.pin, + sample_setting.channel); + + /* Update the event counter */ + ret = smokey_check_errno(ioctl(fd, RT_PTP_EVENT_COUNT_TSTAMP2, &events)); + if (ret < 0) + goto end; + + uint64_t previous_count = events.event_count; + + while(running) { + if (!mode_interrupt) { + ret = smokey_check_errno(ioctl(fd, + RT_PTP_EVENT_COUNT_TSTAMP2, &events)); + if (ret < 0) + goto end; + /* When the event count has changed from the original + value */ + if (events.event_count != previous_count) { + /* We should convert ART timestamp read from + TGPIO to display correct timestamps for captured + edges. We use hardware cross-timestamping to read + ART and TSC clocks simultaneously. Two cross- + timestamps with some delay between allow to + calculate the clocks' speed ratio and convert + timestamps from one to another */ + + /* Get current ART and PCS timestamps using HW + cross-timestamping and calculate ART clock and + system clock speed ratio */ + ret = smokey_check_errno(ioctl(fd, + RT_PTP_SYS_OFFSET_PRECISE2, + ¤t_offset)); + if (ret < 0) + goto end; + + uint64_t art_cur_ns = + ptptime2ns(current_offset.device); + uint64_t tsc_cur_ns = + ptptime2ns(current_offset.sys_realtime); + long double art_to_tsc_ratio = (long double) + (art_cur_ns - + ptptime2ns(initial_offset.device)) / + (tsc_cur_ns - + ptptime2ns(initial_offset.sys_realtime)); + + /* Convert TGPIO event timestamp to the system + clock */ + int64_t delta = ptptime2ns(events.device_time) - + ptptime2ns(initial_offset.device); + uint64_t tsc_event = + ptptime2ns(initial_offset.sys_realtime) + + delta / art_to_tsc_ratio; + + smokey_warning("Poll mode detected edge on" + "channel %d at %s.%09lu \n", + events.index, + convert_to_human_readable(tsc_event / + TCC_NSEC_PER_SEC), + tsc_event % TCC_NSEC_PER_SEC); + + previous_count = events.event_count; + } + /* Poll frequency 10 KHz. Even if signal edge occurs when + application is sleeping, it will be captured by TGPIO + HW and application will get the precise timestamp of the + edge after resuming on the next iteration*/ + usleep(SLEEP_TIME_USEC); + } else { + ret = smokey_check_errno(read(fd, &event, + sizeof(struct rt_ptp_extts_event))); + if (ret < 0) + goto end; + ret = smokey_check_errno(ioctl(fd, + RT_PTP_SYS_OFFSET_PRECISE2, + ¤t_offset)); + if (ret < 0) + goto end; + uint64_t art_cur_ns = ptptime2ns(current_offset.device); + uint64_t tsc_cur_ns = + ptptime2ns(current_offset.sys_realtime); + long double art_to_tsc_ratio = + (long double)(art_cur_ns - + ptptime2ns(initial_offset.device)) / + (tsc_cur_ns - ptptime2ns(initial_offset.sys_realtime)); + + int64_t delta = ptptime2ns(event.t) - + ptptime2ns(initial_offset.device); + uint64_t tsc_event = + ptptime2ns(initial_offset.sys_realtime) + + delta / art_to_tsc_ratio; + + smokey_warning("Interrupt mode detected edge on channel" + "%d at %s.%09lu \n", + event.index, + convert_to_human_readable(tsc_event / + TCC_NSEC_PER_SEC), + tsc_event % TCC_NSEC_PER_SEC); + } + } + smokey_warning("\nDone\n"); +end: + /* Shut down TGPIO pin */ + request.flags = 0; + ioctl(fd, RT_PTP_EXTTS_REQUEST2, &request); + desc.func = 0; + ioctl(fd, RT_PTP_PIN_SETFUNC2, &desc); + close(fd); + return ret; +} + +static int run_tgpio_info(struct smokey_test *t, int argc, char *const argv[]) +{ + const char *device = NULL; + int fd, ret = 0; + struct rt_ptp_clock_caps caps = {0}; + struct rt_ptp_pin_desc desc = {0}; + int n_pins = 0; + + smokey_parse_args(t, argc, argv); + + if (!SMOKEY_ARG_ISSET(tgpio_info, device)) { + smokey_warning("missing device= specification"); + return -EINVAL; + } + + device = SMOKEY_ARG_STRING(tgpio_info, device); + fd = open(device, O_RDONLY|O_NONBLOCK); + if (fd < 0) { + ret = -errno; + smokey_warning("cannot open device %s [%s]", + device, symerror(ret)); + return ret; + } + + ret = smokey_check_errno(ioctl(fd, RT_PTP_CLOCK_GETCAPS2, &caps)); + if (ret < 0) + goto end; + + smokey_warning("Available capabilities:\n" + " Pins: %d\n" + " Input function - External timestamp channels: %d\n" + " Output function - Programmable periodic signals: %d\n" + " Precise system-device cross-timestamps: %s\n", + caps.n_pins, + caps.n_ext_ts, + caps.n_per_out, + caps.cross_timestamping ? "Supported" : "Not supported"); + + n_pins = caps.n_pins; + if (n_pins > 0) { + smokey_warning("Available TGPIO pins:\n"); + smokey_warning("Pin Name Index Used Function Channel\n"); + for (int i = 0; i < n_pins; i++) { + desc.index = (unsigned)i; + ret = smokey_check_errno(ioctl(fd, RT_PTP_PIN_GETFUNC2, + &desc)); + if (ret < 0) + goto end; + smokey_warning("%s %d %s %d\n", + desc.name, desc.index, + functions[desc.func], desc.chan); + } + } +end: + close(fd); + return ret; +} + +int main(int argc, char *const argv[]) +{ + struct smokey_test *t; + int ret, fails = 0; + + if (pvlist_empty(&smokey_test_list)) + return 0; + + for_each_smokey_test(t) { + ret = t->run(t, argc, argv); + if (ret) { + if (ret == -ENOSYS) { + smokey_note("%s skipped (no kernel support)", + t->name); + continue; + } + fails++; + if (smokey_keep_going) + continue; + if (smokey_verbose_mode) + error(1, -ret, "test %s failed", t->name); + return 1; + } + smokey_note("%s OK", t->name); + } + return fails != 0; +} -- 2.25.1
