diff options
author | Martin Burnicki <martin.burnicki@meinberg.de> | 2017-08-11 12:00:00 +0200 |
---|---|---|
committer | Martin Burnicki <martin.burnicki@meinberg.de> | 2017-08-11 12:00:00 +0200 |
commit | 3743722bf58ea2819f6c81974d75baa295562572 (patch) | |
tree | 06e9a3e9ffedc4157f10758a62e12ad61704146c /mbgctrl | |
parent | bd8c46abdb579b8c7871b9ff22a2555c2211e110 (diff) | |
download | mbgtools-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-x | mbgctrl/Makefile | 50 | ||||
-rwxr-xr-x | mbgctrl/mbgctrl.c | 3440 |
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; +} |