{"thread":{"id":"37101","subject":"[PATCH v4] linuxptp: add phc_ctl program to help debug PHC devices","startedAt":"2014-07-10T22:48:05Z","lastAt":"2014-07-10T23:39:15Z","messageCount":3,"participants":["Jacob Keller","Junio C Hamano","Keller, Jacob E"],"isPatch":true,"patchVersion":4,"patchTotal":null},"messages":[{"id":"245733","messageId":"1405032485-26117-2-git-send-email-jacob.e.keller@intel.com","threadId":"37101","inReplyTo":null,"subject":"[PATCH v4] linuxptp: add phc_ctl program to help debug PHC devices","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2014-07-10T22:48:05Z","receivedAt":"2014-07-10T22:48:05Z","isPatch":true,"sender":{"key":"jacob.e.keller@intel.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"This is an updated version of a script I wrote a couple years ago for\ndebugging the PHC when writing a new driver. I figured that it might be\nhandy for the LinuxPTP project to include, as it can give some insight\ninto the PHC directly. I have updated it to make use of the shared code\nhere, in order to reduce duplication. Hopefully this is of some use to\neveryone.\n\nSigned-off-by: Jacob Keller <jacob.e.keller@intel.com>\n---\n-v4\n* fix manpage warnings and incorrect display\n* add phc_ctl to .gitignore\n\n .gitignore |   1 +\n makefile   |   4 +-\n phc_ctl.8  | 108 ++++++++++++\n phc_ctl.c  | 561 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 673 insertions(+), 1 deletion(-)\n create mode 100644 phc_ctl.8\n create mode 100644 phc_ctl.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 098dcdfe1ea7..0ca22afe2b9a 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -5,3 +5,4 @@\n /phc2sys\n /pmc\n /ptp4l\n+/phc_ctl\ndiff --git a/makefile b/makefile\nindex e36835b05d40..e9f2f8fcf416 100644\n--- a/makefile\n+++ b/makefile\n@@ -22,7 +22,7 @@ CC\t= $(CROSS_COMPILE)gcc\n VER     = -DVER=$(version)\n CFLAGS\t= -Wall $(VER) $(incdefs) $(DEBUG) $(EXTRA_CFLAGS)\n LDLIBS\t= -lm -lrt $(EXTRA_LDFLAGS)\n-PRG\t= ptp4l pmc phc2sys hwstamp_ctl\n+PRG\t= ptp4l pmc phc2sys hwstamp_ctl phc_ctl\n OBJ     = bmc.o clock.o clockadj.o clockcheck.o config.o fault.o \\\n  filter.o fsm.o linreg.o mave.o mmedian.o msg.o ntpshm.o phc.o \\\n  pi.o port.o print.o ptp4l.o raw.o servo.o sk.o stats.o tlv.o \\\n@@ -54,6 +54,8 @@ phc2sys: clockadj.o clockcheck.o linreg.o msg.o ntpshm.o phc.o phc2sys.o pi.o \\\n \n hwstamp_ctl: hwstamp_ctl.o version.o\n \n+phc_ctl: phc_ctl.o phc.o sk.o util.o clockadj.o sysoff.o print.o version.o\n+\n version.o: .version version.sh $(filter-out version.d,$(DEPEND))\n \n .version: force\ndiff --git a/phc_ctl.8 b/phc_ctl.8\nnew file mode 100644\nindex 000000000000..06982690410e\n--- /dev/null\n+++ b/phc_ctl.8\n@@ -0,0 +1,108 @@\n+.TH PHC_CTL 8 \"June 2014\" \"linuxptp\"\n+.SH NAME\n+phc_ctl \\- directly control PHC device clock\n+\n+.SH SYNOPSIS\n+.B phc_ctl\n+[ options ] <device> [ commands ]\n+\n+.SH DESCRIPTION\n+.B phc_ctl\n+is a program which can be used to directly control a PHC clock device.\n+Typically, it is used for debugging purposes, and has little use for general\n+control of the device. For general control of PHC clock devices,\n+.B phc2sys (8)\n+should be preferred.\n+\n+<device> may be either CLOCK_REALTIME, any /dev/ptpX device, or any ethernet\n+device which supports ethtool's get_ts_info ioctl.\n+\n+.SH OPTIONS\n+.TP\n+.BI \\-l \" print-level\"\n+Set the maximum syslog level of messages which should be printed or sent to the\n+system logger. The default is 6 (LOG_INFO).\n+.TP\n+.BI \\-q\n+Do not send messages to syslog. By default messages will be sent.\n+.TP\n+.BI \\-Q\n+Do not print messages to standard output. By default messages will be printed.\n+.TP\n+.BI \\-h\n+Display a help message.\n+.TP\n+.B \\-v\n+Prints the software version and exits.\n+\n+.SH COMMANDS\n+\n+.B phc_ctl\n+is controlled by passing commands which take either an optional or required\n+parameter. The commands (outlined below) will control aspects of the PHC clock\n+device. These commands may be useful for inspecting or debugging the PHC\n+driver, but may have adverse side effects on running instances of\n+.B ptp4l (8)\n+or\n+.B phc2sys (8)\n+\n+.TP\n+.BI set \" seconds\"\n+Set the PHC clock time to the value specified in seconds. Defaults to reading\n+CLOCK_REALTIME if no value is provided.\n+.TP\n+.BI get\n+Get the current time of the PHC clock device.\n+.TP\n+.BI adj \" seconds\"\n+Adjust the PHC clock by an amount of seconds provided. This argument is required.\n+.TP\n+.BI freq \" ppb\"\n+Adjust the frequency of the PHC clock by the specified parts per billion. If no\n+argument is provided, it will attempt to read the current frequency and report\n+it.\n+.TP\n+.BI cmp\n+Compare the PHC clock device to CLOCK_REALTIME, using the best method available.\n+.TP\n+.BI caps\n+Display the device capabiltiies. This is the default command if no commands are\n+provided.\n+.TP\n+.BI wait \" seconds\"\n+Sleep the process for the specified period of time, waking up and resuming\n+afterwards. This command may be useful for sanity checking whether the PHC\n+clock is running as expected.\n+\n+The arguments specified in seconds are read as double precision floating point\n+values, and will scale to nanoseconds. This means providing a value of 5.5\n+means 5 and one half seconds. This allows specifying fairly precise values for time.\n+\n+.SH EXAMPLES\n+\n+Read the current clock time from the device\n+.RS\n+\\f(CWphc_ctl /dev/ptp0 get\\fP\n+.RE\n+\n+Set the PHC clock time to CLOCK_REALTIME\n+.RS\n+\\f(CWphc_ctl /dev/ptp0 set\\fP\n+.RE\n+\n+Set PHC clock time to 0 (seconds since Epoch)\n+.RS\n+\\f(CWphc_ctl /dev/ptp0 set 0.0\\fP\n+.RE\n+\n+Quickly sanity check frequency slewing by setting slewing frequency by positive\n+10%, resetting clock to 0.0 time, waiting for 10 seconds, and then reading\n+time. The time read back should be (roughly) 11 seconds, since the clock was\n+slewed 10% faster.\n+.RS\n+\\f(CWphc_ctl /dev/ptp0 freq 10000000 set 0.0 wait 10.0 get\n+.RE\n+\n+.SH SEE ALSO\n+.BR ptp4l (8)\n+.BR phc2sys (8)\ndiff --git a/phc_ctl.c b/phc_ctl.c\nnew file mode 100644\nindex 000000000000..aa139ac44869\n--- /dev/null\n+++ b/phc_ctl.c\n@@ -0,0 +1,561 @@\n+/*\n+ * @file phc_ctl.c\n+ * @brief Utility program to directly control and debug a PHC device.\n+ * @note Copyright (C) 2014 Jacob Keller <jacob.keller@gmail.com>\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License as published by\n+ * the Free Software Foundation; either version 2 of the License, or\n+ * (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License along\n+ * with this program; if not, write to the Free Software Foundation, Inc.,\n+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n+ */\n+#include <errno.h>\n+#include <fcntl.h>\n+#include <float.h>\n+#include <signal.h>\n+#include <inttypes.h>\n+#include <limits.h>\n+#include <net/if.h>\n+#include <poll.h>\n+#include <stdint.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <string.h>\n+#include <sys/ioctl.h>\n+#include <sys/queue.h>\n+#include <sys/stat.h>\n+#include <sys/types.h>\n+#include <unistd.h>\n+#include <math.h>\n+\n+#include <linux/pps.h>\n+#include <linux/ptp_clock.h>\n+\n+#include \"clockadj.h\"\n+#include \"missing.h\"\n+#include \"phc.h\"\n+#include \"print.h\"\n+#include \"sysoff.h\"\n+#include \"util.h\"\n+#include \"version.h\"\n+\n+#define NSEC2SEC 1000000000.0\n+\n+/* trap the alarm signal so that pause() will wake up on receipt */\n+static void handle_alarm(int s)\n+{\n+\treturn;\n+}\n+\n+static void double_to_timespec(double d, struct timespec *ts)\n+{\n+\tdouble fraction, whole;\n+\n+\tfraction = modf(d, &whole);\n+\n+\t/* cast the whole value to a time_t to store as seconds */\n+\tts->tv_sec = (time_t)whole;\n+\t/* tv_nsec is a long, so we multiply the nanoseconds per second double\n+\t * value by our fractional component. This results in a correct\n+\t * timespec from the double representing seconds.\n+\t */\n+\tts->tv_nsec = (long)(NSEC2SEC * fraction);\n+}\n+\n+static int install_handler(int signum, void(*handler)(int))\n+{\n+\tstruct sigaction action;\n+\tsigset_t mask;\n+\n+\t/* Unblock the signal */\n+\tsigemptyset(&mask);\n+\tsigaddset(&mask, signum);\n+\tsigprocmask(SIG_UNBLOCK, &mask, NULL);\n+\n+\t/* Install the signal handler */\n+\taction.sa_handler = handler;\n+\taction.sa_flags = 0;\n+\tsigemptyset(&action.sa_mask);\n+\tsigaction(signum, &action, NULL);\n+\n+\treturn 0;\n+}\n+\n+static int64_t calculate_offset(struct timespec *ts1,\n+\t\t\t\t      struct timespec *rt,\n+\t\t\t\t      struct timespec *ts2)\n+{\n+\tint64_t interval;\n+\tint64_t offset;\n+\n+#define NSEC_PER_SEC 1000000000ULL\n+\t/* calculate interval between clock realtime */\n+\tinterval = (ts2->tv_sec - ts1->tv_sec) * NSEC_PER_SEC;\n+\tinterval += ts2->tv_nsec - ts1->tv_nsec;\n+\n+\t/* assume PHC read occured half way between CLOCK_REALTIME reads */\n+\n+\toffset = (rt->tv_sec - ts1->tv_sec) * NSEC_PER_SEC;\n+\toffset += (rt->tv_nsec - ts1->tv_nsec) - (interval / 2);\n+\n+\treturn offset;\n+}\n+\n+static clockid_t clock_open(char *device)\n+{\n+\tstruct sk_ts_info ts_info;\n+\tchar phc_device[16];\n+\tint clkid;\n+\n+\t/* check if device is CLOCK_REALTIME */\n+\tif (!strcasecmp(device, \"CLOCK_REALTIME\"))\n+\t\treturn CLOCK_REALTIME;\n+\n+\t/* check if device is valid phc device */\n+\tclkid = phc_open(device);\n+\tif (clkid != CLOCK_INVALID)\n+\t\treturn clkid;\n+\n+\t/* check if device is a valid ethernet device */\n+\tif (sk_get_ts_info(device, &ts_info) || !ts_info.valid) {\n+\t\tpr_err(\"unknown clock %s: %m\", device);\n+\t\treturn CLOCK_INVALID;\n+\t}\n+\n+\tif (ts_info.phc_index < 0) {\n+\t\tpr_err(\"interface %s does not have a PHC\", device);\n+\t\treturn CLOCK_INVALID;\n+\t}\n+\n+\tsprintf(phc_device, \"/dev/ptp%d\", ts_info.phc_index);\n+\tclkid = phc_open(phc_device);\n+\tif (clkid == CLOCK_INVALID)\n+\t\tpr_err(\"cannot open %s for %s: %m\", phc_device, device);\n+\treturn clkid;\n+}\n+\n+static void usage(const char *progname)\n+{\n+\tfprintf(stderr,\n+\t\t\"\\n\"\n+\t\t\"usage: %s [options] <device> -- [command]\\n\\n\"\n+\t\t\" device         ethernet or ptp clock device\"\n+\t\t\"\\n\"\n+\t\t\" options\\n\"\n+\t\t\" -l [num]       set the logging level to 'num'\\n\"\n+\t\t\" -q             do not print messages to the syslog\\n\"\n+\t\t\" -Q             do not print messages to stdout\\n\"\n+\t\t\" -v             prints the software version and exits\\n\"\n+\t\t\" -h             prints this message and exits\\n\"\n+\t\t\"\\n\"\n+\t\t\" commands\\n\"\n+\t\t\" specify commands with arguments. Can specify multiple\\n\"\n+\t\t\" commands to be executed in order. Seconds are read as\\n\"\n+\t\t\" double precision floating point values.\\n\"\n+\t\t\"  set  [seconds]  set PHC time (defaults to time on CLOCK_REALTIME)\\n\"\n+\t\t\"  get             get PHC time\\n\"\n+\t\t\"  adj  <seconds>  adjust PHC time by offset\\n\"\n+\t\t\"  freq [ppb]      adjust PHC frequency (default returns current offset)\\n\"\n+\t\t\"  cmp             compare PHC offset to CLOCK_REALTIME\\n\"\n+\t\t\"  caps            display device capabilities (default if no command given)\\n\"\n+\t\t\"  wait <seconds>  pause between commands\\n\"\n+\t\t\"\\n\",\n+\t\tprogname);\n+}\n+\n+typedef int (*cmd_func_t)(clockid_t, int, char *[]);\n+struct cmd_t {\n+\tconst char *name;\n+\tconst cmd_func_t function;\n+};\n+\n+static cmd_func_t get_command_function(const char *name);\n+static inline int name_is_a_command(const char *name);\n+\n+static int do_set(clockid_t clkid, int cmdc, char *cmdv[])\n+{\n+\tstruct timespec ts;\n+\tdouble time_arg = 0;\n+\tint args_to_eat = 0;\n+\n+\tenum parser_result r;\n+\n+\tmemset(&ts, 0, sizeof(ts));\n+\n+\t/* if we have no more arguments, or the next argument is the \";\"\n+\t * separator, then we run set as default parameter mode */\n+\tif (cmdc < 1 || name_is_a_command(cmdv[0])) {\n+\t\tclock_gettime(CLOCK_REALTIME, &ts);\n+\n+\t\t/* since we aren't using the options, we can simply ensure\n+\t\t * that we don't eat any arguments\n+\t\t */\n+\t\targs_to_eat = 0;\n+\t} else {\n+\t\t/* parse the double */\n+\t\tr = get_ranged_double(cmdv[0], &time_arg, 0.0, DBL_MAX);\n+\t\tswitch (r) {\n+\t\tcase PARSED_OK:\n+\t\t\tbreak;\n+\t\tcase MALFORMED:\n+\t\t\tpr_err(\"set: '%s' is not a valid double\", cmdv[0]);\n+\t\t\treturn -2;\n+\t\tcase OUT_OF_RANGE:\n+\t\t\tpr_err(\"set: '%s' is out of range\", cmdv[0]);\n+\t\t\treturn -2;\n+\t\tdefault:\n+\t\t\tpr_err(\"set: couldn't process '%s'\", cmdv[0]);\n+\t\t\treturn -2;\n+\t\t}\n+\n+\t\tdouble_to_timespec(time_arg, &ts);\n+\n+\t\t/* in order for processing to work, we need to ensure the\n+\t\t * run_cmds loop eats the optional set argument\n+\t\t */\n+\t\targs_to_eat = 1;\n+\t}\n+\n+\tif (clock_settime(clkid, &ts)) {\n+\t\tpr_err(\"set: failed to set clock time: %s\",\n+\t\t\tstrerror(errno));\n+\t\treturn -1;\n+\t} else {\n+\t\tpr_notice(\"set clock time to %ld.%09ld or %s\",\n+\t\t\tts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));\n+\t}\n+\n+\treturn args_to_eat;\n+}\n+\n+static int do_get(clockid_t clkid, int cmdc, char *cmdv[])\n+{\n+\tstruct timespec ts;\n+\n+\tmemset(&ts, 0, sizeof(ts));\n+\tif (clock_gettime(clkid, &ts)) {\n+\t\tpr_err(\"get: failed to get clock time: %s\",\n+\t\t\tstrerror(errno));\n+\n+\t\treturn -1;\n+\t} else {\n+\t\tpr_notice(\"clock time is %ld.%09lu or %s\",\n+\t\t\tts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));\n+\t}\n+\n+\t/* get operation does not require any arguments */\n+\treturn 0;\n+}\n+\n+static int do_adj(clockid_t clkid, int cmdc, char *cmdv[])\n+{\n+\tdouble time_arg;\n+\tint64_t nsecs;\n+\tenum parser_result r;\n+\n+\tif (cmdc < 1 || name_is_a_command(cmdv[0])) {\n+\t\tpr_err(\"adj: missing required time argument\");\n+\t\treturn -2;\n+\t}\n+\n+\t/* parse the double time offset argument */\n+\tr = get_ranged_double(cmdv[0], &time_arg, DBL_MIN, DBL_MAX);\n+\tswitch (r) {\n+\tcase PARSED_OK:\n+\t\tbreak;\n+\tcase MALFORMED:\n+\t\tpr_err(\"adj: '%s' is not a valid double\", cmdv[0]);\n+\t\treturn -2;\n+\tcase OUT_OF_RANGE:\n+\t\tpr_err(\"adj: '%s' is out of range.\", cmdv[0]);\n+\t\treturn -2;\n+\tdefault:\n+\t\tpr_err(\"adj: couldn't process '%s'\", cmdv[0]);\n+\t\treturn -2;\n+\t}\n+\n+\tnsecs = (int64_t)(NSEC2SEC * time_arg);\n+\n+\tclockadj_init(clkid);\n+\tclockadj_step(clkid, nsecs);\n+\n+\tpr_notice(\"adjusted clock by %lf seconds\", time_arg);\n+\n+\t/* adjustment always consumes one argument */\n+\treturn 1;\n+}\n+\n+static int do_freq(clockid_t clkid, int cmdc, char *cmdv[])\n+{\n+\tdouble ppb;\n+\tenum parser_result r;\n+\n+\tclockadj_init(clkid);\n+\n+\tif (cmdc < 1 || name_is_a_command(cmdv[0])) {\n+\t\tppb = clockadj_get_freq(clkid);\n+\t\tpr_err(\"clock frequency offset is %lfppb\", ppb);\n+\n+\t\t/* no argument was used */\n+\t\treturn 0;\n+\t}\n+\n+\t/* parse the double ppb argument */\n+\tr = get_ranged_double(cmdv[0], &ppb, -NSEC2SEC, NSEC2SEC);\n+\tswitch (r) {\n+\tcase PARSED_OK:\n+\t\tbreak;\n+\tcase MALFORMED:\n+\t\tpr_err(\"freq: '%s' is not a valid double\", cmdv[0]);\n+\t\treturn -2;\n+\tcase OUT_OF_RANGE:\n+\t\tpr_err(\"freq: '%s' is out of range.\", cmdv[0]);\n+\t\treturn -2;\n+\tdefault:\n+\t\tpr_err(\"freq: couldn't process '%s'\", cmdv[0]);\n+\t\treturn -2;\n+\t}\n+\n+\tclockadj_set_freq(clkid, ppb);\n+\tpr_err(\"adjusted clock frequency offset to %lfppb\", ppb);\n+\n+\t/* consumed one argument to determine the frequency adjustment value */\n+\treturn 1;\n+}\n+\n+static int do_caps(clockid_t clkid, int cmdc, char *cmdv[])\n+{\n+\tstruct ptp_clock_caps caps;\n+\n+\tif (clkid == CLOCK_REALTIME) {\n+\t\tpr_warning(\"CLOCK_REALTIME is not a PHC device.\");\n+\t\treturn 0;\n+\t}\n+\n+\tif (ioctl(CLOCKID_TO_FD(clkid), PTP_CLOCK_GETCAPS, &caps)) {\n+\t\tpr_err(\"get capabilities failed: %s\",\n+\t\t\tstrerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\tpr_notice(\"\\n\"\n+\t\t\"capabilities:\\n\"\n+\t\t\"  %d maximum frequency adjustment (ppb)\\n\"\n+\t\t\"  %d programable alarms\\n\"\n+\t\t\"  %d external time stamp channels\\n\"\n+\t\t\"  %d programmable periodic signals\\n\"\n+\t\t\"  %s pulse per second support\",\n+\t\tcaps.max_adj,\n+\t\tcaps.n_alarm,\n+\t\tcaps.n_ext_ts,\n+\t\tcaps.n_per_out,\n+\t\tcaps.pps ? \"has\" : \"doesn't have\");\n+\treturn 0;\n+}\n+\n+static int do_cmp(clockid_t clkid, int cmdc, char *cmdv[])\n+{\n+\tstruct timespec ts, rta, rtb;\n+\tint64_t sys_offset, delay = 0, offset;\n+\tuint64_t sys_ts;\n+\n+\tif (SYSOFF_SUPPORTED ==\n+\t    sysoff_measure(CLOCKID_TO_FD(clkid),\n+\t\t\t   9, &sys_offset, &sys_ts, &delay)) {\n+\t\tpr_notice( \"offset from CLOCK_REALTIME is %\"PRId64\"ns\\n\",\n+\t\t\tsys_offset);\n+\t\treturn 0;\n+\t}\n+\n+\tmemset(&ts, 0, sizeof(ts));\n+\tmemset(&ts, 0, sizeof(rta));\n+\tmemset(&ts, 0, sizeof(rtb));\n+\tif (clock_gettime(CLOCK_REALTIME, &rta) ||\n+\t    clock_gettime(clkid, &ts) ||\n+\t    clock_gettime(CLOCK_REALTIME, &rtb)) {\n+\t\tpr_err(\"cmp: failed clock reads: %s\\n\",\n+\t\t\tstrerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\toffset = calculate_offset(&rta, &ts, &rtb);\n+\tpr_notice( \"offset from CLOCK_REALTIME is approximately %\"PRId64\"ns\\n\",\n+\t\toffset);\n+\n+\treturn 0;\n+}\n+\n+static int do_wait(clockid_t clkid, int cmdc, char *cmdv[])\n+{\n+\tdouble time_arg;\n+\tstruct timespec ts;\n+\tstruct itimerval timer;\n+\tenum parser_result r;\n+\n+\tif (cmdc < 1 || name_is_a_command(cmdv[0])) {\n+\t\tpr_err(\"wait: requires sleep duration argument\\n\");\n+\t\treturn -2;\n+\t}\n+\n+\tmemset(&timer, 0, sizeof(timer));\n+\n+\t/* parse the double time offset argument */\n+\tr = get_ranged_double(cmdv[0], &time_arg, 0.0, DBL_MAX);\n+\tswitch (r) {\n+\tcase PARSED_OK:\n+\t\tbreak;\n+\tcase MALFORMED:\n+\t\tpr_err(\"wait: '%s' is not a valid double\", cmdv[0]);\n+\t\treturn -2;\n+\tcase OUT_OF_RANGE:\n+\t\tpr_err(\"wait: '%s' is out of range.\", cmdv[0]);\n+\t\treturn -2;\n+\tdefault:\n+\t\tpr_err(\"wait: couldn't process '%s'\", cmdv[0]);\n+\t\treturn -2;\n+\t}\n+\n+\tdouble_to_timespec(time_arg, &ts);\n+\ttimer.it_value.tv_sec = ts.tv_sec;\n+\ttimer.it_value.tv_usec = ts.tv_nsec / 1000;\n+\tsetitimer(ITIMER_REAL, &timer, NULL);\n+\tpause();\n+\n+\t/* the SIGALRM is already trapped during initialization, so we will\n+\t * wake up here once the alarm is handled.\n+\t */\n+\tpr_notice( \"process slept for %lf seconds\\n\", time_arg);\n+\n+\treturn 1;\n+}\n+\n+static const struct cmd_t all_commands[] = {\n+\t{ \"set\", &do_set },\n+\t{ \"get\", &do_get },\n+\t{ \"adj\", &do_adj },\n+\t{ \"freq\", &do_freq },\n+\t{ \"cmp\", &do_cmp },\n+\t{ \"caps\", &do_caps },\n+\t{ \"wait\", &do_wait },\n+\t{ 0, 0 }\n+};\n+\n+static cmd_func_t get_command_function(const char *name)\n+{\n+\tint i;\n+\tcmd_func_t cmd = NULL;\n+\n+\tfor (i = 0; all_commands[i].name != NULL; i++) {\n+\t\tif (!strncmp(name,\n+\t\t\t     all_commands[i].name,\n+\t\t\t     strlen(all_commands[i].name)))\n+\t\t\tcmd = all_commands[i].function;\n+\t}\n+\n+\treturn cmd;\n+}\n+\n+static inline int name_is_a_command(const char *name)\n+{\n+\treturn get_command_function(name) != NULL;\n+}\n+\n+static int run_cmds(clockid_t clkid, int cmdc, char *cmdv[])\n+{\n+\tint i = 0, result = 0;\n+\tcmd_func_t action = NULL;\n+\n+\twhile (i < cmdc) {\n+\t\tchar *arg = cmdv[i];\n+\n+\t\t/* increment now to remove the command argument */\n+\t\ti++;\n+\n+\t\taction = get_command_function(arg);\n+\t\tif (action)\n+\t\t\tresult = action(clkid, cmdc - i, &cmdv[i]);\n+\t\telse\n+\t\t\tpr_err(\"unknown command %s.\", arg);\n+\n+\t\t/* result is how many arguments were used up by the command,\n+\t\t * not including the \";\". We will increment the loop counter\n+\t\t * to avoid processing the arguments as commands.\n+\t\t */\n+\t\tif (result < 0)\n+\t\t\treturn result;\n+\t\telse\n+\t\t\ti += result;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int main(int argc, char *argv[])\n+{\n+\tconst char *progname;\n+\tint c, result, print_level = LOG_INFO, verbose = 1, use_syslog = 1;\n+\tclockid_t clkid;\n+\n+\tinstall_handler(SIGALRM, handle_alarm);\n+\n+\t/* Process the command line arguments. */\n+\tprogname = strrchr(argv[0], '/');\n+\tprogname = progname ? 1+progname : argv[0];\n+\twhile (EOF != (c = getopt(argc, argv,\n+\t\t\t\t  \"l:qQvh\"))) {\n+\t\tswitch (c) {\n+\t\tcase 'l':\n+\t\t\tif (get_arg_val_i(c, optarg, &print_level,\n+\t\t\t\t\t  PRINT_LEVEL_MIN, PRINT_LEVEL_MAX))\n+\t\t\t\treturn -1;\n+\t\t\tbreak;\n+\t\tcase 'q':\n+\t\t\tuse_syslog = 0;\n+\t\t\tbreak;\n+\t\tcase 'Q':\n+\t\t\tverbose = 0;\n+\t\t\tbreak;\n+\t\tcase 'v':\n+\t\t\tversion_show(stdout);\n+\t\t\treturn 0;\n+\t\tcase 'h':\n+\t\t\tusage(progname);\n+\t\t\treturn 0;\n+\t\tdefault:\n+\t\t\tusage(progname);\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\n+\tprint_set_progname(progname);\n+\tprint_set_verbose(verbose);\n+\tprint_set_syslog(use_syslog);\n+\tprint_set_level(print_level);\n+\n+\tif ((argc - optind) < 1) {\n+\t\tusage(progname);\n+\t\treturn -1;\n+\t}\n+\n+\tclkid = clock_open(argv[optind]);\n+\tif (clkid == CLOCK_INVALID)\n+\t\treturn -1;\n+\n+\t/* pass the remaining arguments to the run_cmds loop */\n+\tresult = run_cmds(clkid, argc - optind - 1, &argv[optind+1]);\n+\tif (result < -1) {\n+\t\t/* show usage when command fails */\n+\t\tusage(progname);\n+\t\treturn result;\n+\t}\n+\n+\treturn 0;\n+}\n-- \n2.0.1.475.g9b8d714\n"},{"id":"245736","messageId":"xmqqr41s6bsg.fsf@gitster.dls.corp.google.com","threadId":"37101","inReplyTo":"1405032485-26117-2-git-send-email-jacob.e.keller@intel.com","subject":"Re: [PATCH v4] linuxptp: add phc_ctl program to help debug PHC devices","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-07-10T23:20:47Z","receivedAt":"2014-07-10T23:20:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.e.keller@intel.com> writes:\n\n> This is an updated version of a script I wrote a couple years ago for\n\nI suspect that this is not for us ;-)\n"},{"id":"245737","messageId":"1405035555.4925.46.camel@jekeller-desk1.amr.corp.intel.com","threadId":"37101","inReplyTo":"xmqqr41s6bsg.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v4] linuxptp: add phc_ctl program to help debug PHC devices","fromName":"Keller, Jacob E","fromEmail":"jacob.e.keller@intel.com","sentAt":"2014-07-10T23:39:15Z","receivedAt":"2014-07-10T23:39:15Z","isPatch":true,"sender":{"key":"jacob.e.keller@intel.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Thu, 2014-07-10 at 16:20 -0700, Junio C Hamano wrote:\n> Jacob Keller <jacob.e.keller@intel.com> writes:\n> \n> > This is an updated version of a script I wrote a couple years ago for\n> \n> I suspect that this is not for us ;-)\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n\n\nIndeed. This was intended for the LinuxPTP project, and I accidentally\nsent here.\n\nPlease just ignore these.\n\nThanks,\nJake\n"}]}