Newer
Older
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini
*
* Released to the public domain
*/
/*
* This is the startup thing for hosted environments. It
* defines main and then calls the main loop.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <minipc.h>
#include <ppsi/ppsi.h>
#include <libwr/shmem.h>
# define WRSW_HAL_RETRIES 1000
#define WRSW_HAL_TIMEOUT 2000000 /* us */
const struct wrh_operations wrh_oper = {
.locking_enable = wrs_locking_enable,
.locking_poll = wrs_locking_poll,
.locking_disable = wrs_locking_disable,
.locking_reset = wrs_locking_reset,
.enable_ptracker = wrs_enable_ptracker,
.adjust_in_progress = wrs_adjust_in_progress,
.adjust_counters = wrs_adjust_counters,
.adjust_phase = wrs_adjust_phase,
.get_clock_period = wrs_get_clock_period,
.set_timing_mode= wrs_set_timing_mode,
.get_timing_mode= wrs_get_timing_mode,
.get_timing_mode_state= wrs_get_timing_mode_state,
struct minipc_ch *hal_ch;
struct minipc_ch *ppsi_ch;
struct hal_port_state *hal_ports;
struct wrs_shm_head *ppsi_head;
extern struct pp_ext_hooks pp_hooks;
static int set_extension(struct pp_instance *ppi, int extension);
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
static int init_extensions(struct pp_instance *ppi)
{
struct pp_ext_mem *ppi_ext_data;
if (!(ppi_ext_data =
wrs_shm_alloc(ppsi_head,
sizeof(struct pp_ext_mem) * (PPSI_EXT_N)))
) {
return -1;
}
/* Clean the allocated memory */
memset(ppi_ext_data, 0, sizeof(struct pp_ext_mem) * PPSI_EXT_N);
ppi->ext_mem = ppi_ext_data;
#if CONFIG_HAS_EXT_WR
/* Add WR extension portDS */
if (!(ppi_ext_data[PPSI_EXT_WR].ext_dsport =
wrs_shm_alloc(ppsi_head, sizeof(struct wr_dsport)))
) {
return -1;
}
/* Allocate WR data extension */
if (!(ppi_ext_data[PPSI_EXT_WR].ext_data =
wrs_shm_alloc(ppsi_head, sizeof(struct wr_data)))
) {
return -1;
}
/* Set WR extension hooks */
ppi_ext_data[PPSI_EXT_WR].ext_hooks = &wr_ext_hooks;
#endif
if (!(ppi_ext_data[PPSI_EXT_L1S].ext_dsport =
wrs_shm_alloc(ppsi_head, sizeof(l1e_ext_portDS_t)))
) {
return -1;
}
/* Allocate L1SYNC data extension */
if (!(ppi_ext_data[PPSI_EXT_L1S].ext_data =
wrs_shm_alloc(ppsi_head,sizeof(struct l1e_data)))
) {
return -1;
}
/* Set L1SYNC extension hooks */
ppi_ext_data[PPSI_EXT_L1S].ext_hooks = &l1e_ext_hooks;
#endif
return 0;
}
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/* Should be called when the extension is changed in runtime */
static int set_extension_runtime_init(struct pp_instance *ppi)
{
int tmo;
ppi->pdstate = PP_PDSTATE_NONE;
/* Default value for PTP. Can be overwritten in specific init */
ppi->extState =
(ppi->protocol_extension == PPSI_EXT_NONE)
? PP_EXSTATE_DISABLE : PP_EXSTATE_ACTIVE;
if (is_ext_hook_available(ppi, init)
&& ppi->ext_hooks->init(ppi, NULL, 0)
) {
pp_diag(ppi, ext, 1, "%s: can't init profile\n",
__func__);
return 1;
}
/* WRPC 4.2 and possibly old WRS FW does not send delay_req/delay_resp
* while locking the PLL. Due to that extension setup can timeout. */
tmo = is_ext_hook_available(ppi,get_tmo_lstate_detection) ?
(*ppi->ext_hooks->get_tmo_lstate_detection)(ppi)
: 20000; // 20s per default
pp_timeout_set(ppi, PP_TO_PROT_STATE, tmo);
if (ppi->state == PPS_SLAVE) {
/* When the extension/profile is changed the servo is not reset
* nor initialized. Initialization of servo (wrh_servo_init) is
* done via new_slave */
if (is_ext_hook_available(ppi, new_slave))
ppi->ext_hooks->new_slave(ppi, NULL, 0);
else
pp_servo_init(ppi);
}
return 0;
}
int set_extension_runtime(struct pp_instance *ppi, int extension)
{
if (set_extension(ppi, extension) < 0) {
/* Error */
return -1;
}
/* Initialize set extenstion */
return set_extension_runtime_init(ppi);
}
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
int set_extension_precheck(struct pp_instance *ppi, int extension)
{
int profile = ppi->cfg.profile;
if (extension == PPSI_EXT_NONE)
return 0;
if (extension == PPSI_EXT_WR) {
if (!CONFIG_HAS_EXT_WR)
return -1;
if (profile == PPSI_PROFILE_HA_WR
|| profile == PPSI_PROFILE_CUSTOM) {
/* Extension is supported for the selected profile */
return 0;
}
return -2;
}
if (extension == PPSI_EXT_L1S) {
if (!CONFIG_HAS_EXT_L1SYNC)
return -1;
if (profile == PPSI_PROFILE_HA_WR
|| profile == PPSI_PROFILE_CUSTOM) {
/* Extension is supported for the selected profile */
return 0;
}
return -2;
}
pp_diag(ppi, config, 1, "BUG: %s: unexpected combination of extension (%d) "
"and profile (%d)\n", __func__, extension, profile);
return -2;
}
static int set_extension(struct pp_instance *ppi, int extension)
{
pp_diag(ppi, config, 3, "%s: set extension: %s (%d)\n",
__func__, lut_extension_name[extension], extension);
switch (extension) {
#if CONFIG_HAS_EXT_WR
case PPSI_EXT_WR:
ppi->portDS->ext_dsport = ppi->ext_mem[PPSI_EXT_WR].ext_dsport;
ppi->ext_data = ppi->ext_mem[PPSI_EXT_WR].ext_data;
ppi->ext_hooks = ppi->ext_mem[PPSI_EXT_WR].ext_hooks;
ppi->protocol_extension = PPSI_EXT_WR;
break;
#endif
#if CONFIG_HAS_EXT_L1SYNC
case PPSI_EXT_L1S:
ppi->portDS->ext_dsport = ppi->ext_mem[PPSI_EXT_L1S].ext_dsport;
ppi->ext_data = ppi->ext_mem[PPSI_EXT_L1S].ext_data;
ppi->ext_hooks = ppi->ext_mem[PPSI_EXT_L1S].ext_hooks;
ppi->protocol_extension=PPSI_EXT_L1S;
/* Set L1SYNC state. Must be done here because the init hook is
* called only in the initializing state. If the port is not
* connected, the initializing is then never called so the
* L1SYNC state is invalid (0)
L1E_DSPOR_BS(ppi)->L1SyncState = L1SYNC_DISABLED;
L1E_DSPOR_BS(ppi)->L1SyncEnabled = TRUE;
break;
#endif
case PPSI_EXT_NONE:
ppi->portDS->ext_dsport = NULL;
ppi->ext_data = NULL;
ppi->ext_hooks = &pp_hooks;
ppi->protocol_extension = PPSI_EXT_NONE;
break;
default:
fprintf(stderr, "ppsi: Extension not supported\n");
return -1;
break;
static void set_l1sync_params(struct pp_instance *ppi)
{
/* Force mandatory attributes - Do not take care of the configuration */
L1E_DSPOR_BS(ppi)->rxCoherentIsRequired = TRUE;
L1E_DSPOR_BS(ppi)->txCoherentIsRequired = TRUE;
L1E_DSPOR_BS(ppi)->congruentIsRequired = TRUE;
L1E_DSPOR_BS(ppi)->L1SyncEnabled = TRUE;
L1E_DSPOR_BS(ppi)->optParamsEnabled = FALSE;
}
static int set_profile(struct pp_instance *ppi, int profile, int extension,
int startup)
ppi->cfg.profile = profile;
pp_diag(ppi, config, 3, "%s: set profile: %s (%d)\n",
__func__, lut_profile_name[ppi->cfg.profile],
ppi->cfg.profile);
switch (profile) {
case PPSI_PROFILE_PTP :
/* Disable extension */
if (set_extension(ppi, PPSI_EXT_NONE) < 0) {
/* Error */
return -1;
}
/* Disable extension autonegotiation in case it was enabled */
ppi->cfg.extAutonegEnable = 0;
/* Do not take care of L1SYNC */
enable_asymmetryCorrection(ppi, ppi->cfg.asymmetryCorrectionEnable);
break;
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
case PPSI_PROFILE_HA_WR:
#if CONFIG_HAS_PROFILE_HA_WR
if (startup) {
/* Change the extension to the configured one at
* startup */
if (set_extension(ppi, extension) < 0) {
/* Error */
return -1;
}
} else if (CONFIG_HAS_EXT_WR
/* if autonegotiation is used use L1Sync as
* extension */
&& ppi->cfg.extAutonegEnable == FALSE) {
if (set_extension(ppi, PPSI_EXT_WR) < 0) {
/* Error */
return -1;
}
} else if (CONFIG_HAS_EXT_L1SYNC) {
if (set_extension(ppi, PPSI_EXT_L1S) < 0) {
/* Error */
return -1;
}
} else {
if (set_extension(ppi, PPSI_EXT_NONE) < 0) {
/* Error */
return -1;
}
if (CONFIG_HAS_EXT_L1SYNC) {
/* Set L1Sync params even for WR profile, since we allow
* configuration WR profile with L1Sync extension */
set_l1sync_params(ppi);
}
enable_asymmetryCorrection(ppi, TRUE);
break;
#else
fprintf(stderr, "ppsi: Profile HA/WR not supported\n");
#endif /* CONFIG_HAS_PROFILE_HA_WR */
case PPSI_PROFILE_CUSTOM :
#if CONFIG_HAS_PROFILE_CUSTOM
if (startup
&& set_extension(ppi, extension) < 0) {
/* Error */
return -1;
}
#if CONFIG_HAS_EXT_L1SYNC
if (!startup) {
/* Set extension and other parameters used by CUSTOM
* profile only at startup. The intention is that
* no parameters are changed at the switch to CUSTOM
* profile in runtime. If some parametres need
* to be adjusted when running CUSTOM profile, they
* shall be changed using other ways (e.g. runtime
* reconfiguration). */
break;
}
if (ppi->cfg.l1SyncEnabled ) {
/* Read L1SYNC parameters from configuration */
L1E_DSPOR_BS(ppi)->rxCoherentIsRequired = ppi->cfg.l1SyncRxCoherentIsRequired;
L1E_DSPOR_BS(ppi)->txCoherentIsRequired = ppi->cfg.l1SyncTxCoherentIsRequired;
L1E_DSPOR_BS(ppi)->congruentIsRequired = ppi->cfg.l1SyncCongruentIsRequired;
L1E_DSPOR_BS(ppi)->optParamsEnabled = ppi->cfg.l1SyncOptParamsEnabled;
if (L1E_DSPOR_BS(ppi)->optParamsEnabled) {
L1E_DSPOR_OP(ppi)->timestampsCorrectedTx = ppi->cfg.l1SyncOptParamsTimestampsCorrectedTx;
}
}
#endif /* CONFIG_HAS_EXT_L1SYNC*/
enable_asymmetryCorrection(ppi, ppi->cfg.asymmetryCorrectionEnable);
#else
fprintf(stderr, "ppsi: Profile CUSTOM not supported\n");
return -1;
break;
default:
fprintf(stderr, "ppsi: Not supported profile %d\n", profile);
return -1;
}
return 0;
}
int set_profile_runtime(struct pp_instance *ppi, int profile)
{
if (set_profile(ppi, profile, 0 /* ignored */, 0 /* no startup */))
/* Initialize set extenstion */
return set_extension_runtime_init(ppi);
/**
* Enable/disable asymmetry correction
*/
void enable_asymmetryCorrection(struct pp_instance *ppi, Boolean enable ) {
if ( (ppi->asymmetryCorrectionPortDS.enable=enable)==TRUE ) {
/* Enabled: The delay asymmetry will be calculated */
ppi->asymmetryCorrectionPortDS.scaledDelayCoefficient =( ppi->cfg.scaledDelayCoefficient != 0) ?
ppi->cfg.scaledDelayCoefficient :
(RelativeDifference)(ppi->cfg.delayCoefficient * REL_DIFF_TWO_POW_FRACBITS);
ppi->portDS->delayAsymCoeff=pp_servo_calculateDelayAsymCoefficient(ppi->asymmetryCorrectionPortDS.scaledDelayCoefficient);
ppi->asymmetryCorrectionPortDS.constantAsymmetry=picos_to_interval(ppi->cfg.constantAsymmetry_ps);
/* Functions to set global parameters */
void set_param_global_prio1(struct pp_globals *ppg, int prio1)
{
ppg->rt_opts->priority1 = prio1;
bmc_apply_configured_device_attributes(ppg);
}
void set_param_global_prio2(struct pp_globals *ppg, int prio2)
{
ppg->rt_opts->priority2 = prio2;
bmc_apply_configured_device_attributes(ppg);
}
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
/* Functions to set parameters specific to instance */
void set_param_inst_logMinDelayReqInterval(struct pp_instance *ppi,
int logMinDelayReqInterval)
{
int millisec;
DSPOR(ppi)->logMinDelayReqInterval = logMinDelayReqInterval;
ppi->cfg.min_delay_req_interval = logMinDelayReqInterval;
millisec = pp_timeout_log_to_ms(logMinDelayReqInterval);
if (is_delayMechanismE2E(ppi))
pp_timeout_set_rename(ppi, PP_TO_REQUEST, millisec);
}
void set_param_inst_logMinPdelayReqInterval(struct pp_instance *ppi,
int logMinPdelayReqInterval)
{
int millisec;
DSPOR(ppi)->logMinPdelayReqInterval = logMinPdelayReqInterval;
ppi->cfg.min_pdelay_req_interval = logMinPdelayReqInterval;
millisec = pp_timeout_log_to_ms(logMinPdelayReqInterval);
if (is_delayMechanismP2P(ppi))
pp_timeout_set_rename(ppi, PP_TO_REQUEST, millisec);
}
void set_param_inst_logSyncInterval(struct pp_instance *ppi,
int logSyncInterval)
{
int millisec;
DSPOR(ppi)->logSyncInterval = logSyncInterval;
ppi->cfg.sync_interval = logSyncInterval;
millisec = pp_timeout_log_to_ms(logSyncInterval);
pp_timeout_set_rename(ppi, PP_TO_SYNC_SEND, millisec);
}
static char *strCodeOpt="CODEOPT=("
#if CONFIG_HAS_CODEOPT_EPC_ENABLED
" EPC"
#endif
#if CONFIG_HAS_CODEOPT_SO_ENABLED
" SO"
#endif
#if CONFIG_HAS_CODEOPT_SINGLE_FMASTER
" SFM"
#endif
#if CONFIG_HAS_CODEOPT_SINGLE_PORT
" SP"
#endif
")";
int main(int argc, char **argv)
{
struct pp_globals *ppg;
struct wrs_shm_head *hal_head;
setbuf(stdout, NULL);
pp_printf("PPSi. Commit %s, built on " __DATE__ ", %s\n",
PPSI_VERSION,
strCodeOpt);
/* Check for -h or --help in params list */
pp_parse_cmdline_early(argc, argv);
/* check if there is another instance of PPSi already running */
ppsi_head = wrs_shm_get(wrs_shm_ptp, "", WRS_SHM_READ);
if (!ppsi_head) {
pp_printf("Unable to open shm for PPSi! Unable to check if "
"there is another PPSi instance running. Error: %s\n",
strerror(errno));
exit(1);
}
/* check if pid is 0 (shm not filled) or process with provided
* pid does not exist (probably crashed) */
{
int nbTry=1;
while ( nbTry >= 0 ) {
if ((ppsi_head->pid != 0) && (kill(ppsi_head->pid, 0) == 0)) {
nbTry--;
sleep(1);
}
else
break;
}
if ( nbTry<0 ) {
wrs_shm_put(ppsi_head);
pp_printf("Fatal: There is another PPSi instance running. "
"Exit...\n\n");
exit(1);
}
Adam Wujek
committed
/* it will be opened later for write */
wrs_shm_put(ppsi_head);
/* try connecting to HAL multiple times in case it's still not ready */
hal_retries = WRSW_HAL_RETRIES;
while (hal_retries) { /* may be never, if built without WR extension */
hal_ch = minipc_client_create(WRSW_HAL_SERVER_ADDR,
MINIPC_FLAG_VERBOSE);
usleep(WRSW_HAL_TIMEOUT);
}
if (!hal_ch) {
pp_printf("ppsi: could not connect to HAL RPC");
exit(1);
/* If we connected, we also know "for sure" shmem is there */
hal_head = wrs_shm_get(wrs_shm_hal,"", WRS_SHM_READ);
if (!hal_head || !hal_head->data_off) {
pp_printf("ppsi: Can't connect with HAL "
"shared memory\n");
exit(1);
}
if (hal_head->version != HAL_SHMEM_VERSION) {
pp_printf("ppsi: unknown HAL's shm version %i "
"(known is %i)\n", hal_head->version,
HAL_SHMEM_VERSION);
exit(1);
}
hal_shmem = (void *)hal_head + hal_head->data_off;
hal_nports = hal_shmem->nports;
hal_ports = wrs_shm_follow(hal_head, hal_shmem->ports);
if (!hal_ports) {
pp_printf("ppsi: unable to follow hal_ports pointer "
"in HAL's shmem\n");
exit(1);
}
/* And create your own channel, until we move to shmem too */
ppsi_ch = minipc_server_create("ptpd", 0);
if (!ppsi_ch) { /* FIXME should we retry ? */
pp_printf("ppsi: could not create minipc server\n");
exit(1);
}
ppsi_head = wrs_shm_get(wrs_shm_ptp, "ppsi",
WRS_SHM_WRITE | WRS_SHM_LOCKED);
if (!ppsi_head) {
fprintf(stderr, "Fatal: could not create shmem: %s\n",
strerror(errno));
exit(1);
ppsi_head->version = WRS_PPSI_SHMEM_VERSION;
ppg = wrs_shm_alloc(ppsi_head, sizeof(*ppg));
ppg->defaultDS = wrs_shm_alloc(ppsi_head, sizeof(*ppg->defaultDS));
ppg->currentDS = wrs_shm_alloc(ppsi_head, sizeof(*ppg->currentDS));
ppg->parentDS = wrs_shm_alloc(ppsi_head, sizeof(*ppg->parentDS));
ppg->timePropertiesDS = wrs_shm_alloc(ppsi_head,sizeof(*ppg->timePropertiesDS));
ppg->rt_opts = &__pp_default_rt_opts;
ppg->max_links = PP_MAX_LINKS;
ppg->arch_glbl_data = wrs_shm_alloc(ppsi_head, sizeof(wrs_arch_data_t));
ppg->pp_instances = wrs_shm_alloc(ppsi_head,
ppg->max_links * sizeof(struct pp_instance));
if ((!ppg->arch_glbl_data) || (!ppg->pp_instances)) {
fprintf(stderr, "ppsi: out of memory\n");
exit(1);
}
/* Set default configuration value for all instances */
for (i = 0; i < ppg->max_links; i++) {
memcpy(&INST(ppg, i)->cfg, &__pp_default_instance_cfg,sizeof(__pp_default_instance_cfg));
/* Set offset here, so config parsing can override it */
memset(&t, 0, sizeof(t));
ppg->timePropertiesDS->currentUtcOffset = (Integer16)t.tai;
if (pp_parse_cmdline(ppg, argc, argv) != 0)
return -1;
/* If no item has been parsed, provide a default file or string */
if (ppg->cfg.cfg_items == 0)
pp_config_file(ppg, 0, PP_DEFAULT_CONFIGFILE);
if (ppg->cfg.cfg_items == 0) {
/* Default configuration for switch is all ports - Priority given to HA */
char s[128];
for (i = 0; i < WRS_NUMBER_PHYSICAL_PORTS; i++) {
sprintf(s, "port %i; iface wri%i; proto raw;", i + 1, i + 1);
if (CONFIG_HAS_PROFILE_HA_WR) {
sprintf(s, "profile ha_wr;");
if (CONFIG_HAS_EXT_L1SYNC)
sprintf(s, "extension ha;");
else if (CONFIG_HAS_EXT_WR)
sprintf(s, "extension wr");
} else
sprintf(s, "profile ptp;");
pp_config_string(ppg, s);
}
}
for (i = 0; i < ppg->nlinks; i++) {
struct pp_instance *ppi= INST(ppg, i);
ppi->ch[PP_NP_EVT].fd = -1;
ppi->ch[PP_NP_GEN].fd = -1;
ppi->vlans_array_len = CONFIG_VLAN_ARRAY_SIZE;
ppi->iface_name = ppi->cfg.iface_name;
ppi->port_name = ppi->cfg.port_name;
ppi->delayMechanism = ppi->cfg.delayMechanism;
ppi->portDS = wrs_shm_alloc(ppsi_head, sizeof(*ppi->portDS));
ppi->servo = wrs_shm_alloc(ppsi_head, sizeof(*ppi->servo));
ppi->ext_hooks=&pp_hooks; /* Default value. Can be overwritten by an extension */
if (init_extensions(ppi) < 0)
goto exit_out_of_memory;
ppi->protocol_extension = ppi->cfg.extension;
if (set_profile(ppi, ppi->cfg.profile, ppi->cfg.extension, 1 /* startup */) < 0) {
fprintf(stderr,
"ppsi: Profile %d not supported\n",
ppi->cfg.profile);
/* Parameters profile independent */
ppi->timestampCorrectionPortDS.egressLatency=picos_to_interval(ppi->cfg.egressLatency_ps);
ppi->timestampCorrectionPortDS.ingressLatency=picos_to_interval(ppi->cfg.ingressLatency_ps);
ppi->timestampCorrectionPortDS.messageTimestampPointLatency=0;
ppi->portDS->masterOnly= ppi->cfg.masterOnly; /* can be overridden in pp_init_globals() */
} else {
goto exit_out_of_memory;
/* The following default names depend on TIME= at build time */
ppi->n_ops = &DEFAULT_NET_OPS;
ppi->t_ops = &DEFAULT_TIME_OPS;
ppi->__tx_buffer = malloc(PP_MAX_FRAME_LENGTH);
ppi->__rx_buffer = malloc(PP_MAX_FRAME_LENGTH);
if (!ppi->__tx_buffer || !ppi->__rx_buffer) {
goto exit_out_of_memory;
pp_init_globals(ppg, &__pp_default_rt_opts);
wrh_timing_mode_pll_state_t timing_mode_pll_state;
struct pp_instance *ppi=INST(ppg,0);
wrh_timing_mode_t current_timing_mode;
int ret=WRH_OPER()->get_timing_mode(ppg,¤t_timing_mode);
if (ret<0) {
fprintf(stderr, "ppsi: Cannot get current timing mode\n");
exit(1);
}
WRS_ARCH_G(ppg)->timingModeLockingState=WRH_TM_LOCKING_STATE_LOCKING;
switch (current_timing_mode) {
case WRH_TM_FREE_MASTER :
// FR mode is OK for starting in all cases. We are not going to touch it
// -> GM will be set in the main loop
// -> BC will be set when a port will become slave
WRS_ARCH_G(ppg)->timingMode=WRH_TM_FREE_MASTER; // set here because set_timing_mode() is not called
break;
case WRH_TM_GRAND_MASTER :
if ( ppg->defaultDS->clockQuality.clockClass == PP_PTP_CLASS_GM_LOCKED ) {
// Already the correct timing mode. Not touched to avoid an unlock of the PLL
WRS_ARCH_G(ppg)->timingMode=WRH_TM_GRAND_MASTER; // set here because set_timing_mode() is not called
break;
}
// No break here to set the timing mode to FR
case WRH_TM_BOUNDARY_CLOCK :
case WRH_TM_DISABLED :
// Must be reseted to FR
WRH_OPER()->set_timing_mode(ppg,WRH_TM_FREE_MASTER);
/* Waiting for PLL locking. We do not need a precise time-out here */
/* We are waiting up to 3s for PLL locking.
* We do that to avoid to jump too quickly to a degraded clock class.
*/
nbRetry=2;
while(nbRetry>0) {
int ret=WRH_OPER()->get_timing_mode_state(ppg,&timing_mode_pll_state);
if ( ret==0 && timing_mode_pll_state==WRH_TM_PLL_STATE_LOCKED )
if ( timing_mode_pll_state==WRH_TM_PLL_STATE_UNLOCKED) {
WRS_ARCH_G(ppg)->timingModeLockingState = WRH_TM_LOCKING_STATE_LOCKING;
} else if (timing_mode_pll_state==WRH_TM_PLL_STATE_LOCKED) {
TOPS(ppi)->set(ppi,NULL); // GM locked: set the time
WRS_ARCH_G(ppg)->timingModeLockingState = WRH_TM_LOCKING_STATE_LOCKED;
}
/* Enable the PPS generation only if
* - Grand master and PLL is locked
* OR
* - Free running master (no condition required)
enablePPS=(WRS_ARCH_G(ppg)->timingModeLockingState== WRH_TM_LOCKING_STATE_LOCKED &&
ppg->defaultDS->clockQuality.clockClass == PP_PTP_CLASS_GM_LOCKED) ||
GOPTS(ppg)->forcePpsGen;
TOPS(ppi)->enable_timing_output(ppg,enablePPS);
wrs_init_ipcserver(ppg, ppsi_ch);
/* release lock from wrs_shm_get */
wrs_shm_write(ppsi_head, WRS_SHM_WRITE_END);
wrs_main_loop(ppg);
return 0; /* never reached */
exit_out_of_memory:;
fprintf(stderr, "ppsi: out of memory\n");
exit(1);