summaryrefslogtreecommitdiff
path: root/mbgctrl
diff options
context:
space:
mode:
authorMartin Burnicki <martin.burnicki@meinberg.de>2017-08-11 12:00:00 +0200
committerMartin Burnicki <martin.burnicki@meinberg.de>2017-08-11 12:00:00 +0200
commit3743722bf58ea2819f6c81974d75baa295562572 (patch)
tree06e9a3e9ffedc4157f10758a62e12ad61704146c /mbgctrl
parentbd8c46abdb579b8c7871b9ff22a2555c2211e110 (diff)
downloadmbgtools-nbsd-3743722bf58ea2819f6c81974d75baa295562572.tar.gz
mbgtools-nbsd-3743722bf58ea2819f6c81974d75baa295562572.zip
Changed directory layout to common structure
Also update Makefiles accordingly. Remove obsolete files.
Diffstat (limited to 'mbgctrl')
-rwxr-xr-xmbgctrl/Makefile50
-rwxr-xr-xmbgctrl/mbgctrl.c3440
2 files changed, 3490 insertions, 0 deletions
diff --git a/mbgctrl/Makefile b/mbgctrl/Makefile
new file mode 100755
index 0000000..367960d
--- /dev/null
+++ b/mbgctrl/Makefile
@@ -0,0 +1,50 @@
+
+#########################################################################
+#
+# $Id: Makefile 1.8 2017/07/05 18:51:21 martin REL_M $
+#
+# Description:
+# Makefile for mbgctrl.
+#
+# -----------------------------------------------------------------------
+# $Log: Makefile $
+# Revision 1.8 2017/07/05 18:51:21 martin
+# Updated list of object files and use top level
+# Makefile properly.
+# Revision 1.7 2009/07/24 10:31:16 martin
+# Moved declarations to a common file which is now included.
+# Revision 1.6 2008/12/22 11:54:31 martin
+# Changed enumeration of source files.
+# Revision 1.5 2007/03/02 11:45:09 martin
+# Parameter "DEBUG=1" lets the target be built with debug enabled.
+# Revision 1.4 2003/04/25 10:21:05 martin
+# Updated source module list.
+# Revision 1.3 2002/11/21 14:56:31 martin
+# New targets install and uninstall.
+# Revision 1.2 2001/11/30 10:21:54 martin
+# Added pcpsutil.o to the list of source files.
+# Revision 1.1 2001/09/17 15:07:55 martin
+#
+#########################################################################
+
+TARGET = mbgctrl
+INST_TO_SBIN = 1
+
+# By default these tools only need a simple set of mbgdevio API
+# functions, but here we use all API calls.
+MBGDEVIO_SIMPLE = 0
+
+OBJS = $(TARGET).o
+OBJS += mbgdevio.o
+OBJS += mbgutil.o
+OBJS += timeutil.o
+OBJS += str_util.o
+OBJS += toolutil.o
+OBJS += mbgerror.o
+OBJS += cfg_hlp.o
+OBJS += deviohlp.o
+OBJS += gpsutils.o
+OBJS += lan_util.o
+
+BASEDIR := ..
+include $(BASEDIR)/Makefile
diff --git a/mbgctrl/mbgctrl.c b/mbgctrl/mbgctrl.c
new file mode 100755
index 0000000..ea9e250
--- /dev/null
+++ b/mbgctrl/mbgctrl.c
@@ -0,0 +1,3440 @@
+
+/**************************************************************************
+ *
+ * $Id: mbgctrl.c 1.23 2017/07/05 19:10:07 martin REL_M $
+ *
+ * Description:
+ * Main file for mbgctrl program which sends commands and
+ * configuration parameters to a Meinberg device via IOCTL calls.
+ *
+ * -----------------------------------------------------------------------
+ * $Log: mbgctrl.c $
+ * Revision 1.23 2017/07/05 19:10:07 martin
+ * New way to maintain version information.
+ * Support build under Windows.
+ * Bugfix: accept parameter keyword only when substring is found at the
+ * start of the parameter string.
+ * Support configuring PTP parameters incl. unicast
+ * Warn if trying to handle serial port cfg but no serial port is supported.
+ * Basic support for programmable pulse outputs, including pulse shift feature.
+ * Yet incomplete support for synthesizer output.
+ * Use more functions from common library modules.
+ * Use codes and inline functions from mbgerror.h.
+ * Proper return codes and exit codes.
+ * Revision 1.22 2009/09/29 14:58:18 martin
+ * Unified and simplified parameter evaluation.
+ * Updated version number to 3.4.0.
+ * Revision 1.21 2009/09/28 09:36:43 martin
+ * Support configuration of antenna cable length.
+ * Removed duplicate usage message for event time.
+ * Revision 1.20 2009/09/28 07:19:03 martin
+ * Made parameter syntax for enable_flags and LAN configuration more flexible.
+ * Revision 1.19 2009/08/20 14:19:29 martin
+ * Updated version number to 3.3.1.
+ * Support configuration of board's LAN interface.
+ * Revision 1.18 2009/07/24 09:50:08 martin
+ * Updated version number to 3.3.0.
+ * Revision 1.17 2009/06/19 12:38:51 martin
+ * Updated version number to 3.2.0.
+ * Revision 1.16 2009/06/18 15:14:53 martin
+ * Added command TZOFFS which can be used to set TZDL to UTC, and then
+ * configure the standard TZDL offset to a dedicated number of seconds, to the
+ * output time has a configurable UTC offset against the time derived from the
+ * input signal.
+ * Revision 1.15 2009/03/20 11:53:19 martin
+ * Updated version number to 3.1.0.
+ * Updated copyright year to include 2009.
+ * Support programmable time scales.
+ * Revision 1.14 2008/12/22 12:39:00 martin
+ * Updated description, copyright, revision number and string.
+ * Use unified functions from toolutil module.
+ * Accept device name(s) on the command line.
+ * Don't use printf() without format, which migth produce warnings
+ * with newer gcc versions.
+ * Revision 1.13 2008/11/07 10:25:49 martin
+ * Support modification of a card's ENABLE_FLAGS.
+ * Changes due to renamed library function.
+ * Changes due to renamed library function.
+ * Revision 1.12 2008/09/15 14:20:21 martin
+ * Support generic serial settings including string type and mode.
+ * Reworked evaluation of command line parameters.
+ * New parameter COM0= to configure an individual port.
+ * Account for renamed library symbols.
+ * Revision 1.11 2007/07/24 09:31:39 martin
+ * Changes due to renamed library symbols.
+ * Revision 1.10 2007/03/01 16:09:16 martin
+ * Be able to set the board date and/or time.
+ * Revision 1.9 2007/02/22 16:38:50 martin
+ * Added function to set on-board date.
+ * Revision 1.8 2006/08/28 10:45:50 martin
+ * Picked up Heiko's patch to set the GPS receiver position.
+ * Revision 1.7 2004/11/08 15:47:40 martin
+ * Using type cast to avoid compiler warning.
+ * Revision 1.6 2003/08/26 14:37:35 martin
+ * Support configuration of some standard time zones,
+ * also for GPS devices.
+ * Revision 1.5 2003/07/31 13:48:54 martin
+ * Added function to set COM port parms for GPS clocks.
+ * Usage shows which parameters are supported by a device.
+ * Revision 1.4 2003/04/25 10:27:58 martin
+ * Use new functions from mbgdevio library.
+ * New program version v2.1.
+ * Revision 1.3 2002/08/22 14:52:49 martin
+ * Added function to set clock's COM port parameters.
+ * Revision 1.2 2001/12/03 16:04:46 martin
+ * New program version 1.2.
+ * Added new function set_event_time().
+ * Fixed a bug in eval of cmd line parameters.
+ * Revision 1.1 2001/09/17 15:07:56 martin
+ *
+ **************************************************************************/
+
+// include Meinberg headers
+#include <mbgdevio.h>
+#include <deviohlp.h>
+#include <pcpsmktm.h>
+#include <pcpsutil.h>
+#include <cfg_hlp.h>
+#include <myutil.h>
+#include <gpsutils.h>
+#include <cnv_wday.h>
+#include <toolutil.h>
+#include <ptp_util.h>
+#include <lan_util.h>
+#include <str_util.h>
+
+// include system headers
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+
+
+#define MBG_MICRO_VERSION 0
+#define MBG_FIRST_COPYRIGHT_YEAR 2001
+#define MBG_LAST_COPYRIGHT_YEAR 0 // use default
+
+#define RC_USAGE 1
+
+static const char *pname = "mbgctrl";
+
+static char *dev_name;
+
+static int err_unicast_nsupp;
+
+static const char str_spc_not[] = " not";
+static const char str_spc_not_supp[] = " (not supported)";
+static const char str_spc_wildcard[] = " (wildcard)";
+
+static TZDL tzdl_utc = DEFAULT_TZDL_UTC;
+static TZDL tzdl_cet_cest = DEFAULT_TZDL_CET_CEST_EN;
+static TZDL tzdl_eet_eest = DEFAULT_TZDL_EET_EEST_EN;
+static long max_tzdl_offs = 0x7FFFFFFFL; // max val for int32_t
+
+static const char tz_info_utc[] = TZ_INFO_UTC;
+static const char tz_info_cet_cest[] = TZ_INFO_CET_CEST_EN;
+static const char tz_info_eet_eest[] = TZ_INFO_EET_EEST_EN;
+
+static const char *mode_names[N_STR_MODE] = DEFAULT_ENG_MODE_NAMES;
+
+static const char *time_scale_names[N_MBG_TIME_SCALE] = MBG_TIME_SCALE_STRS;
+
+#define _get_time_scale_name( _i ) \
+ ( ( (_i) < N_MBG_TIME_SCALE ) ? time_scale_names[_i] : str_unknown )
+
+static const char * const pout_mode_names_eng[N_POUT_MODES] = DEFAULT_ENG_POUT_NAMES;
+
+#define _get_pout_mode_name( _i ) \
+ ( ( (_i) < N_POUT_MODES ) ? pout_mode_names_eng[_i] : str_unknown )
+
+
+static const char no_gps_cmd[] = "does not support GPS commands";
+static const char no_tzdl[] = "does not support configurable time zone";
+static const char no_tz[] = "does not support time zones";
+static const char no_synth[] = "has no synthesizer output";
+static const char no_event_time[] = "does not support event times";
+static const char no_enable_flags[] = "does not support enable flags";
+static const char no_time_scale[] = "does not support a configurable time scale";
+static const char no_lan_intf[] = "has no LAN interface";
+static const char no_ptp[] = "does not provide PTP";
+static const char no_cab_len[] = "does not support antenna signal delay compensation";
+
+
+
+/**
+ * @brief A type used to pass print control flags to functions
+ *
+ * @see ::CTRL_FLAG_MASKS
+ */
+typedef int CTRL_FLAGS;
+
+
+/**
+ * @brief flag masks used with ::CTRL_FLAGS
+ *
+ * @see ::CTRL_FLAGS
+ */
+enum CTRL_FLAG_MASKS
+{
+ CTRL_PRINT_ALL = 0x01,
+ CTRL_PRINT_NEWLINES = 0x02,
+ CTRL_PRINT_IDX = 0x04,
+ CTRL_PRINT_ERR = 0x08,
+ CTRL_PRINT_PLUS = 0x10,
+ CTRL_NOT_SUPP = 0x20
+};
+
+
+struct OPT_HANDLER_SPEC_S;
+
+typedef int HELP_FNC( MBG_DEV_HANDLE, const PCPS_DEV *, const struct OPT_HANDLER_SPEC_S *, CTRL_FLAGS );
+typedef int SET_FNC( MBG_DEV_HANDLE, const char *, int );
+typedef int SHOW_FNC( MBG_DEV_HANDLE, const struct OPT_HANDLER_SPEC_S *, const PCPS_DEV *, const char * );
+
+typedef struct OPT_HANDLER_SPEC_S
+{
+ const char *cmd_name;
+ MBG_CHK_SUPP_FNC *chk_supp_fnc;
+ HELP_FNC *help_fnc;
+ SET_FNC *set_fnc;
+ SHOW_FNC *show_fnc;
+ const char *cmd_info;
+ const char *not_supp_msg;
+ uint32_t flags; ///< see ::OPT_FLAG_MASKS
+
+} OPT_HANDLER_SPEC;
+
+
+enum OPT_FLAG_BITS
+{
+ OPT_SUPP_CMD_IDX_BIT, ///< e.g. COM0=, COM1=, etc. vs. TZ=
+ N_OPT_FLAG_BITS
+};
+
+enum OPT_FLAG_MASKS
+{
+ OPT_SUPP_CMD_IDX = ( 1UL << OPT_SUPP_CMD_IDX_BIT ) ///< see ::OPT_SUPP_CMD_IDX_BIT
+};
+
+OPT_HANDLER_SPEC ohs_pout =
+{
+ "POUT", // cmd_name
+ NULL, // chk_supp_fnc
+ NULL, // help_fnc
+ NULL, // set_fnc
+ NULL, // show_fnc
+ NULL, // cmd_info
+ NULL, // not_supp_msg
+ OPT_SUPP_CMD_IDX // flags
+};
+
+
+
+typedef struct
+{
+ int indent_1;
+ int indent_2;
+ int indent_3;
+ int comm_col_x;
+} INDENTS;
+
+const INDENTS usage_indents = { 2, 4, 6, 30 };
+const INDENTS usage_indents_detailed = { 4, 6, 8, 30 };
+const INDENTS show_indents = { 2, 4, 0, 0 };
+
+
+#define SHOW_INDENT_1 " "
+#define SHOW_INDENT_2 " "
+
+
+
+typedef struct
+{
+ const char *name;
+ uint16_t *flags;
+ uint16_t on_flags;
+} EF_INFO;
+
+#define N_EF_INFO 3
+
+static const char ef_name_serial[] = "SERIAL";
+static const char ef_name_pulses[] = "PULSES";
+static const char ef_name_synth[] = "SYNTH";
+
+
+typedef struct
+{
+ const char *name;
+ IP4_ADDR *addr;
+} IP4_INFO;
+
+#define N_IP4_INFO 4
+
+static const char ip4_name_ip[] = "IP";
+static const char ip4_name_nm[] = "NM";
+static const char ip4_name_ba[] = "BA";
+static const char ip4_name_gw[] = "GW";
+
+static const char ptp_name_net[] = "NP";
+static const char ptp_name_del[] = "DM";
+static const char ptp_name_dom[] = "DO";
+static const char ptp_name_v1[] = "HW";
+
+static const char ptp_name_role[] = "ROLE";
+
+static const char ptp_name_gmip[] = "GMIP";
+static const char ptp_name_gmid[] = "GMID";
+static const char ptp_name_pid[] = "PID";
+static const char ptp_name_smi[] = "SMI";
+static const char ptp_name_ami[] = "AMI";
+static const char ptp_name_dri[] = "DRI";
+static const char ptp_name_dur[] = "DUR";
+
+//##+++++
+static const char *delay_mech[] = PTP_DELAY_MECH_NAMES;
+static const char *nw_prot[] = PTP_NW_PROT_STRS;
+static const char *nw_prot_short[] = PTP_NW_PROT_STRS_SHORT;
+
+static const char *ptp_roles[] = PTP_ROLE_STRS;
+static const char *ptp_roles_short[] = PTP_ROLE_STRS_SHORT;
+
+static const PTP_CLOCK_ID clock_id_wildcard = PTP_CLOCK_ID_WILDCARD;
+
+//##+++++++++++++++++++
+// If unicast is not supported for a PTP device then the device is definitely
+// a multicast slave, in which case index 0 returns the correct role name.
+#define _ptp_role_name( _i ) \
+ ( ( (_i) < N_PTP_ROLES ) ? ptp_roles[_i] : str_unknown )
+
+#define _ptp_role_name_short( _i ) \
+ ( ( (_i) < N_PTP_ROLES ) ? ptp_roles_short[_i] : str_unknown )
+
+
+static const char pout_name_mode[] = "MODE";
+static const char pout_name_len[] = "LEN";
+static const char pout_name_inv[] = "INV";
+static const char pout_name_ois[] = "OIS";
+static const char pout_name_shift[] = "SHIFT";
+
+
+
+
+
+static /*HDR*/
+__attribute__( ( format( printf, 4, 5 ) ) )
+int usage_line( const INDENTS *p_ind, const OPT_HANDLER_SPEC *p_opt,
+ const char *cmd_parm, const char *cmd_comment_fmt, ... )
+{
+ int n = 0;
+
+ // print left margin, if not 0
+ if ( p_ind->indent_1 )
+ n += printf( "%*s", p_ind->indent_1, str_empty );
+
+ // print command name
+ if ( p_opt )
+ n += printf( "%s", p_opt->cmd_name );
+
+ // print the command parameters, if specified
+ if ( cmd_parm )
+ {
+ if ( p_opt && ( p_opt->flags & OPT_SUPP_CMD_IDX ) )
+ n+= printf( "%s", "<n>" );
+
+ n += printf( "=%s", cmd_parm );
+ }
+
+ // print command comment which can be a format string
+ // expecting additional parameters
+ if ( cmd_comment_fmt )
+ {
+ va_list arg_list;
+
+ // indent the comment string
+ if ( p_ind->indent_2 )
+ {
+ int w = p_ind->indent_2 - n;
+
+ while ( w < 0 )
+ w += 8;
+
+ n += printf( "%*s", w, str_empty );
+ }
+
+ va_start( arg_list, cmd_comment_fmt );
+ n += vprintf( cmd_comment_fmt, arg_list );
+ va_end( arg_list );
+ }
+
+ n += printf( "\n" );
+
+ return n;
+
+} // usage_line
+
+
+
+static /*HDR*/
+int print_indent( int i )
+{
+ int n = printf( "%*s", i, str_empty );
+
+ return n;
+
+} // print_indent
+
+
+
+static /*HDR*/
+__attribute__( ( format( printf, 2, 3 ) ) )
+int usage_note( int indent, const char *fmt, ... )
+{
+ // print left margin, if not 0
+ int n = print_indent( indent );
+
+ if ( fmt )
+ {
+ va_list arg_list;
+
+ va_start( arg_list, fmt );
+ n += vprintf( fmt, arg_list );
+ n += printf( "\n" );
+ va_end( arg_list );
+ }
+
+ return n;
+
+} // usage_note
+
+
+
+typedef const char *(STR_FNC)( int idx );
+
+
+static /*HDR*/
+void print_bit_mask_list( const char *info_1, const char *info_2, uint32_t supp_mask,
+ int n_known, const char * const names[], STR_FNC *s_fnc,
+ int inst_idx, CTRL_FLAGS ctrl_flags, const INDENTS *p_ind )
+{
+ const char *str_s_fnc = s_fnc ? s_fnc( inst_idx ) : str_empty;
+
+ print_indent( p_ind->indent_2 );
+
+ if ( ctrl_flags & CTRL_PRINT_ALL )
+ {
+ supp_mask = ( 1UL << n_known ) - 1;
+
+ if ( supp_mask )
+ printf( "Known %s%s: ", info_1, str_s_fnc );
+ else
+ printf( "No %s%s known.", info_1, str_s_fnc );
+ }
+ else
+ {
+ if ( supp_mask )
+ {
+ printf( "%s", info_1 );
+
+ if ( info_2 )
+ printf( " %s", info_2 );
+
+ #if defined( DEBUG )
+ printf( " (%04lX)", (ulong) supp_mask );
+ #endif
+
+ printf( ":" );
+ }
+ else
+ {
+ printf( "No %s.", info_1 );
+
+ if ( info_2 )
+ printf( " %s", info_2 );
+
+ printf( "." );
+ }
+ }
+
+
+ if ( supp_mask )
+ {
+ int n_printed = 0;
+ int i;
+ const char *str_sep = ( ctrl_flags & CTRL_PRINT_PLUS ) ? "+" : ", ";
+
+ for ( i = 0; i < n_known; i++ )
+ {
+ const char *cp;
+
+ if ( ( supp_mask & ( 1UL << i ) ) == 0 )
+ continue;
+
+ if ( names )
+ cp = names[i];
+ else
+ if ( s_fnc )
+ cp = s_fnc( i );
+ else
+ cp = str_empty;
+
+ if ( ctrl_flags & ( CTRL_PRINT_NEWLINES | CTRL_PRINT_IDX ) )
+ {
+ printf( "\n" );
+ print_indent( p_ind->indent_3 );
+
+ if ( ctrl_flags & CTRL_PRINT_IDX )
+ printf( "%i: ", i );
+
+ printf( "%s", cp );
+ }
+ else
+ printf( "%s%s", n_printed ? str_sep : str_empty, cp );
+
+ n_printed++;
+ }
+ }
+
+ printf( "\n" );
+
+} // print_bit_mask_list
+
+
+
+static /*HDR*/
+void err_msg( const PCPS_DEV *p_dev, const char *msg )
+{
+ printf( "This device %s.\n", msg );
+
+} // err_msg
+
+
+
+static /*HDR*/
+int set_tz_code( MBG_DEV_HANDLE dh, PCPS_TZCODE tzcode, const char *s )
+{
+ int rc = mbg_set_tzcode( dh, &tzcode );
+
+ if ( mbg_cond_err_msg( rc, "mbg_set_tzcode" ) )
+ return rc;
+
+ printf( "The clock's time zone setting has been set to %s.\n", s );
+
+ return MBG_SUCCESS;
+
+} // set_tz_code
+
+
+
+static /*HDR*/
+int set_gps_tzdl( MBG_DEV_HANDLE dh, const TZDL *tzdl, const char *info )
+{
+ int rc = mbg_set_gps_tzdl( dh, tzdl );
+
+ if ( mbg_cond_err_msg( rc, "set_gps_tzdl" ) )
+ return rc;
+
+ if ( info )
+ printf( "The clock's time zone setting has been set to %s.\n", info );
+
+ return MBG_SUCCESS;
+
+} // set_gps_tzdl
+
+
+
+static /*HDR*/
+int set_timezone( MBG_DEV_HANDLE dh, const char *tz_name, const PCPS_DEV *p_dev )
+{
+ const char *tz_info = NULL;
+ TZDL *tz_tzdl = NULL;
+ PCPS_TZCODE tz_code = 0;
+ int rc = MBG_SUCCESS;
+
+ if ( strcmp( tz_name, "UTC" ) == 0 )
+ {
+ tz_info = tz_info_utc;
+ tz_tzdl = &tzdl_utc;
+ tz_code = PCPS_TZCODE_UTC;
+ }
+ else if ( strcmp( tz_name, "CET" ) == 0 )
+ {
+ tz_info = tz_info_cet_cest;
+ tz_tzdl = &tzdl_cet_cest;
+ tz_code = PCPS_TZCODE_MEZMESZ;
+ }
+ else if ( strcmp( tz_name, "EET" ) == 0 )
+ {
+ tz_info = tz_info_eet_eest;
+ tz_tzdl = &tzdl_eet_eest;
+ tz_code = PCPS_TZCODE_OEZ;
+ }
+ else
+ {
+ printf( "** Unknown timezone name %s\n", tz_name );
+ rc = RC_USAGE;
+ }
+
+ if ( tz_info )
+ {
+ if ( _pcps_has_tzcode( p_dev ) )
+ rc = set_tz_code( dh, tz_code, tz_info );
+ else
+ if ( _pcps_has_tzdl( p_dev ) )
+ rc = set_gps_tzdl( dh, tz_tzdl, tz_info );
+ else
+ err_msg( p_dev, no_tz );
+ }
+
+ return rc;
+
+} // set_timezone
+
+
+
+static /*HDR*/
+int show_tzdl_offs( MBG_DEV_HANDLE dh, const char *info )
+{
+ TZDL tzdl;
+ int rc = mbg_get_gps_tzdl( dh, &tzdl );
+
+ if ( mbg_cond_err_msg( rc, "mbg_get_gps_tzdl" ) )
+ return rc;
+
+ printf( "%s timezone offset: UTC%+lis", info, (long) tzdl.offs );
+
+ return MBG_SUCCESS;
+
+} // show_tzdl_offs
+
+
+
+static /*HDR*/
+int set_tzdl_offs( MBG_DEV_HANDLE dh, const char *s )
+{
+ TZDL tzdl = tzdl_utc;
+ long tzdl_offs = atol( s );
+
+ if ( labs( tzdl_offs ) > max_tzdl_offs ) // max val for int32_t
+ {
+ fprintf( stderr, "** Time zone offset %li exceeds range (%li..%li)\n",
+ tzdl_offs, -max_tzdl_offs, max_tzdl_offs );
+ return MBG_ERR_CFG;
+ }
+
+ tzdl.offs = (int32_t) tzdl_offs;
+
+ return set_gps_tzdl( dh, &tzdl, NULL );
+
+} // set_tzdl_offs
+
+
+
+static /*HDR*/
+int show_synth( MBG_DEV_HANDLE dh, const char *info )
+{
+ SYNTH synth;
+ int rc = mbg_get_synth( dh, &synth );
+
+ if ( mbg_cond_err_msg( rc, "mbg_get_synth" ) )
+ return rc;
+
+ printf( "%s synthesizer settings: freq %i.%iE%i Hz, phase %+i.%i deg", info,
+ synth.freq / 10, synth.freq % 10, synth.range,
+ synth.phase / 10, abs( synth.phase ) % 10 );
+
+/*
+ int16_t freq; ///< four digits used; scale: 0.1 Hz; e.g. 1234 -> 123.4 Hz
+ int16_t range; ///< scale factor for freq; 0..::MAX_SYNTH_RANGE
+ int16_t phase; ///< -::MAX_SYNTH_PHASE..+::MAX_SYNTH_PHASE; >0 -> pulses later
+*/
+ return MBG_SUCCESS;
+
+} // show_synth
+
+
+
+static /*HDR*/
+int set_synth( MBG_DEV_HANDLE dh, const char *s )
+{
+ SYNTH synth = { 0 };
+ char *cp;
+
+ long freq = strtol( s, &cp, 10 ) * 10;
+
+ if ( *cp == '.' )
+ {
+ long frac = strtol( ++cp, NULL, 10 );
+ if ( frac < 0 || frac > 9 )
+ goto fail;
+
+ freq += frac;
+ }
+
+ synth.freq = (int16_t) freq;
+ synth.range = 0;
+ synth.phase = 0;
+
+ return mbg_set_synth( dh, &synth );
+
+fail:
+ return MBG_ERR_INV_PARM;
+
+} // set_synth
+
+
+
+static /*HDR*/
+int show_lan_intf( MBG_DEV_HANDLE dh, const PCPS_DEV *p_dev, const char *info )
+{
+ IP4_SETTINGS ip4_settings;
+ char ws[256];
+
+ int rc = mbg_get_ip4_settings( dh, &ip4_settings );
+
+ if ( mbg_cond_err_msg( rc, "mbg_get_ip4_settings" ) )
+ return rc;
+
+ printf( "On-board LAN interface settings:" );
+
+ if ( ip4_settings.flags & IP4_MSK_DHCP )
+ printf( " (DHCP client)\n" );
+ else
+ {
+ printf( "\n" );
+
+ snprint_ip4_addr( ws, sizeof( ws ), &ip4_settings.ip_addr, NULL );
+ printf( " IP Address: %s\n", ws );
+
+ snprint_ip4_addr( ws, sizeof( ws ), &ip4_settings.netmask, NULL );
+ printf( " Net Mask: %s\n", ws );
+
+ snprint_ip4_addr( ws, sizeof( ws ), &ip4_settings.broad_addr, NULL );
+ printf( " Broadcast Addr: %s\n", ws );
+
+ snprint_ip4_addr( ws, sizeof( ws ), &ip4_settings.gateway, NULL );
+ printf( " Gateway: %s\n", ws );
+ }
+
+ return MBG_SUCCESS;
+
+} // show_lan_intf
+
+
+
+static /*HDR*/
+const char *intv_str( int i )
+{
+ static char s[20];
+
+ int abs_i = abs( i );
+ ulong ul;
+
+ // Currently the valid range is [-7:+7]
+ if ( abs_i > 7 )
+ return str_empty;
+
+ ul = 1UL << abs_i;
+
+ snprintf_safe( s, sizeof( s ), " (%s%lu s)", ( i < 0 ) ? "1/" : str_empty, ul );
+
+ return s;
+
+} // intv_str
+
+
+
+static /*HDR*/
+int show_ptp_cfg( MBG_DEV_HANDLE dh, const PCPS_DEV *p_dev, const char *info )
+{
+ ALL_PTP_CFG_INFO all_ptp_cfg_info;
+ PTP_CFG_INFO *pi = &all_ptp_cfg_info.ptp_cfg_info;
+ PTP_CFG_SETTINGS *ps = &pi->settings;
+ char ws[256];
+ int unicast_supported;
+ int idx;
+
+ int rc = mbg_get_all_ptp_cfg_info( dh, &all_ptp_cfg_info );
+
+ if ( mbg_cond_err_msg( rc, "mbg_get_all_ptp_cfg_info" ) )
+ return rc;
+
+ unicast_supported = ( pi->supp_flags & PTP_CFG_MSK_SUPPORT_PTP_UNICAST ) != 0;
+
+ printf( "\nPTP configuration:\n");
+
+ idx = ps->ptp_role;
+ printf( " PTP Role : %s (%s)\n", _ptp_role_name_short( idx ), _ptp_role_name( idx ) );
+
+ idx = ps->nw_prot;
+ printf( " Network Protocol: %s (%s)\n", nw_prot_short[idx], nw_prot[idx] );
+
+ printf( " Delay Mechanism : %s\n", delay_mech[ps->delay_mech] );
+ printf( " Domain Number : %d\n", ps->domain_number );
+ printf( " V1 HW Compat. : %d\n", ( ps->flags & PTP_CFG_MSK_V1_HW_COMPAT ) ? 1 : 0 );
+
+ printf( " Sync Msg Intv. : % i%s\n", ps->sync_intv, intv_str( ps->sync_intv) );
+ printf( " Ann. Msg Intv. : % i%s\n", ps->ann_intv, intv_str( ps->ann_intv ) );
+ printf( " Dly. Req. Intv. : % i%s\n", ps->delay_req_intv, intv_str( ps->delay_req_intv ) );
+
+ if ( unicast_supported )
+ {
+ PTP_UC_MASTER_CFG_LIMITS *p_uc_limits = &all_ptp_cfg_info.ptp_uc_master_cfg_limits;
+ int i;
+
+ for ( i = 0; i < p_uc_limits->n_supp_master; i++ )
+ {
+ PTP_UC_MASTER_SETTINGS *p = &all_ptp_cfg_info.all_ptp_uc_master_info_idx[i].info.settings;
+
+ printf( "\nConfigured PTP unicast master" );
+
+ if ( p_uc_limits->n_supp_master > 1 )
+ printf( " %i", i );
+
+ if ( ps->ptp_role == PTP_ROLE_MULTICAST_SLAVE )
+ printf( " (not used for this role)" );
+
+ printf( ":\n");
+
+ printf( " GM Host: %s\n", p->gm_host );
+
+ snprint_octets( ws, sizeof( ws ), p->gm_clock_id.b, sizeof( p->gm_clock_id.b ), MAC_SEP_CHAR, NULL );
+ printf( " GM Clock ID: %s%s\n", ws,
+ ( memcmp( &p->gm_clock_id, &clock_id_wildcard, sizeof( p->gm_clock_id ) ) == 0 ) ?
+ str_spc_wildcard : str_empty );
+
+ printf( " GM Port ID: %d%s\n", p->gm_port_id,
+ ( p->gm_port_id == PTP_PORT_ID_WILDCARD ) ? str_spc_wildcard : str_empty );
+
+ printf( " Sync Msg Intv. : % i%s\n", p->sync_intv, intv_str( p->sync_intv ) );
+ printf( " Ann. Msg Intv. : % i%s\n", p->ann_intv, intv_str( p->ann_intv ) );
+ printf( " Dly. Req. Intv. : % i%s\n", p->delay_req_intv, intv_str( p->delay_req_intv ) );
+ printf( " Message Duration: %i s\n", p->message_duration );
+ }
+ }
+
+ return MBG_SUCCESS;
+
+} // show_ptp_cfg
+
+
+
+static /*HDR*/
+/**
+ * @brief Lookup a string in a string table and return the table index
+ *
+ * @param s The string to be searched for in the string table
+ * @param tbl A table of strings to be searched
+ * @param n_entries The number of strings in the string table
+ * @param supp_mask A string with a given index is only supported if the
+ * corresponding bit is set in this mask.
+ * @param info A descriptive name of the parameter which is to be
+ * set to the index of the searched string
+ *
+ * @return >=0 A valid, supported table index
+ * <0 Unknown or unsupported parameter
+ */
+int get_chk_str_table_idx( const char *s, const char *tbl[], int n_entries,
+ uint32_t supp_mask, const char *info )
+{
+ int i;
+ const char *cp;
+
+ for ( i = 0; i < n_entries; i++ )
+ {
+ cp = tbl[i];
+
+ if ( strncmp( s, cp, strlen( cp ) ) == 0 )
+ if ( supp_mask & ( 1UL << i ) )
+ return i;
+ }
+
+ printf( "error: %s %s\n",
+ ( i == n_entries ) ? "unknown" : "unsupported",
+ info );
+
+ return -3;
+
+} // get_chk_str_table_idx
+
+
+
+static /*HDR*/
+/**
+ * @brief Lookup a parameter in an argument list and check if a colon is appended
+ *
+ * @param arg An argument list of the form: "name:val,name:val,..."
+ * @param s A parameter name searched for in the argument list
+ * @param p A pointer to a (char *) which is set to the beginning
+ * of the value part of a parameter string
+ *
+ * @return ::MBG_SUCCESS on success. If parameter has been found then
+ * @p *p is set to the parameter value, else to NULL.
+ * ::MBG_ERR_PARM_FMT if syntax error, i.e. missing colon.
+ */
+int chk_parm_name( const char *arg, const char *id, char **p )
+{
+ char *cp = strstr( arg, id );
+
+ if ( cp )
+ {
+ cp += strlen( id );
+
+ if ( *cp != ':' )
+ return MBG_ERR_PARM_FMT;
+
+ cp++;
+ }
+
+ if ( p )
+ *p = cp; // may be NULL
+
+ return MBG_SUCCESS;
+
+} // chk_parm_name
+
+
+
+static /*HDR*/
+/**
+ * @brief Lookup a parameter in an argument list and check if the value string is valid
+ *
+ * This function expects that the parameter value is a known string which can
+ * be found in a table of known strings. A bit mask indicates if a string with
+ * a given table index is supported, or not.
+ *
+ * @param arg An argument list of the form: "name:val,name:val,..."
+ * @param id The name of the argument to be checked.
+ * @param tbl A table of strings with predefined parameter values
+ * @param n_entries The number of strings in the string table
+ * @param supp_mask A string with a given index is only supported if the
+ * corresponding bit is set in this mask.
+ * @param info A descriptive name of the parameter which is to be
+ * set to the index of the searched string
+ *
+ * @return >=0 A valid, supported table index
+ * -1 Parameter not found
+ * -2 Unknown or unsupported parameter, or syntax error
+ */
+int chk_tbl_parm( const char *arg, const char *id, const char *tbl[],
+ int n_entries, uint32_t supp_mask, const char *info )
+{
+ char *cp;
+ int rc = chk_parm_name( arg, id, &cp );
+
+ if ( rc < 0 )
+ return -2;
+
+ if ( cp == NULL )
+ return -1;
+
+ rc = get_chk_str_table_idx( cp, tbl, n_entries, supp_mask, info );
+
+ if ( rc < 0 )
+ return -2;
+
+ return rc;
+
+} // chk_tbl_parm
+
+
+
+static /*HDR*/
+/**
+ * @brief Lookup an int16_t parameter in an argument list
+ *
+ * Check and save the numeric parameter if in a valid range.
+ *
+ * @param arg An argument list of the form: "name:val,name:val,..."
+ * @param id The name of the argument to be checked.
+ * @param p A pointer to a variable where the parameter value is
+ * saved if valid
+ * @param range_min The minimum allowed value for the parameter
+ * @param range_max The maximum allowed value for the parameter
+ * @param is_supported A flag indicating if the parameter is actually supported
+ * @param info A descriptive name of the parameter
+ *
+ * @return >=0 A valid, supported table index
+ * -1 Parameter not found, or out of range
+ */
+int chk_int16_parm( const char *arg, const char *id, int16_t *p, int16_t range_min,
+ int16_t range_max, int is_supported, const char *info )
+{
+ char *cp;
+ int idx = chk_parm_name( arg, id, &cp );
+
+ if ( mbg_rc_is_error( idx ) ) // parameter error
+ return idx;
+
+ if ( cp ) // parameter found
+ {
+ if ( !is_supported )
+ err_unicast_nsupp = 1;
+ else
+ {
+ int16_t tmp = atoi( cp );
+
+ if ( ( tmp < range_min ) || ( tmp > range_max ) )
+ {
+ printf( "error: %s %i out of range (%i..%i)\n", info, tmp, range_min, range_max );
+ return MBG_ERR_RANGE;
+ }
+
+ *p = tmp;
+ printf( " setting %s: %i\n", info, tmp );
+ }
+ }
+
+ return 0;
+
+} // chk_int16_parm
+
+
+
+static /*HDR*/
+int set_ptp_cfg( MBG_DEV_HANDLE dh, const char *arg, const PCPS_DEV *p_dev )
+{
+ ALL_PTP_CFG_INFO all_ptp_cfg_info = { { { 0 } } };
+ ALL_PTP_CFG_INFO prv_all_ptp_cfg_info;
+ PTP_CFG_INFO *p_info;
+ PTP_CFG_SETTINGS *p_settings;
+ PTP_UC_MASTER_CFG_LIMITS *p_uc_limits;
+ PTP_UC_MASTER_INFO *p_uc_info;
+ PTP_UC_MASTER_SETTINGS *p_uc_settings;
+ uint32_t supp_mask;
+ IP4_ADDR ip4addr;
+ char ws[256];
+ char *cp;
+ int idx;
+ int unicast_supported;
+ int uc_master_idx = 0;
+ const char *err_info = NULL;
+ int rc = mbg_get_all_ptp_cfg_info( dh, &all_ptp_cfg_info );
+
+ if ( rc < 0 )
+ return rc; // failed to read current settings and capabilities
+
+
+ err_unicast_nsupp = 0;
+
+ // save a copy of the current settings
+ prv_all_ptp_cfg_info = all_ptp_cfg_info;
+
+ p_info = &all_ptp_cfg_info.ptp_cfg_info;
+ p_settings = &p_info->settings;
+ unicast_supported = ( p_info->supp_flags & PTP_CFG_MSK_SUPPORT_PTP_UNICAST ) != 0;
+
+ p_uc_limits = &all_ptp_cfg_info.ptp_uc_master_cfg_limits;
+ // The pointers below need to be updated whenever uc_master_idx is changed
+ p_uc_info = &all_ptp_cfg_info.all_ptp_uc_master_info_idx[uc_master_idx].info;
+ p_uc_settings = &p_uc_info->settings;
+
+
+ // Network protocol
+ idx = chk_tbl_parm( arg, ptp_name_net, nw_prot_short, N_PTP_NW_PROT, p_info->supp_nw_prot, "network protocol type" );
+
+ if ( idx >= 0 ) // valid parameter found
+ p_settings->nw_prot = idx;
+ else
+ if ( idx < -1 )
+ {
+ err_info = ptp_name_net;
+ goto fail;
+ }
+
+ // Delay Mechanism
+ idx = chk_tbl_parm( arg, ptp_name_del, delay_mech, N_PTP_DELAY_MECH, p_info->supp_delay_mech, "delay mechanism" );
+
+ if ( idx >= 0 ) // valid parameter found
+ p_settings->delay_mech = idx;
+ else
+ if ( idx < -1 )
+ {
+ err_info = ptp_name_del;
+ goto fail;
+ }
+
+
+ // Domain Number
+ idx = chk_parm_name( arg, ptp_name_dom, &cp );
+
+ if ( mbg_rc_is_error( idx ) ) // parameter error
+ {
+ err_info = ptp_name_dom;
+ goto fail;
+ }
+
+ if ( cp ) // parameter found
+ {
+ idx = atoi( cp );
+ // TODO Must check range!!
+ p_settings->domain_number = idx;
+ }
+
+
+ // V1 Hardware compatibility flag
+ idx = chk_parm_name( arg, ptp_name_v1, &cp );
+
+ if ( mbg_rc_is_error( idx ) ) // parameter error
+ {
+ err_info = ptp_name_v1;
+ goto fail;
+ }
+
+ if ( cp ) // parameter found
+ {
+ idx = atoi( cp );
+
+ switch ( idx )
+ {
+ case 0:
+ p_settings->flags &= ~PTP_CFG_MSK_V1_HW_COMPAT;
+ break;
+
+ case 1:
+ p_settings->flags |= PTP_CFG_MSK_V1_HW_COMPAT;
+ break;
+
+ default:
+ printf( "error: No valid V1 HWC settings!\n" );
+ goto fail;
+ }
+ }
+
+
+ //---- unicast stuff ----
+
+ // PTP role
+ supp_mask = get_supp_ptp_role_mask( p_info->supp_flags );
+ idx = chk_tbl_parm( arg, ptp_name_role, ptp_roles_short, N_PTP_ROLES, supp_mask, "PTP role" );
+
+ if ( idx >= 0 ) // valid parameter found
+ {
+ if ( !unicast_supported )
+ err_unicast_nsupp = 1;
+ else
+ p_settings->ptp_role = idx;
+ }
+ else
+ if ( idx < -1 ) // parameter error
+ {
+ err_info = ptp_name_role;
+ goto fail;
+ }
+
+
+ // GM Host
+ idx = chk_parm_name( arg, ptp_name_gmip, &cp );
+
+ if ( mbg_rc_is_error( idx ) ) // parameter error
+ {
+ err_info = ptp_name_gmip;
+ goto fail;
+ }
+
+ if ( cp ) // parameter found
+ {
+ if ( !unicast_supported )
+ err_unicast_nsupp = 1;
+ else
+ {
+ // currently IP addresses are accepted only, so check for
+ // a valid IPv4 address
+ if ( str_to_ip4_addr( &ip4addr, cp ) > 0 )
+ {
+ snprint_ip4_addr( ws, sizeof( ws ), &ip4addr, NULL );
+ printf( " GM IP Address: %s\n", ws );
+
+ memset( p_uc_settings->gm_host, 0, sizeof( p_uc_settings->gm_host ) );
+ strcpy( p_uc_settings->gm_host, ws );
+ }
+ else
+ {
+ err_info = ptp_name_gmip;
+ goto fail;
+ }
+ }
+ }
+
+
+ // GM Clock ID
+ idx = chk_parm_name( arg, ptp_name_gmid, &cp );
+
+ if ( mbg_rc_is_error( idx ) ) // parameter error
+ {
+ err_info = ptp_name_gmid;
+ goto fail;
+ }
+
+ if ( cp ) // parameter found
+ {
+ if ( !unicast_supported )
+ err_unicast_nsupp = 1;
+ else
+ {
+ PTP_CLOCK_ID gm_clock_id;
+
+ // Check if specified GM ID is wildcard.
+ if ( *cp == '*' ) // TODO check if next char is separator
+ {
+ gm_clock_id = clock_id_wildcard;
+ idx = sizeof( gm_clock_id );
+ }
+ else
+ idx = str_to_octets( gm_clock_id.b, sizeof( gm_clock_id.b ), cp );
+
+ if ( idx != sizeof( gm_clock_id ) )
+ {
+ printf( "Syntax error in specified GM clock ID\n" );
+ goto fail;
+ }
+
+ p_uc_settings->gm_clock_id = gm_clock_id;
+
+ snprint_octets( ws, sizeof( ws ), p_uc_settings->gm_clock_id.b,
+ sizeof( p_uc_settings->gm_clock_id.b ), MAC_SEP_CHAR, NULL );
+ printf( " setting GM Clock ID: %s\n", ws );
+ }
+ }
+
+
+ // GM Target Port ID
+ idx = chk_parm_name( arg, ptp_name_pid, &cp );
+
+ if ( mbg_rc_is_error( idx ) ) // parameter error
+ {
+ err_info = ptp_name_pid;
+ goto fail;
+ }
+
+ if ( cp ) // parameter found
+ {
+ if ( !unicast_supported )
+ err_unicast_nsupp = 1;
+ else
+ {
+ // Check if specified Port ID is wildcard.
+ if ( *cp == '*' ) // TODO check if next char is separator
+ p_uc_settings->gm_port_id = PTP_PORT_ID_WILDCARD;
+ else
+ p_uc_settings->gm_port_id = (PTP_PORT_ID) strtoul( cp, NULL, 0 );
+
+ printf( " setting GM port id: %d%s\n", p_uc_settings->gm_port_id,
+ ( p_uc_settings->gm_port_id == PTP_PORT_ID_WILDCARD ) ? str_spc_wildcard : str_empty );
+ }
+ }
+
+
+ // Sync Message Rate
+ idx = chk_int16_parm( arg, ptp_name_smi,
+ ( p_settings->ptp_role == PTP_ROLE_MULTICAST_SLAVE ) ?
+ &p_settings->sync_intv : &p_uc_settings->sync_intv,
+ p_uc_limits->sync_intv_min, p_uc_limits->sync_intv_max,
+ 1, "sync intv." );
+ if ( idx < 0 )
+ {
+ err_info = ptp_name_smi;
+ goto fail;
+ }
+
+
+ // Announce Message Rate
+ idx = chk_int16_parm( arg, ptp_name_ami,
+ ( p_settings->ptp_role == PTP_ROLE_MULTICAST_SLAVE ) ?
+ &p_settings->ann_intv : &p_uc_settings->ann_intv,
+ p_uc_limits->ann_intv_min, p_uc_limits->ann_intv_max,
+ 1, "ann. intv." );
+ if ( idx < 0 )
+ {
+ err_info = ptp_name_ami;
+ goto fail;
+ }
+
+
+ // Delay Request Interval Rate
+ idx = chk_int16_parm( arg, ptp_name_dri,
+ ( p_settings->ptp_role == PTP_ROLE_MULTICAST_SLAVE ) ?
+ &p_settings->delay_req_intv : &p_uc_settings->delay_req_intv,
+ p_uc_limits->delay_req_intv_min, p_uc_limits->delay_req_intv_max,
+ 1, "delay req. intv." );
+ if ( idx < 0 )
+ {
+ err_info = ptp_name_dri;
+ goto fail;
+ }
+
+
+
+ // Message Duration
+ idx = chk_int16_parm( arg, ptp_name_dur, (int16_t *) &p_uc_settings->message_duration,
+ PTP_UC_MSG_DURATION_MIN, PTP_UC_MSG_DURATION_MAX,
+ unicast_supported, "msg. duration" );
+ if ( idx < 0 )
+ {
+ err_info = ptp_name_dur;
+ goto fail;
+ }
+
+
+ if ( memcmp( &all_ptp_cfg_info, &prv_all_ptp_cfg_info, sizeof( all_ptp_cfg_info ) ) != 0 )
+ {
+ rc = mbg_save_all_ptp_cfg_info( dh, &all_ptp_cfg_info );
+
+ if ( mbg_cond_err_msg( rc, "mbg_save_all_ptp_cfg_info" ) )
+ return rc;
+ }
+
+ return MBG_SUCCESS;
+
+
+fail:
+ printf( "Invalid parameter in argument" );
+
+ if ( err_info )
+ printf( " %s", err_info );
+
+ printf( "!\n" );
+
+ return MBG_ERR_CFG;
+
+} // set_ptp_cfg
+
+
+
+static /*HDR*/
+int ip4_check_parm( const char *s, IP4_INFO *p )
+{
+ int l = strlen( p->name );
+ int n;
+
+ if ( strncmp( s, p->name, l ) != 0 )
+ return 0; // parameter does not match
+
+ if ( s[l] != ':' )
+ goto fail; // parameter syntax error: name not followed by colon
+
+ l++;
+
+ n = str_to_ip4_addr( p->addr, &s[l] );
+
+ if ( n < 0 )
+ goto fail; // parameter syntax error: failed to convert numeric address
+
+ return n + l; //##++++++ needs to be verified
+
+fail:
+ return MBG_ERR_CFG;
+
+} // ip4_check_parm
+
+
+
+static /*HDR*/
+int set_lan_intf( MBG_DEV_HANDLE dh, const char *arg, const PCPS_DEV *p_dev )
+{
+ IP4_SETTINGS prv_ip4_settings;
+ IP4_SETTINGS ip4_settings = { 0 };
+ IP4_ADDR default_broad_addr;
+
+ IP4_INFO ip4_info[N_IP4_INFO] =
+ {
+ { ip4_name_ip, &ip4_settings.ip_addr },
+ { ip4_name_nm, &ip4_settings.netmask },
+ { ip4_name_ba, &ip4_settings.broad_addr },
+ { ip4_name_gw, &ip4_settings.gateway }
+ };
+
+ int rc = mbg_get_ip4_settings( dh, &prv_ip4_settings );
+
+ if ( mbg_cond_err_msg( rc, "mbg_get_ip4_settings" ) )
+ return rc;
+
+ if ( strcmp( arg, "DHCP" ) == 0 )
+ {
+ ip4_settings = prv_ip4_settings;
+ ip4_settings.flags |= IP4_MSK_DHCP;
+ goto save;
+ }
+
+ for (;;)
+ {
+ int i;
+
+ for ( i = 0; i < N_IP4_INFO; i++ )
+ {
+ rc = ip4_check_parm( arg, &ip4_info[i] );
+
+ if ( rc == 0 ) // check next
+ continue;
+
+ if ( rc < 0 ) // error
+ goto invalid_msg;
+
+ arg += rc;
+
+ if ( *arg == 0 ) // end of parameter string
+ goto done;
+
+ if ( *arg != ',' )
+ goto invalid_msg;
+
+ arg++;
+ }
+ }
+
+
+done: // now check static configuration
+ if ( ip4_settings.ip_addr == 0 ) // no IP address specified on the command line
+ ip4_settings.ip_addr = prv_ip4_settings.ip_addr; // use previous IP address
+
+ if ( ip4_settings.ip_addr == 0 ) // no IP address specified at all
+ {
+ printf( "*** Aborting: No IP address specified\n" );
+ goto invalid;
+ }
+
+ if ( ip4_settings.netmask == 0 ) // no network mask specified on the command line
+ ip4_settings.netmask = prv_ip4_settings.netmask; // use previous network mask
+
+ if ( ip4_settings.netmask == 0 ) // no network mask specified at all
+ {
+ printf( "*** Aborting: No network mask specified\n" );
+ goto invalid;
+ }
+
+ // the default broadcast address is computed from the IP address and the network mask
+ default_broad_addr = ip4_settings.ip_addr | ~ip4_settings.netmask;
+
+ if ( ip4_settings.broad_addr == 0 ) // no broadcast address specified on the command line
+ ip4_settings.broad_addr = default_broad_addr;
+ else
+ if ( ip4_settings.broad_addr != default_broad_addr )
+ {
+ char ws1[40];
+ char ws2[40];
+ snprint_ip4_addr( ws1, sizeof( ws1 ), &ip4_settings.broad_addr, NULL );
+ snprint_ip4_addr( ws2, sizeof( ws2 ), &default_broad_addr, NULL );
+ printf( "*** Warning: Broadcast address %s does not match the default broadcast address %s\n",
+ ws1, ws2 );
+ }
+
+ if ( ip4_settings.gateway == 0 )
+ ip4_settings.gateway = prv_ip4_settings.gateway;
+
+ ip4_settings.flags = prv_ip4_settings.flags & ~IP4_MSK_DHCP;
+ // fall through to save:
+
+save:
+ rc = mbg_set_ip4_settings( dh, &ip4_settings );
+
+ if ( mbg_cond_err_msg( rc, "mbg_set_ip4_settings" ) )
+ return rc;
+
+ return MBG_SUCCESS;
+
+
+invalid_msg:
+ printf( "*** Warning: invalid LAN interface parameter syntax\n" );
+ // fall through to invalid:
+
+invalid:
+ return MBG_ERR_CFG; // invalid parameter or parameter syntax error
+
+} // set_lan_intf
+
+
+
+static /*HDR*/
+int set_gps_pos( MBG_DEV_HANDLE dh, const char *gp )
+{
+ LLA new_pos_lla;
+ char *cp;
+ int rc;
+ double r2d = 180 / PI;
+
+ new_pos_lla[LAT] = strtod( gp, &cp ) / r2d;
+
+ if ( *cp++ != ',' )
+ goto invalid;
+
+ new_pos_lla[LON] = strtod( cp, &cp ) / r2d;
+
+ if ( *cp++ != ',' )
+ goto invalid;
+
+ new_pos_lla[ALT] = strtod( cp, &cp );
+
+ if (
+ ( ( new_pos_lla[LAT] * r2d ) < -90 ) ||
+ ( ( new_pos_lla[LAT] * r2d ) > +90 ) ||
+ ( ( new_pos_lla[LON] * r2d ) < -180 ) ||
+ ( ( new_pos_lla[LON] * r2d ) > +180 ) ||
+ ( new_pos_lla[ALT] < -50 ) ||
+ ( new_pos_lla[ALT] > 20000 )
+ )
+ goto invalid;
+
+
+ rc = mbg_set_gps_pos_lla ( dh, new_pos_lla );
+
+ if ( mbg_cond_err_msg( rc, "mbg_set_gps_pos_lla" ) )
+ return rc;
+
+ printf( "The clock's receiver position has been set to lat=%+.4f, lon=%+.4f, alt=%.0fm\n",
+ new_pos_lla[LAT] * r2d, new_pos_lla[LON] * r2d, new_pos_lla[ALT] );
+
+ return rc;
+
+
+invalid:
+ fprintf( stderr, "** Invalid GPS position parameters: %s\n", gp );
+
+ return MBG_ERR_CFG;
+
+} // set_gps_pos
+
+
+
+static /*HDR*/
+int set_date_time( MBG_DEV_HANDLE dh, const PCPS_DEV *p_dev,
+ const char *sdate, const char *stime )
+{
+ PCPS_TIME_UNION u = { { 0 } };
+ TTM ttm = { 0 };
+ unsigned int year = 0;
+ unsigned int month = 0;
+ unsigned int mday = 0;
+ unsigned int hour = 0;
+ unsigned int min = 0;
+ unsigned int sec = 0;
+ unsigned int sec100 = 0;
+ int rc;
+
+ // Either a date string, a time string, or both
+ // may have been passed to this function.
+ // If either of them is NULL read the current date/time
+ // as default values.
+ if ( sdate == NULL || stime == NULL )
+ {
+ rc = mbg_get_time( dh, &u.t );
+
+ if ( mbg_cond_err_msg( rc, "mbg_get_time" ) )
+ return rc;
+ }
+
+
+ if ( sdate )
+ {
+ rc = sscanf( sdate, "%u-%u-%u", &year, &month, &mday );
+
+ if ( ( rc < 3 )
+ || ( month < 1 ) || ( month > 12 )
+ || ( mday < 1 ) || ( mday > 31 ) )
+ {
+ printf( "** Invalid date: %04u-%02u-%02u\n", year, month, mday );
+ return MBG_ERR_CFG;
+ }
+ }
+
+
+ if ( stime )
+ {
+ rc = sscanf( stime, "%u:%u:%u.%u", &hour, &min, &sec, &sec100 );
+
+ if ( ( rc < 2 ) // at least hours and minutes are required
+ || ( hour > 23 )
+ || ( min > 59 )
+ || ( sec > 60 )
+ || ( sec100 > 99 ) )
+ {
+ printf( "** Invalid time: %02u:%02u:%02u.%02u\n", hour, min, sec, sec100 );
+ return MBG_ERR_CFG;
+ }
+ }
+
+ // GPS and non-GPS cards require different API calls which use
+ // different structures to set the on-board date and time,
+ // so we have to distinguish.
+
+ if ( _pcps_is_gps( p_dev ) ) // is a GPS card
+ {
+ ttm.channel = -1;
+
+ if ( sdate ) // new date
+ {
+ ttm.tm.year = year;
+ ttm.tm.month = month;
+ ttm.tm.mday = mday;
+ }
+ else // copy current date as default
+ {
+ ttm.tm.year = u.t.year + 2000;
+ ttm.tm.month = u.t.month;
+ ttm.tm.mday = u.t.mday;
+ }
+
+ if ( stime ) // new time
+ {
+ ttm.tm.hour = hour;
+ ttm.tm.min = min;
+ ttm.tm.sec = sec;
+ ttm.tm.frac = sec100;
+ }
+ else // copy current time as default
+ {
+ ttm.tm.hour = u.t.hour;
+ ttm.tm.min = u.t.min;
+ ttm.tm.sec = u.t.sec;
+ ttm.tm.frac = u.t.sec100;
+ }
+
+ ttm.tm.frac *= 100000; // fracs are in 100 ns units
+
+ #if 0
+ // Existing versions of the GPS cards just take
+ // TTM.tm as local time without accounting for
+ // the status flags, or UTC offset.
+ // Instead, the TTM.tm time is converted on-board
+ // to UTC depending on the local TZDL configuration.
+ // This works if the system time and the
+ ttm.tm.offs_from_utc = 7200;
+ ttm.tm.status |= TM_UTC | TM_LOCAL;
+ ttm.tm.status |= TM_DL_ENB;
+ #endif
+
+ rc = mbg_set_gps_time( dh, &ttm );
+
+ if ( mbg_cond_err_msg( rc, "mbg_set_gps_time" ) )
+ return rc;
+ }
+ else // is not a GPS card
+ {
+ if ( sdate ) // new date
+ {
+ // determine the day-of-week for the given date
+ struct tm tm = { 0 };
+
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = mday;
+ tm.tm_hour = 12;
+ tm.tm_isdst = -1;
+ mktime( &tm );
+
+ u.stime.year = year % 100;
+ u.stime.month = month;
+ u.stime.mday = mday;
+ u.stime.wday = _wday_sun06_to_mon17( tm.tm_wday );
+ }
+
+ if ( stime ) // new time
+ {
+ u.stime.hour = hour;
+ u.stime.min = min;
+ u.stime.sec = sec;
+ u.stime.sec100 = sec100;
+ }
+
+ if ( u.stime.wday == 0 )
+ u.stime.wday = 1; // dummy
+
+ rc = mbg_set_time( dh, &u.stime );
+
+ if ( mbg_cond_err_msg( rc, "mbg_set_time" ) )
+ return rc;
+ }
+
+ printf( "Device date/time have been set.\n" );
+
+ return MBG_SUCCESS;
+
+} // set_date_time
+
+
+
+static /*HDR*/
+int check_setup_receiver_info( MBG_DEV_HANDLE dh, RECEIVER_INFO *p_ri )
+{
+ int rc = MBG_SUCCESS;
+
+ // Set up the RECEIVER_INFO structure only if this has not been done
+ // before. Check the ticks_per_sec field to see if the structure
+ // has already been set up or not.
+ if ( p_ri->ticks_per_sec == 0 && p_ri->model_code == 0 )
+ {
+ rc = mbg_setup_receiver_info( dh, NULL, p_ri );
+ mbg_cond_err_msg( rc, "mbg_setup_receiver_info" );
+ }
+
+ return rc;
+
+} // check_setup_receiver_info
+
+
+
+static /*HDR*/
+// returns a negative error code on failure, or the number of supported COM ports on success
+int check_get_receiver_port_cfg( MBG_DEV_HANDLE dh, RECEIVER_PORT_CFG *p_rpcfg,
+ const PCPS_DEV *p_dev, RECEIVER_INFO *p_ri )
+{
+ int rc = check_setup_receiver_info( dh, p_ri );
+
+ if ( mbg_rc_is_error( rc ) )
+ goto out;
+
+ // Set up the RECEIVER_PORT_CFG structure only if this has not been done
+ // before. Check whether the number of ports is > 0 and the first port's
+ // baud rate is still 0 to see if the structure has already been set up.
+ if ( ( p_ri->n_com_ports > 0 ) &&
+ ( p_rpcfg->pii[0].port_info.port_settings.parm.baud_rate == 0 ) )
+ {
+ rc = mbg_get_serial_settings( dh, p_dev, p_rpcfg, p_ri );
+ mbg_cond_err_msg( rc, "mbg_get_serial_settings" );
+ }
+
+ if ( mbg_rc_is_success( rc ) )
+ rc = p_ri->n_com_ports;
+
+out:
+ return rc;
+
+} // check_get_receiver_port_cfg
+
+
+
+static /*HDR*/
+int snprint_port_cfg( char *s, int sz, unsigned int port_num,
+ const RECEIVER_PORT_CFG *p_rpcfg )
+{
+ const PORT_SETTINGS *p_ps = &p_rpcfg->pii[port_num].port_info.port_settings;
+
+ int n = snprintf( s, sz, "\"%s\" %s at %lu/%s",
+ p_rpcfg->stii[p_ps->str_type].str_type_info.long_name,
+ mode_names[p_ps->mode],
+ (unsigned long) p_ps->parm.baud_rate,
+ p_ps->parm.framing
+ );
+
+ return n;
+
+} // snprint_port_cfg
+
+
+
+static /*HDR*/
+void show_serial_settings( MBG_DEV_HANDLE dh, const RECEIVER_PORT_CFG *p_rpcfg,
+ const RECEIVER_INFO *p_ri )
+{
+ char ws[256];
+ unsigned int i;
+
+ for ( i = 0; i < p_ri->n_com_ports; i ++ )
+ {
+ snprint_port_cfg( ws, sizeof( ws ), i, p_rpcfg );
+ printf( "Serial port COM%u: %s.\n", i, ws );
+ }
+
+} // show_serial_settings
+
+
+
+static /*HDR*/
+void print_port_info_errors( const char *port_name, unsigned int port_num,
+ int flags, const RECEIVER_PORT_CFG *p_rpcfg )
+{
+ const char *msg_nsupp_drvr = "is not supported by the driver software";
+
+ const PORT_INFO *p_pi = &p_rpcfg->pii[port_num].port_info;
+ const PORT_SETTINGS *p_ps = &p_pi->port_settings;
+
+
+ if ( flags & MBG_PS_MSK_BAUD_RATE_OVR_SW )
+ printf( "** Baud rate %lu %s.\n", (ulong) p_ps->parm.baud_rate, msg_nsupp_drvr );
+ else
+ if ( flags & MBG_PS_MSK_BAUD_RATE )
+ printf( "** Baud rate %lu is not supported by %s%u.\n",
+ (ulong) p_ps->parm.baud_rate, port_name, port_num );
+
+
+ if ( flags & MBG_PS_MSK_FRAMING_OVR_SW )
+ printf( "** Framing %s %s.\n", p_ps->parm.framing, msg_nsupp_drvr );
+ else
+ if ( flags & MBG_PS_MSK_FRAMING )
+ printf( "** Framing %s is not supported by %s%u.\n",
+ p_ps->parm.framing, port_name, port_num );
+
+
+ if ( flags & MBG_PS_MSK_HS_OVR_SW )
+ printf( "** Handshake mode %u %s.\n", p_ps->parm.handshake, msg_nsupp_drvr );
+ else
+ if ( flags & MBG_PS_MSK_HS )
+ printf( "** Handshake mode %u is not supported by %s%u.\n",
+ (unsigned int) p_ps->parm.handshake, port_name, port_num );
+
+
+ if ( flags & MBG_PS_MSK_STR_TYPE_OVR_DEV )
+ printf( "** String type index %u exceeds number of string types supp. by device.\n",
+ p_ps->str_type );
+ else
+ if ( flags & MBG_PS_MSK_STR_TYPE )
+ printf( "** String type \"%s\" is not supported by %s%u.\n",
+ p_rpcfg->stii[p_ps->str_type].str_type_info.long_name,
+ port_name, port_num );
+
+
+ if ( flags & MBG_PS_MSK_STR_MODE_OVR_SW )
+ printf( "** String mode index %u exceeds number of string modes supported by driver software (%u).\n",
+ p_ps->mode, N_STR_MODE );
+ else
+ if ( flags & MBG_PS_MSK_STR_MODE )
+ printf( "** String mode \"%s\" is not supported by string type \"%s\" via %s%u .\n",
+ mode_names[p_ps->mode],
+ p_rpcfg->stii[p_ps->str_type].str_type_info.long_name,
+ port_name, port_num );
+
+} // print_port_info_errors
+
+
+
+static /*HDR*/
+int save_serial_settings( MBG_DEV_HANDLE dh, unsigned int port_num, const char *parm_str,
+ const PCPS_DEV *p_dev, const RECEIVER_INFO *p_ri )
+{
+ char ws[256];
+ RECEIVER_PORT_CFG rpcfg = { { { 0 } } };
+ PORT_INFO *p_pi = &rpcfg.pii[port_num].port_info;
+ PORT_SETTINGS *p_ps = &p_pi->port_settings;
+ ulong ul;
+ const char *cp;
+ char *p_tail;
+ int rc;
+ int flags;
+ int i;
+
+ // load current settings and supported settings
+ rc = mbg_get_serial_settings( dh, p_dev, &rpcfg, p_ri );
+
+ if ( mbg_cond_err_msg( rc, "mbg_get_serial_settings" ) )
+ return rc;
+
+
+ cp = parm_str;
+ p_ps->parm.baud_rate = strtoul( cp, &p_tail, 10 );
+
+ if ( p_tail == cp ) // no number found
+ goto invalid;
+
+ cp = p_tail;
+
+ if ( *cp++ != ',' ) // separator before framing missing
+ goto invalid;
+
+ for ( i = 0; i < sizeof( p_ps->parm.framing ); i++ )
+ {
+ int c = *cp;
+
+ if ( !isalnum( c ) ) // stop copying if non-alpha and non-digit
+ break;
+
+ p_ps->parm.framing[i] = *cp++;
+ }
+
+ p_ps->parm.framing[i] = 0; // force terminating 0
+
+ p_ps->parm.handshake = HS_NONE; // this is the only supported setting
+
+
+ // get optional string type index
+
+ if ( *cp++ != ',' )
+ goto done_parm_str;
+
+ ul = strtoul( cp, &p_tail, 10 );
+
+ if ( p_tail != cp )
+ p_ps->str_type = (uint8_t) ul; // TODO check range ?
+
+ cp = p_tail;
+
+
+ // get optional string mode index
+
+ if ( *cp++ != ',' )
+ goto done_parm_str;
+
+ ul = strtoul( cp, &p_tail, 10 );
+
+ if ( p_tail != cp )
+ p_ps->mode = (uint8_t) ul; // TODO check range ?
+
+
+done_parm_str:
+
+ // check if the new parameter set is valid
+ flags = check_valid_port_info( p_pi, rpcfg.stii, p_ri->n_str_type );
+
+ if ( flags ) // parameters not valid
+ {
+ print_port_info_errors( "COM", port_num, flags, &rpcfg );
+ goto invalid;
+ }
+
+ // save new parameters
+ rc = mbg_save_serial_settings( dh, p_dev, &rpcfg, port_num );
+
+ if ( mbg_cond_err_msg( rc, "mbg_save_serial_settings" ) )
+ return rc;
+
+
+ snprint_port_cfg( ws, sizeof( ws ), port_num, &rpcfg );
+ printf( "The clock's COM%u port has been set to %s.\n", port_num, ws );
+
+ return MBG_SUCCESS;
+
+
+invalid:
+ fprintf( stderr, "** Invalid COM port parameters: %s\n", parm_str );
+ return MBG_ERR_CFG;
+
+} // save_serial_settings
+
+
+
+static /*HDR*/
+// returns a negative error code on failure, or the number of supported programmable pulse outputs on success
+int check_get_pout_cfg( MBG_DEV_HANDLE dh, ALL_POUT_INFO_IDX api, RECEIVER_INFO *p_ri )
+{
+ int rc = check_setup_receiver_info( dh, p_ri );
+
+ // Set up the ALL_POUT_INFO_IDX structure only if this has not been done
+ // before. Check whether the number of ports is > 0 and the first port's
+ // baud rate is still 0 to see if the structure has already been set up.
+ if ( ( p_ri->n_prg_out > 0 ) &&
+ ( api[0].pout_info.supp_modes == 0 ) )
+ {
+ rc = mbg_get_gps_all_pout_info( dh, api, p_ri );
+ mbg_cond_err_msg( rc, "mbg_get_gps_all_pout_info" );
+ }
+
+ if ( mbg_rc_is_success( rc ) )
+ rc = p_ri->n_prg_out;
+
+ return rc;
+
+} // check_get_pout_cfg
+
+
+
+static /*HDR*/
+int help_pout_arg( MBG_DEV_HANDLE dh, const PCPS_DEV *p_dev,
+ const OPT_HANDLER_SPEC *p_opt, CTRL_FLAGS ctrl_flags )
+{
+ const INDENTS *p_ind = &usage_indents;
+ const INDENTS *p_ind_detailed = &usage_indents_detailed;
+ int rc = MBG_SUCCESS;
+ int i;
+
+ usage_line( p_ind, p_opt, NULL, "Show settings of the programmable output(s)" );
+ usage_line( p_ind, p_opt, "MODE:<m>[,LEN:<l>][,INV:<i>][,OIS:<o>][,SHIFT:<s>]", "Configure programmable output <n>" ); // ### TODO
+
+ printf( "\n"
+ " where:\n" );
+
+ printf( " MODE:<m> Specifies the programmable output mode with index <m>, see mbgctrl -?.\n"
+ " Please note that subsequent parameters may only be appropriate for specific modes.\n\n" );
+
+ printf( " LEN:<l> Specifies the pulse lenght for modes that support this.\n"
+ " <l> is an integer value in 10 ms units, i.e. <l> = 20 yields 200 ms.\n\n" );
+
+ printf( " INV:<i> \"Inverted\" flag specifying if the output level is to be inverted,\n"
+ " if the output supports this.\n"
+ " Values for <i>: 1 invert output level, 0 don't invert.\n\n" );
+
+ printf( " OIS:<o> \"Only If Sync\" flag specifying if the output is to be enabled ONLY\n"
+ " while the device is synchronized, if the output supports this.\n"
+ " Values for <o>: 1 enable this feature, 0 disable this feature\n"
+ " NOTE: This overrides the Enable Flags settings (parameter \"EF\").\n"
+ " The output is enabled ONLY when the device state changes to \"synchronized\"\n"
+ " after power-up, and is disabled again when the device enters holdover mode,\n"
+ " i.e. the reference signal is lost.\n\n" );
+
+ printf( " SHIFT:<s> Specifies a phase shift of the output slope, if the output supports this.\n"
+ " <s> is an integer value, in nanoseconds.\n"
+ " The maximum range is %+li to %+li ns,\n"
+ " corresponding to %+li to %+li ms.\n"
+ " The effective resolution depends on the resolution of the device's internal clock,\n"
+ " which may be e.g. 10 or 20 ns, depending on the device type. See mbgctrl -?.\n\n",
+ (long) DEFAULT_POUT_PULSE_SHIFT_MIN, (long) DEFAULT_POUT_PULSE_SHIFT_MAX,
+ (long) DEFAULT_POUT_PULSE_SHIFT_MIN / 1000L, (long) DEFAULT_POUT_PULSE_SHIFT_MAX / 1000L );
+
+
+ if ( dh != MBG_INVALID_DEV_HANDLE )
+ {
+ RECEIVER_INFO ri = { 0 };
+ ALL_POUT_INFO_IDX api = { { 0 } };
+ int n_pout;
+
+ rc = check_get_pout_cfg( dh, api, &ri );
+
+ if ( mbg_rc_is_error( rc ) )
+ goto out;
+
+ n_pout = rc;
+
+ for ( i = 0; i < n_pout; i++ )
+ {
+ const POUT_INFO *pi = &api[i].pout_info;
+
+ printf( "%*s", p_ind->indent_2, str_empty );
+ printf( "Programmable output %i:\n", i );
+
+ print_bit_mask_list( "Supported modes", NULL, pi->supp_modes,
+ N_POUT_MODES, pout_mode_names_eng, NULL, 0,
+ ctrl_flags | CTRL_PRINT_IDX, p_ind_detailed );
+
+ // ### TODO evaluate more properties
+ usage_note( p_ind_detailed->indent_2, "Output level can%s be inverted.",
+ ( pi->flags & POUT_NOT_INVERTIBLE ) ? str_spc_not : str_empty );
+
+ if ( pi->flags & POUT_FIXED_PULSE_LEN )
+ usage_note( p_ind_detailed->indent_2, "Pulse length is fixed and can't be changed." );
+
+ if ( pi->flags & POUT_SUPP_PULSE_SHIFT )
+ usage_note( p_ind_detailed->indent_2, "Output supports pulse shift with resolution %u ns.",
+ pi->pulse_shift_res );
+
+ printf( "\n" );
+ }
+ }
+ else
+ {
+#if 0 // ### TODO
+ print_bit_mask_list( "modes", str_supp_by_dev, tsi.supp_scales,
+ N_MBG_TIME_SCALE, time_scale_names, NULL, ctrl_flags | CTRL_PRINT_IDX );
+#endif
+ }
+
+out:
+ return rc;
+
+} // help_pout_arg
+
+
+
+static /*HDR*/
+int eval_pout( MBG_DEV_HANDLE dh, const char *s, int inst_num )
+{
+ RECEIVER_INFO ri = { 0 };
+ ALL_POUT_INFO_IDX api = { { 0 } };
+ ALL_POUT_INFO_IDX prv_api;
+ POUT_INFO *p_pi = &api[inst_num].pout_info; // ### TODO check inst_num range?
+ POUT_SETTINGS *p_ps = &p_pi->pout_settings;
+ const char *err_info = NULL;
+ char *cp;
+ int n_pout;
+ int idx;
+ int i;
+ int rc = check_get_pout_cfg( dh, api, &ri );
+
+ if ( mbg_cond_err_msg( rc, "check_get_pout_cfg" ) )
+ return rc;
+
+ n_pout = rc; // Contains now the number of programmable pulse outputs
+
+ // save current settings
+ memcpy( prv_api, api, sizeof( prv_api ) );
+
+ // Mode
+ idx = chk_parm_name( s, pout_name_mode, &cp );
+
+ if ( mbg_rc_is_error( idx ) ) // parameter error
+ {
+ err_info = pout_name_mode;
+ goto fail;
+ }
+
+ if ( cp ) // parameter found
+ {
+ idx = atoi( cp );
+
+ if ( idx < 0 || idx >= N_POUT_MODES )
+ {
+ printf( "Programmable output mode %i out of range (0..%i)\n", idx, N_POUT_MODES - 1 );
+ goto fail;
+ }
+
+ if ( !_is_supported( idx, p_pi->supp_modes ) )
+ {
+ printf( "Programmable output mode %i (%s) not supported for output %i\n",
+ idx, _get_pout_mode_name( idx ), inst_num );
+ goto fail;
+ }
+
+ p_ps->mode = idx;
+ printf( "Programmable output mode for output %i changed to %s (%i)\n",
+ inst_num, _get_pout_mode_name( p_ps->mode ), p_ps->mode );
+ }
+
+
+ // Pulse len
+ idx = chk_parm_name( s, pout_name_len, &cp );
+
+ if ( mbg_rc_is_error( idx ) ) // parameter error
+ {
+ err_info = pout_name_len;
+ goto fail;
+ }
+
+ if ( cp ) // parameter found
+ {
+ idx = atoi( cp );
+
+ if ( !_is_supported( p_ps->mode, POUT_MODES_MODE_PARAM_AS_PULSE_LEN ) )
+ {
+ printf( "Pulse length parameter not supported for mode \"%s\"\n",
+ _get_pout_mode_name( p_ps->mode ) );
+ goto fail;
+ }
+
+ if ( idx < 0 || idx >= MAX_POUT_PULSE_LEN )
+ {
+ printf( "Pulse length %i out of range (1..%i)\n", idx, MAX_POUT_PULSE_LEN );
+ goto fail;
+ }
+
+ if ( p_pi->flags & POUT_FIXED_PULSE_LEN )
+ {
+ if ( idx != p_ps->mode_param )
+ printf( "Warning. pulse length %i (%i ms) is fix and can't be changed!\n",
+ p_ps->mode_param, p_ps->mode_param * 10 );
+ }
+ else
+ {
+ p_ps->mode_param = idx;
+ printf( "Pulse length for programmable output %i changed to %i (%i ms)\n",
+ inst_num, p_ps->mode_param, p_ps->mode_param * 10 );
+ }
+ }
+
+
+ // "Inverted" flag
+ idx = chk_parm_name( s, pout_name_inv, &cp );
+
+ if ( mbg_rc_is_error( idx ) ) // parameter error
+ {
+ err_info = pout_name_inv;
+ goto fail;
+ }
+
+ if ( cp ) // parameter found
+ {
+ idx = atoi( cp );
+
+ if ( idx < 0 || idx > 1 )
+ {
+ printf( "Invalid flag value %i for parameter %s, must be 0 or 1\n", idx, pout_name_inv );
+ goto fail;
+ }
+
+ if ( p_pi->flags & POUT_NOT_INVERTIBLE )
+ {
+ if ( idx )
+ {
+ printf( "Warning: Output level can't be inverted for output %i\n", inst_num );
+ goto fail;
+ }
+ }
+
+ if ( idx )
+ p_ps->flags |= POUT_INVERTED;
+ else
+ p_ps->flags &= ~POUT_INVERTED;
+
+ printf( "Output level for output %i%s inverted\n",
+ inst_num, idx ? str_empty : str_spc_not );
+ }
+
+
+ // "Only If Sync" flag
+ idx = chk_parm_name( s, pout_name_ois, &cp );
+
+ if ( mbg_rc_is_error( idx ) ) // parameter error
+ {
+ err_info = pout_name_ois;
+ goto fail;
+ }
+
+ if ( cp ) // parameter found
+ {
+ idx = atoi( cp );
+
+ if ( idx < 0 || idx > 1 )
+ {
+ printf( "Invalid flag value %i for parameter %s, must be 0 or 1\n", idx, pout_name_ois );
+ goto fail;
+ }
+
+ if ( !( p_pi->flags & POUT_SUPP_IF_SYNC_ONLY ) )
+ {
+ if ( idx )
+ {
+ printf( "Warning: \"Only if sync\" flag not supported for output %i\n", inst_num );
+ goto fail;
+ }
+ }
+
+ if ( idx )
+ p_ps->flags |= POUT_IF_SYNC_ONLY;
+ else
+ p_ps->flags &= ~POUT_IF_SYNC_ONLY;
+
+ printf( "\"Only if sync\" flag%s set for output %i\n",
+ idx ? str_empty : str_spc_not, inst_num );
+ }
+
+
+ // "Pulse Shift" parameter
+ idx = chk_parm_name( s, pout_name_shift, &cp );
+
+ if ( mbg_rc_is_error( idx ) ) // parameter error
+ {
+ err_info = pout_name_shift;
+ goto fail;
+ }
+
+ if ( cp ) // parameter found
+ {
+ long pulse_shift = atol( cp );
+
+ if ( !( p_pi->flags & POUT_SUPP_PULSE_SHIFT ) )
+ {
+ printf( "Warning: pulse shift not supported for output %i\n", inst_num );
+ goto fail;
+ }
+
+ if ( !_is_supported( p_ps->mode, POUT_MODES_DATA_PULSE_SHIFT ) )
+ {
+ printf( "Pulse shift not supported for mode \"%s\"\n",
+ _get_pout_mode_name( p_ps->mode ) );
+ goto fail;
+ }
+
+
+ if ( ( pulse_shift < DEFAULT_POUT_PULSE_SHIFT_MIN ) ||
+ ( pulse_shift > DEFAULT_POUT_PULSE_SHIFT_MAX ) )
+ {
+ printf( "Pulse shift %li ns out of range (%+li..%+li ns)\n", pulse_shift,
+ (long) DEFAULT_POUT_PULSE_SHIFT_MIN, (long) DEFAULT_POUT_PULSE_SHIFT_MAX );
+ goto fail;
+ }
+
+ if ( p_pi->pulse_shift_res )
+ {
+ long rem = pulse_shift % p_pi->pulse_shift_res;
+ long l = pulse_shift - rem;
+
+ #if 0 && defined( DEBUG )
+ printf( "DEBUG: s %li, rem %li, l %li\n", pulse_shift, rem, l );
+ #endif
+
+ if ( l != pulse_shift )
+ {
+ printf( "Warning: pulse shift %+li ns not appropriate for resolution %u ns, truncating to %li ns.\n",
+ pulse_shift, p_pi->pulse_shift_res, l );
+ pulse_shift = l;
+ }
+ }
+
+ p_ps->pout_data.pulse_shift = pulse_shift;
+
+ printf( "Pulse shift for programmable output %i changed to %li ns\n",
+ inst_num, (long) p_ps->pout_data.pulse_shift );
+ }
+
+
+ for ( i = 0; i < n_pout; i++ )
+ {
+ POUT_SETTINGS *p_ps = &api[i].pout_info.pout_settings;
+
+ if ( memcmp( p_ps, &prv_api[i].pout_info.pout_settings, sizeof( *p_ps ) ) )
+ {
+ // settings for this output have changed
+ rc = mbg_set_gps_pout_settings( dh, p_ps, i );
+
+ if ( mbg_rc_is_error( rc ) )
+ return rc;
+ }
+ }
+
+ return MBG_SUCCESS;
+
+fail:
+ printf( "Invalid parameter in argument" );
+
+ if ( err_info )
+ printf( " %s", err_info );
+
+ printf( "!\n" );
+
+ return MBG_ERR_CFG;
+
+} // eval_pout
+
+
+
+static /*HDR*/
+int show_pout( MBG_DEV_HANDLE dh, const OPT_HANDLER_SPEC *p_opt, const PCPS_DEV *p_devx, const char *cmd_info )
+{
+ const INDENTS *p_ind = &show_indents;
+ RECEIVER_INFO ri = { 0 };
+ ALL_POUT_INFO_IDX api = { { 0 } };
+ int i;
+ int n_pout;
+ int rc = check_get_pout_cfg( dh, api, &ri );
+
+ if ( mbg_cond_err_msg( rc, "check_get_pout_cfg" ) )
+ return rc;
+
+ n_pout = rc; // Contains now the number of programmable pulse outputs
+
+ print_indent( p_ind->indent_1 );
+ printf( "%s:", cmd_info );
+
+ if ( n_pout )
+ {
+ for ( i = 0; i < n_pout; i++ )
+ {
+ const POUT_INFO *pi = &api[i].pout_info;
+ const POUT_SETTINGS *ps = &pi->pout_settings;
+
+ // TODO: Actually the code below only shows the current mode,
+ // and whether the output signal is inverted, or not.
+ // Full featured code should also display additional parameters
+ // depending the selected mode.
+
+ printf( "\n" );
+ print_indent( p_ind->indent_2 );
+ printf( "Output %i: ", i );
+ printf( "%s", _get_pout_mode_name( ps->mode ) );
+
+ // Print pulse len, if supported by the mode
+ if ( _is_supported( ps->mode, POUT_MODES_MODE_PARAM_AS_PULSE_LEN ) )
+ {
+ // pulse len is 10 ms units, so multiply by 10 to get ms
+ printf( ", len %u ms", (unsigned int) ps->mode_param * 10 );
+
+ if ( pi->flags & POUT_FIXED_PULSE_LEN )
+ printf( " (fix)" );
+ }
+
+ // ### FIXME check more mode_param usage, times etc.
+
+ // If outputs can be inverted then this doesn't depend on the current mode
+ if ( !( pi->flags & POUT_NOT_INVERTIBLE ) )
+ printf( ", %sinverted", ( ps->flags & POUT_INVERTED ) ? str_empty : str_not_spc );
+
+
+
+ if ( _is_supported( ps->mode, POUT_MODES_SUPP_IF_SYNC_ONLY ) )
+ {
+ char ws[80];
+ const char *cp = NULL;
+
+ if ( _is_supported( ps->mode, POUT_MODES_TIMEOUT ) &&
+ ( ( pi->flags & POUT_SUPP_IF_SYNC_ONLY ) == 0 ) &&
+ ( ps->timeout != 0 ) )
+ {
+ snprintf_safe( ws, sizeof( ws ), "after timeout %i min ", ps->timeout );
+ cp = ws;
+ }
+
+ if ( pi->flags & POUT_SUPP_IF_SYNC_ONLY )
+ printf( ", %sdisabled %sif sync. lost",
+ ( ps->flags & POUT_IF_SYNC_ONLY ) ? str_empty : str_not_spc,
+ cp ? cp : str_empty );
+ }
+
+
+
+
+ if ( pi->flags & POUT_SUPP_PULSE_SHIFT )
+ if ( _is_supported( ps->mode, POUT_MODES_DATA_PULSE_SHIFT ) )
+ printf( ", shift %li ns, res. %li ns", (long) ps->pout_data.pulse_shift, (long) pi->pulse_shift_res );
+ }
+ }
+ else
+ printf( str_spc_not_supp );
+
+ printf( "\n" );
+
+ return MBG_SUCCESS;
+
+} // show_pout
+
+
+
+static /*HDR*/
+void printf_ef( uint16_t flag, const char *info )
+{
+ printf( "%s:%u", info, ( flag == EF_OFF ) ? 0 : 1 );
+
+} // printf_ef
+
+
+
+static /*HDR*/
+int show_enable_flags( MBG_DEV_HANDLE dh, const PCPS_DEV *p_dev, const char *info )
+{
+ ENABLE_FLAGS ef;
+ int rc;
+
+ rc = mbg_get_gps_enable_flags( dh, &ef );
+
+ if ( mbg_cond_err_msg( rc, "mbg_get_gps_enable_flags" ) )
+ return rc;
+
+ printf( "%s enable flags: ", info );
+ printf_ef( ef.serial, ef_name_serial );
+ printf( "," );
+ printf_ef( ef.pulses, ef_name_pulses );
+
+ if ( _pcps_has_synth( p_dev ) )
+ {
+ printf( "," );
+ printf_ef( ef.synth, ef_name_synth );
+ }
+
+ printf( "\n" );
+
+ return MBG_SUCCESS;
+
+} // show_enable_flags
+
+
+
+static /*HDR*/
+int ef_check_parm( const char *s, EF_INFO *p )
+{
+ int l = strlen( p->name );
+
+ if ( strncmp( s, p->name, l ) != 0 )
+ return 0; // parameter does not match
+
+ if ( s[l] != ':' )
+ goto fail; // parameter syntax error: name not followed by colon
+
+ l++;
+
+ if ( s[l] != '0' && s[l] != '1' )
+ goto fail; // parameter syntax error: colon not followed by '0' or '1'
+
+ *p->flags = ( s[l] == '0' ) ? EF_OFF : p->on_flags;
+
+ l++;
+
+ return l;
+
+
+fail:
+ return MBG_ERR_CFG;
+
+} // ef_check_parm
+
+
+
+static /*HDR*/
+int set_enable_flags( MBG_DEV_HANDLE dh, const char *arg, const PCPS_DEV *p_dev )
+{
+ ENABLE_FLAGS ef;
+
+ EF_INFO ef_parms[N_EF_INFO] =
+ {
+ { ef_name_serial, &ef.serial, EF_SERIAL_BOTH },
+ { ef_name_pulses, &ef.pulses, EF_PULSES_BOTH },
+ { ef_name_synth, &ef.synth, EF_SYNTH }
+ };
+
+ int rc;
+
+ rc = mbg_get_gps_enable_flags( dh, &ef );
+
+ if ( mbg_cond_err_msg( rc, "mbg_get_gps_enable_flags" ) )
+ return rc;
+
+ // Scan input parameters
+ for (;;)
+ {
+ int i;
+
+ for ( i = 0; i < N_EF_INFO; i++ )
+ {
+ rc = ef_check_parm( arg, &ef_parms[i] );
+
+ if ( rc == 0 ) // check next
+ continue;
+
+ if ( rc < 0 ) // error
+ return rc;
+
+ arg += rc;
+
+ if ( *arg == 0 ) // done
+ goto save;
+
+ if ( *arg != ',' )
+ return MBG_ERR_CFG; // invalid parameter or parameter syntax error
+
+ arg++;
+ }
+ }
+
+save:
+ rc = mbg_set_gps_enable_flags( dh, &ef );
+
+ if ( mbg_cond_err_msg( rc, "mbg_set_gps_enable_flags" ) )
+ return rc;
+
+ return MBG_SUCCESS;
+
+} // set_enable_flags
+
+
+
+static /*HDR*/
+int send_gps_cmd( MBG_DEV_HANDLE dh, ushort cmd )
+{
+ int rc = mbg_set_gps_cmd( dh, &cmd );
+
+ if ( mbg_cond_err_msg( rc, "mbg_set_gps_cmd" ) )
+ return rc;
+
+ printf( "NOTE: the command code %u has been sent to the GPS clock.\n", cmd );
+
+ return MBG_SUCCESS;
+
+} // send_gps_cmd
+
+
+
+static /*HDR*/
+int show_ant_cable_len( MBG_DEV_HANDLE dh, const char *info )
+{
+ ANT_CABLE_LEN len;
+ int rc = mbg_get_gps_ant_cable_len( dh, &len );
+
+ if ( mbg_cond_err_msg( rc, "mbg_get_gps_ant_cable_len" ) )
+ return rc;
+
+ printf( "%s antenna cable length: %u meter(s)", info, len );
+
+ return MBG_SUCCESS;
+
+} // show_ant_cable_len
+
+
+
+static /*HDR*/
+int set_ant_cable_len( MBG_DEV_HANDLE dh, const char *s )
+{
+ ANT_CABLE_LEN len = (ANT_CABLE_LEN) atol( s ); // TODO check range ?
+ int rc = mbg_set_gps_ant_cable_len( dh, &len );
+
+ if ( mbg_cond_err_msg( rc, "mbg_set_gps_ant_cable_len" ) )
+ return rc;
+
+ return MBG_SUCCESS;
+
+} // set_ant_cable_len
+
+
+
+static /*HDR*/
+int set_event_time( MBG_DEV_HANDLE dh, const char *s )
+{
+ char ws[80];
+ time_t event_time;
+ PCPS_TIME_STAMP event_ts;
+ int rc;
+
+ // set event at current system time + number of seconds
+ event_time = time( NULL ) + strtol( s, NULL, 10 );
+ event_ts.sec = (uint32_t) event_time; // Unix UTC seconds // TODO: check range / conversion
+ event_ts.frac = 0; // fraction of second, 0xFFFFFFFF == 0.99.. sec
+
+ rc = mbg_set_event_time( dh, &event_ts );
+
+ if ( mbg_cond_err_msg( rc, "mbg_set_event_time" ) )
+ return rc;
+
+ mbg_snprint_hr_tstamp( ws, sizeof( ws ), &event_ts, 0, 0 ); // raw timestamp?
+ printf( "Event time set to UTC %s\n", ws );
+
+ return MBG_SUCCESS;
+
+} // set_event_time
+
+
+
+static /*HDR*/
+int get_n_time_scale( MBG_DEV_HANDLE dh, MBG_TIME_SCALE_INFO *p_tsci )
+{
+ MBG_TIME_SCALE_INFO tsci = { { 0 } };
+ int rc;
+
+ rc = mbg_chk_dev_has_time_scale( dh );
+
+ if ( mbg_rc_is_error( rc ) )
+ {
+ if ( rc == MBG_ERR_NOT_SUPP_BY_DEV )
+ printf( "This device does not support a configurable time scale.\n" );
+ else
+ mbg_cond_err_msg( rc, "mbg_chk_dev_has_time_scale" );
+
+ goto done;
+ }
+
+ rc = mbg_get_time_scale_info( dh, &tsci );
+
+ if ( rc != MBG_SUCCESS )
+ goto done;
+
+ if ( p_tsci )
+ *p_tsci = tsci;
+
+done:
+ return tsci.max_settings.scale;
+
+} // get_n_time_scale
+
+
+
+static /*HDR*/
+int show_time_scale( MBG_DEV_HANDLE dh, const PCPS_DEV *p_dev, const char *info )
+{
+ MBG_TIME_SCALE_INFO tsci = { { 0 } };
+ int rc;
+
+ rc = mbg_get_time_scale_info( dh, &tsci );
+
+ if ( mbg_cond_err_msg( rc, "mbg_get_time_scale_info" ) )
+ return rc;
+
+ printf( "%s time scale index: %u (%s)", info, tsci.settings.scale,
+ _get_time_scale_name( tsci.settings.scale ) );
+
+ printf( "\n" );
+
+ return MBG_SUCCESS;
+
+} // show_time_scale
+
+
+
+static /*HDR*/
+int set_time_scale( MBG_DEV_HANDLE dh, const char *arg, const PCPS_DEV *p_dev )
+{
+ MBG_TIME_SCALE_INFO tsci = { { 0 } };
+ int rc;
+
+ rc = mbg_get_time_scale_info( dh, &tsci );
+
+ if ( mbg_cond_err_msg( rc, "mbg_get_time_scale_info" ) )
+ return rc;
+
+
+ tsci.settings.scale = atoi( arg );
+
+ if ( tsci.settings.scale >= tsci.max_settings.scale )
+ {
+ printf( "*** Error: new time scale index (%u) out of range (0..%u)\n",
+ tsci.settings.scale, tsci.max_settings.scale - 1 );
+ return MBG_ERR_CFG;
+ }
+
+ if ( !( tsci.supp_scales & ( 1UL << tsci.settings.scale ) ) )
+ {
+ printf( "*** Warning: new time scale index (%u) not supported by device\n",
+ tsci.settings.scale );
+ return MBG_ERR_CFG;
+ }
+
+ rc = mbg_set_time_scale_settings( dh, &tsci.settings );
+
+ if ( mbg_cond_err_msg( rc, "mbg_set_time_scale_settings" ) )
+ return rc;
+
+ return MBG_SUCCESS;
+
+} // set_time_scale
+
+
+
+static /*HDR*/
+void usage( MBG_DEV_HANDLE dh, PCPS_DEV *p_dev, RECEIVER_INFO *p_ri,
+ RECEIVER_PORT_CFG *p_rpcfg )
+{
+ int i;
+ int n_time_scale;
+
+ if ( p_dev )
+ check_get_receiver_port_cfg( dh, p_rpcfg, p_dev, p_ri );
+
+ printf( "Usage: %s cmd [dev]\n"
+ "\n"
+ "where cmd can be one of the following:\n"
+ " BOOT set GPS clock to boot mode\n"
+ " GPSPOS=x.x,y.y,z set GPS position to (lat,lon,alt)\n",
+ pname
+ );
+
+
+ printf( " DATE=yyyy-mm-dd set on-board date to year, month, day-of-month\n"
+ " TIME=hh:mm:ss.hh set on-board time to hours, mins, secs, hundredths\n"
+ );
+
+
+ printf( " TIMESCALE show clock's time scale setting, if supported\n"
+ " TIMESCALE=<n> set clock's time scale to scale with index <n>\n"
+ );
+
+ n_time_scale = p_dev ? get_n_time_scale( dh, NULL ) : N_MBG_TIME_SCALE;
+
+ if ( n_time_scale )
+ {
+ printf( " Known time scales:\n" );
+ for ( i = 0; i < n_time_scale; i++ )
+ {
+ printf( " %u: %s", i, _get_time_scale_name( i ) );
+
+ if ( i == 0 )
+ printf( " (default)" );
+
+ printf( "\n" );
+ }
+ }
+ else
+ printf( " (Configurable time scales not supported by this device)\n" );
+
+
+ printf( " TZOFFS show clock's time zone offset, if supported\n"
+ " TZOFFS=<n> set clock's basic time zone to UTC with additional offset, in seconds\n"
+ );
+
+ printf( " TZ=UTC set time zone code to %s\n"
+ " TZ=CET set time zone code to %s\n"
+ " TZ=EET set time zone code to %s\n",
+ tz_info_utc,
+ tz_info_cet_cest,
+ tz_info_eet_eest
+ );
+
+
+ printf( " COMPARM show clock's COM port parameters\n"
+ " COM<n>=<bd>,<f>[,<t>[,<m>]] set parameters of clock's port COM<n>"
+ );
+
+ printf( ", where\n"
+ " <bd> is one of:" );
+ for ( i = 0; i < N_MBG_BAUD_RATES; i++ )
+ printf( " %s", mbg_baud_str[i] );
+
+ printf( "\n"
+ " <f> is one of:" );
+ for ( i = 0; i < N_MBG_FRAMINGS; i++ )
+ printf( " %s", mbg_framing_str[i] );
+
+ printf( "\n <t> optional time string format index" );
+
+ if ( p_dev )
+ {
+ printf( ":" );
+
+ for ( i = 0; i < p_ri->n_str_type; i ++ )
+ {
+ const STR_TYPE_INFO *p_sti = &p_rpcfg->stii[i].str_type_info;
+
+ printf( "\n %u: %s", i, p_sti->long_name );
+ }
+ }
+
+ printf( "\n <m> optional time string mode index" );
+
+ if ( p_dev )
+ {
+ printf( ":" );
+
+ for ( i = 0; i < N_STR_MODE; i ++ )
+ printf( "\n %u: %s", i, mode_names[i] );
+
+ printf( "\n"
+ " Please note not each baud rate, framing, string format and mode\n"
+ " must necessarily be supported by every individual serial port.\n" );
+ }
+
+ printf( "\n" );
+
+ help_pout_arg( dh, p_dev, &ohs_pout, 0 );
+
+ printf( "\n" );
+
+ printf( " EF show clock's enable flags\n"
+ " EF=SERIAL:0,PULSES:1,SYNTH:0 modify clock's enable flags\n"
+ "\n"
+ " SERIAL controls the serial port output\n"
+ " PULSES controls pulse outputs and IRIG timecode output, if available\n"
+ " SYNTH controls a programmable synthesizer output, if available\n"
+ "\n"
+ " 0 or 1 controls when the corresponding output signals are enabled:\n"
+ " 0: only after the card has synchronized to its incoming signal\n"
+ " 1: immediately after power-up\n"
+ "\n"
+ " Please note that not every device supports enable flags.\n"
+ "\n"
+ );
+
+ printf( " LAN show board's LAN interface settings\n"
+ " LAN=DHCP set LAN interface to be configured via DHCP\n"
+ " LAN=IP:<ip>[,NM:<nm>][,BA:<ba>][,GW:<gw>] set LAN interface to given static settings\n"
+ "\n"
+ " where each of <ip>,<nm>,<ba>,<gw> is an IPv4 address of the form aaa.bbb.ccc.ddd, e.g.:\n"
+ " IP:192.168.1.115 specifies the IP address\n"
+ " NM:255.255.255.0 specifies the net mask\n"
+ " BA:192.168.1.255 specifies the broadcast address\n"
+ " GW:192.168.1.1 specifies the default gateway\n"
+ "\n"
+ " If no broadcast address is specified then the default broadcast address\n"
+ " is computed from the IP address and the network mask.\n"
+ "\n"
+ " Please note that not every device provides a LAN interface.\n"
+ " Also, the IP values above are examples only. The real values to be used\n"
+ " depend on the settings of the network to which the card is attached.\n"
+ "\n"
+ );
+
+ printf( " PTP show board's PTP settings\n"
+ " PTP=NP:<np>[,DM:<dm>][,DO:<do>][,HW:<hw>] set general PTP protocol parameters\n"
+ "\n"
+ " where, e.g. :\n"
+ " NP:IP4 specifies UDP/IPv4 (Layer 3) as network protocol\n"
+ " NP:ETH specifies IEEE 802.3 (Layer 2) as network protocol\n"
+ " DM:E2E specifies end-to-end delay mechanism\n"
+ " DM:P2P specifies peer-to-peer delay mechanism\n"
+ " DO:0 specifies PTP domain number [0..255]\n"
+ " HW:1 specifies if the \"v1 hardware compatibility flag\" shall be set ('1') or disabled ('0')\n"
+ " (this is usually not required)\n"
+ "\n"
+ );
+
+ printf( " If the PTP device supports unicast slave mode then the following parameters\n"
+ " can be specified to configure a unicast master to be queried:\n"
+ " PTP=ROLE:<rl>[,GMIP:<ip>][,GMID:<id>][,PID:<po>][,SMI:<sr>][,AMI:<ar>][,DRI:<dr>][,DUR:<du>] set PTP unicast parameters\n"
+ "\n"
+ " where, e.g.:\n"
+ " ROLE:MCS specifies \"Multicast Slave\" PTP role\n"
+ " ROLE:UCS specifies \"Unicast Slave\" PTP role\n"
+ " GMIP:192.168.1.115 specifies the IP address of the grandmaster\n"
+ " GMID:FF:FF:FF:FF:FF:FF:FF:FF specifies the Clock ID of the grandmaster, or '*' as wildcard for all 'FF'\n"
+ " PID:1 specifies the target Port ID of the grandmaster port to be used, or '*' for wildcard\n"
+ " SMI:0 specifies the Sync Message Interval requested from the grandmaster in 2^x seconds [-6..6]\n"
+ " AMI:1 specifies the Announce Message Interval requested from the grandmaster in 2^x seconds [-6..6]\n"
+ " DRI:1 specifies the Delay Request Interval requested from the grandmaster in 2^x seconds [-6..6]\n"
+ " DUR:300 specifies the duration in seconds how long the master shall send messages to the slave until a timeout or renewal occurs\n"
+ "\n"
+ );
+
+ printf( " ANT_CABLE_LEN show the configured antenna cable length\n"
+ " ANT_CABLE_LEN=<len> set the antenna cable length to be compensated to <len>, in meters.\n"
+ "\n"
+ " Please note note this parameter is only supported with cards which provide\n"
+ " compensation of the antenna cable signal propagation delay, i.e. GPS cards.\n"
+ "\n"
+ );
+
+ if ( p_dev )
+ {
+ if ( _pcps_has_event_time( p_dev ) )
+ printf( " EVENT=<n> set event time to system time + <n> seconds\n" );
+ }
+
+} // usage
+
+
+
+static /*HDR*/
+const char *str_parm_p( const char *s, const char *keyword )
+{
+ char *match = strstr( s, keyword );
+
+ if ( match && ( match == s ) )
+ return &s[strlen( keyword )];
+ else
+ return NULL;
+
+} // str_parm_p
+
+
+
+static /*HDR*/
+int check_cmd_line( int argc, char *argv[], MBG_DEV_HANDLE dh, const PCPS_DEV *p_dev,
+ RECEIVER_INFO *p_ri, RECEIVER_PORT_CFG *p_rpcfg )
+{
+ const char *sdate = NULL;
+ const char *stime = NULL;
+ const char *cp;
+ int i;
+ int rc = MBG_SUCCESS;
+ int error_occurred = 0;
+
+ must_print_usage = 0;
+
+ for ( i = 1; i < argc; i++ )
+ {
+ if ( mbg_rc_is_error( rc ) )
+ error_occurred = 1;
+
+ if ( rc == RC_USAGE )
+ must_print_usage = 1;
+
+ rc = MBG_SUCCESS;
+
+ if ( ( strcmp( argv[i], "-?" ) == 0 ) ||
+ ( strcmp( argv[i], "-h" ) == 0 ) ||
+ ( strcmp( argv[i], "--help" ) == 0 ) )
+ {
+ must_print_usage = 1;
+ continue;
+ }
+
+ if ( strcmp( argv[i], "BOOT" ) == 0 )
+ {
+ if ( p_dev )
+ {
+ if ( _pcps_is_gps( p_dev ) )
+ rc = send_gps_cmd( dh, PC_GPS_CMD_BOOT );
+ else
+ err_msg( p_dev, no_gps_cmd );
+ }
+ continue;
+ }
+
+
+ cp = str_parm_p( argv[i], "TZ=" );
+
+ if ( cp )
+ {
+ if ( p_dev )
+ rc = set_timezone( dh, cp, p_dev );
+
+ continue;
+ }
+
+
+ cp = str_parm_p( argv[i], "GPSPOS=" );
+
+ if ( cp )
+ {
+ if ( p_dev )
+ {
+ if ( _pcps_is_gps( p_dev ) )
+ rc = set_gps_pos( dh, cp );
+ else
+ err_msg( p_dev, no_gps_cmd );
+ }
+ continue;
+ }
+
+
+ cp = str_parm_p( argv[i], "DATE=" );
+
+ if ( cp )
+ {
+ sdate = cp;
+ continue;
+ }
+
+
+ cp = str_parm_p( argv[i], "TIME=" );
+
+ if ( cp )
+ {
+ stime = cp;
+ continue;
+ }
+
+
+ cp = str_parm_p( argv[i], "COMPARM" );
+
+ if ( cp )
+ {
+ if ( p_dev )
+ {
+ int rc = check_get_receiver_port_cfg( dh, p_rpcfg, p_dev, p_ri );
+
+ if ( mbg_rc_is_error( rc ) )
+ {
+ // ### TODO print another error?
+ continue;
+ }
+
+ if ( p_ri->n_com_ports )
+ {
+ if ( *cp != 0 )
+ {
+ printf( "** Invalid parameter: %s\n", argv[i] );
+ must_print_usage = 1;
+ continue;
+ }
+
+ show_serial_settings( dh, p_rpcfg, p_ri );
+ }
+ else
+ printf( "** This device does not provide a configurable COM port.\n" );
+ }
+
+ continue;
+ }
+
+
+ cp = str_parm_p( argv[i], "COM" );
+
+ if ( cp )
+ {
+ if ( p_dev )
+ {
+ char *p_tail;
+ ulong port_num;
+
+ rc = check_get_receiver_port_cfg( dh, p_rpcfg, p_dev, p_ri );
+
+ if ( mbg_rc_is_error( rc ) )
+ {
+ // ### TODO print another error?
+ continue;
+ }
+
+ port_num = strtoul( cp, &p_tail, 10 );
+
+ if ( p_tail == cp ) // no COM port number specified
+ {
+ printf( "** Invalid COM port specification: %s\n", argv[i] );
+ must_print_usage = 1;
+ continue;
+ }
+
+ cp = p_tail;
+
+ if ( port_num >= (ulong) p_ri->n_com_ports )
+ {
+ printf( "** COM port number %lu exceeds maximum %u\n",
+ port_num, p_ri->n_com_ports );
+ must_print_usage = 1;
+ continue;
+ }
+
+ if ( *cp != '=' )
+ {
+ printf( "** Invalid COM port specification: %s\n", argv[i] );
+ must_print_usage = 1;
+ continue;
+ }
+
+ rc = save_serial_settings( dh, (unsigned) port_num, ++cp, p_dev, p_ri );
+ }
+ continue;
+ }
+
+
+ cp = str_parm_p( argv[i], "POUT" );
+
+ if ( cp )
+ {
+ if ( p_dev )
+ {
+ ulong n_pout;
+ ulong pout_num;
+ char *p_tail;
+
+ check_setup_receiver_info( dh, p_ri );
+ n_pout = p_ri->n_prg_out;
+
+ if ( n_pout == 0 )
+ {
+ printf( "Programmable outputs not supported!\n" );
+ continue;
+ }
+
+ pout_num = strtoul( cp, &p_tail, 10 );
+
+ if ( p_tail == cp ) // no POUT number specified
+ {
+ show_pout( dh, &ohs_pout, p_dev, "Current programmable outputs settings" );
+ continue;
+ }
+
+ cp = p_tail;
+
+ if ( pout_num >= n_pout )
+ {
+ printf( "** Programmable output number %lu exceeds maximum %lu\n",
+ pout_num, n_pout );
+ must_print_usage = 1;
+ continue;
+ }
+
+ if ( *cp != '=' )
+ {
+ printf( "** Invalid programmable output specification: %s\n", argv[i] );
+ must_print_usage = 1;
+ continue;
+ }
+
+ rc = eval_pout( dh, ++cp, (unsigned) pout_num );
+ printf( "\n" );
+ show_pout( dh, &ohs_pout, p_dev, "Current programmable outputs settings" );
+ }
+ continue;
+ }
+
+
+ cp = str_parm_p( argv[i], "EF" );
+
+ if ( cp )
+ {
+ if ( p_dev )
+ {
+ char *info = "Current";
+
+ if ( !_pcps_has_gps_data( p_dev ) )
+ {
+ err_msg( p_dev, no_enable_flags );
+ continue;
+ }
+
+ if ( *cp == '=' )
+ {
+ rc = set_enable_flags( dh, ++cp, p_dev );
+
+ if ( mbg_rc_is_error( rc ) )
+ {
+ printf( "*** Warning: invalid enable flag parameter syntax\n" );
+ must_print_usage = 1;
+ continue;
+ }
+
+ info = "New";
+ }
+
+ show_enable_flags( dh, p_dev, info );
+ }
+ continue;
+ }
+
+
+ cp = str_parm_p( argv[i], "TIMESCALE" );
+
+ if ( cp )
+ {
+ if ( p_dev )
+ {
+ char *info = "Current";
+
+ if ( !_pcps_has_time_scale( p_dev ) )
+ {
+ err_msg( p_dev, no_time_scale );
+ continue;
+ }
+
+ if ( *cp == '=' )
+ {
+ rc = set_time_scale( dh, ++cp, p_dev );
+
+ if ( mbg_rc_is_error( rc ) )
+ {
+ must_print_usage = 1;
+ continue;
+ }
+
+ info = "New";
+ }
+
+ show_time_scale( dh, p_dev, info );
+ }
+ continue;
+ }
+
+
+ cp = str_parm_p( argv[i], "TZOFFS" );
+
+ if ( cp )
+ {
+ if ( p_dev )
+ {
+ char *info = "Current";
+
+ if ( !_pcps_has_tzdl( p_dev ) )
+ {
+ err_msg( p_dev, no_tzdl );
+ continue;
+ }
+
+ if ( *cp == '=' )
+ {
+ rc = set_tzdl_offs( dh, ++cp );
+
+ if ( mbg_rc_is_error( rc ) )
+ {
+ must_print_usage = 1;
+ continue;
+ }
+
+ info = "New";
+ }
+
+ show_tzdl_offs( dh, info );
+ }
+ continue;
+ }
+
+
+ cp = str_parm_p( argv[i], "SYNTH" );
+
+ if ( cp )
+ {
+ if ( p_dev )
+ {
+ char *info = "Current";
+
+ if ( !_pcps_has_synth( p_dev ) )
+ {
+ err_msg( p_dev, no_synth );
+ continue;
+ }
+
+ if ( *cp == '=' )
+ {
+ rc = set_synth( dh, ++cp );
+
+ if ( mbg_rc_is_error( rc ) )
+ {
+ must_print_usage = 1;
+ continue;
+ }
+
+ info = "New";
+ }
+
+ show_synth( dh, info );
+ }
+ continue;
+ }
+
+
+ cp = str_parm_p( argv[i], "LAN" );
+
+ if ( cp )
+ {
+ if ( p_dev )
+ {
+ char *info = "Current";
+
+ if ( !_pcps_has_lan_intf( p_dev ) )
+ {
+ err_msg( p_dev, no_lan_intf );
+ continue;
+ }
+
+ if ( *cp == '=' )
+ {
+ rc = set_lan_intf( dh, ++cp, p_dev );
+
+ if ( mbg_rc_is_error( rc ) )
+ {
+ must_print_usage = 1;
+ continue;
+ }
+
+ info = "New";
+ }
+
+ show_lan_intf( dh, p_dev, info );
+ }
+ continue;
+ }
+
+
+ cp = str_parm_p( argv[i], "PTP" );
+
+ if ( cp )
+ {
+ if ( p_dev )
+ {
+ char *info = "Current";
+
+ if ( !_pcps_has_ptp( p_dev ) )
+ {
+ err_msg( p_dev, no_ptp );
+ continue;
+ }
+
+ if ( *cp == '=' )
+ {
+ rc = set_ptp_cfg( dh, ++cp, p_dev );
+
+ if ( mbg_rc_is_error( rc ) )
+ continue;
+
+ info = "New";
+ }
+
+ show_ptp_cfg( dh, p_dev, info );
+ }
+ continue;
+ }
+
+ cp = str_parm_p( argv[i], "ANT_CABLE_LEN" );
+
+ if ( cp )
+ {
+ if ( p_dev )
+ {
+ char *info = "Current";
+
+ if ( !_pcps_has_cab_len( p_dev ) )
+ {
+ err_msg( p_dev, no_cab_len );
+ continue;
+ }
+
+ if ( *cp == '=' )
+ {
+ rc = set_ant_cable_len( dh, ++cp );
+
+ if ( mbg_rc_is_error( rc ) )
+ {
+ must_print_usage = 1;
+ continue;
+ }
+
+ info = "New";
+ }
+
+ show_ant_cable_len( dh, info );
+ }
+ continue;
+ }
+
+
+ cp = str_parm_p( argv[i], "EVENT" );
+
+ if ( cp )
+ {
+ if ( p_dev )
+ {
+ if ( _pcps_has_event_time( p_dev ) )
+ {
+ if ( *cp == '=' )
+ rc = set_event_time( dh, ++cp );
+ else
+ rc = MBG_ERR_PARM_FMT;
+
+ if ( mbg_rc_is_error( rc ) )
+ printf( "*** Warning: invalid event time parameter\n" );
+ }
+ else
+ err_msg( p_dev, no_event_time );
+ }
+ continue;
+ }
+
+ if ( dev_name == NULL )
+ dev_name = argv[i];
+
+#if 0 //##++
+ else
+ {
+ printf( "** Unknown command: %s\n", argv[i] );
+ must_print_usage = 1;
+ }
+#endif
+
+ }
+
+ if ( p_dev )
+ if ( sdate || stime )
+ {
+ rc = set_date_time( dh, p_dev, sdate, stime );
+
+ if ( mbg_rc_is_error( rc ) )
+ error_occurred = 1;
+ }
+
+ if ( error_occurred )
+ return MBG_ERR_GENERIC;
+
+ if ( must_print_usage )
+ return RC_USAGE;
+
+ return MBG_SUCCESS;
+
+} // check_cmd_line
+
+
+
+int main( int argc, char *argv[] )
+{
+ RECEIVER_PORT_CFG rpcfg = { { { 0 } } };
+ RECEIVER_INFO ri = { 0 };
+ PCPS_DEV dev = { { 0 } };
+ PCPS_DEV *pdev = NULL;
+ MBG_DEV_HANDLE dh = MBG_INVALID_DEV_HANDLE;
+ MBG_DEV_FN tmp_dev_fn;
+ int rc;
+
+ mbg_print_program_info( pname, MBG_MICRO_VERSION, MBG_FIRST_COPYRIGHT_YEAR, MBG_LAST_COPYRIGHT_YEAR );
+
+ // If no arguments have been given, just print usage.
+ if ( argc < 2 )
+ goto show_usage;
+
+ // Check the command line for a device name, but don't
+ // pass a device handle, so commands are not evaluated.
+ check_cmd_line( argc, argv, dh, NULL, &ri, &rpcfg );
+
+ rc = mbg_open_device_by_param( &dh, dev_name, 0, 0,
+ tmp_dev_fn, sizeof( tmp_dev_fn ), 0 );
+
+ if ( mbg_rc_is_error( rc ) )
+ goto fail;
+
+
+ // get information about the device
+ rc = mbg_get_show_dev_info( dh, NULL, &dev );
+
+ if ( mbg_rc_is_error( rc ) )
+ goto fail;
+
+
+ pdev = &dev;
+
+ rc = check_cmd_line( argc, argv, dh, pdev, &ri, &rpcfg );
+
+ if ( rc < 0 )
+ goto fail;
+
+ if ( rc > 0 )
+ goto show_usage;
+
+ printf( "\n" );
+
+ mbg_close_device( &dh );
+
+ return MBG_EXIT_CODE_SUCCESS;
+
+
+show_usage:
+ usage( dh, pdev, &ri, &rpcfg );
+ return MBG_EXIT_CODE_USAGE;
+
+fail:
+ return MBG_EXIT_CODE_FAIL;
+}