1866 lines
59 KiB
C
1866 lines
59 KiB
C
/*************************************************************************\
|
||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||
* National Laboratory.
|
||
* Copyright (c) 2002 The Regents of the University of California, as
|
||
* Operator of Los Alamos National Laboratory.
|
||
* EPICS BASE Versions 3.13.7
|
||
* and higher are distributed subject to a Software License Agreement found
|
||
* in file LICENSE that is included with this distribution.
|
||
\*************************************************************************/
|
||
/*drvTs.c*/
|
||
|
||
/**************************************************************************
|
||
*
|
||
* Author: Jim Kowalkowski
|
||
*
|
||
***********************************************************************/
|
||
|
||
#define MAKE_DEBUG TSdriverDebug
|
||
#define TS_DRIVER 1
|
||
|
||
#include <vxWorks.h>
|
||
#include <vme.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <ioctl.h>
|
||
#include <math.h>
|
||
#include <unistd.h>
|
||
#include <sysSymTbl.h>
|
||
#include <semLib.h>
|
||
#include <sysLib.h>
|
||
#include <symLib.h>
|
||
#include <taskLib.h>
|
||
#include <intLib.h>
|
||
#include <bootLib.h>
|
||
#include <inetLib.h>
|
||
#include <wdLib.h>
|
||
#include <ioLib.h>
|
||
#include <semLib.h>
|
||
#include <sockLib.h>
|
||
#include <selectLib.h>
|
||
#include <logLib.h>
|
||
#include <timers.h>
|
||
#include <time.h>
|
||
#include <sys/times.h>
|
||
#include <sys/types.h>
|
||
#include <sys/socket.h>
|
||
#include <netinet/in.h>
|
||
#include <net/if.h>
|
||
|
||
#include "envDefs.h"
|
||
#include "envLib.h"
|
||
#include "dbDefs.h"
|
||
#include "epicsPrint.h"
|
||
#include "errMdef.h"
|
||
#include "drvSup.h"
|
||
#include "drvTS.h"
|
||
#include "osiSock.h"
|
||
#include "iocClock.h"
|
||
#include "epicsDynLink.h"
|
||
|
||
/* Use TSprintf() for anything that should be logged, else printf() */
|
||
#define TSprintf epicsPrintf
|
||
|
||
#define DEFAULT_TIME 0
|
||
#define NO_EVENT_SYSTEM 1
|
||
|
||
/* functions used by this driver */
|
||
static long TSgetUnixTime(struct timespec*);
|
||
static long TSgetMasterTime(struct timespec*);
|
||
static long TSgetBroadcastAddr(int soc, struct sockaddr*);
|
||
static int TSgetSocket(int port, struct sockaddr_in* sin);
|
||
static int TSgetBroadcastSocket(int port, struct sockaddr_in* sin);
|
||
static void TSsyncServer();
|
||
static void TSsyncClient();
|
||
static long TSasyncClient();
|
||
static long TSsyncTheTime(struct timespec* cts, struct timespec* ts);
|
||
static long TSgetData(char* buf, int buf_size, int soc,
|
||
struct sockaddr* to_sin, struct sockaddr* from_sin,
|
||
struct timespec* round_trip);
|
||
static void TSaddStamp( struct timespec* result,
|
||
struct timespec* op1, struct timespec* op2);
|
||
static void TSwdIncTime();
|
||
static void TSwdIncTimeSync();
|
||
static void TSstampServer();
|
||
static void TSeventHandler(int Card,int EventNum,unsigned long Ticks);
|
||
static void TSerrorHandler(int Card, int ErrorNum);
|
||
static long TSsetClockFromMaster();
|
||
static void TSstartSoftClock();
|
||
static long TScalcDiff(struct timespec* a, struct timespec* b,
|
||
struct timespec* diff);
|
||
|
||
static long TSgetCurrentTime(struct timespec* ts);
|
||
static long TSforceSoftSync(int Card);
|
||
static long TSstartSyncServer();
|
||
static long TSstartSyncClient();
|
||
static long TSstartAsyncClient();
|
||
static long TSstartStampServer();
|
||
static long TSuserGetJunk(int event_number,struct timespec* sp);
|
||
|
||
/* event system and time clock functions */
|
||
static long (*TSregisterEventHandler)(int Card, void(*func)());
|
||
static long (*TSregisterErrorHandler)(int Card, void(*func)());
|
||
static long (*TSgetTicks)(int Card, unsigned long *Ticks);
|
||
static long (*TShaveReceiver)(int Card);
|
||
static long (*TSforceSync)(int Card);
|
||
static long (*TSgetTime)(struct timespec*);
|
||
static long (*TSsyncEvent)();
|
||
static long (*TSdirectTime)();
|
||
static long (*TSdriverInit)();
|
||
static long (*TSuserGet)(int event_number,struct timespec* sp);
|
||
|
||
static int TSinitialized = 0;
|
||
|
||
/* global functions */
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
long TSinit(void); /* called by iocInit currently */
|
||
long TSreport(); /* callable from vxWorks shell */
|
||
|
||
/* test functions */
|
||
void TSprintRealTime();
|
||
void TSprintUnixTime();
|
||
void TSprintMasterTime();
|
||
void TSprintTimeStamp(int event_number);
|
||
void TSprintCurrentTime();
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
/* data used by all */
|
||
TSinfo TSdata =
|
||
{ TS_master_dead, TS_async_slave, TS_async_none,
|
||
0,NULL,
|
||
TS_SYNC_RATE_SEC,TS_CLOCK_RATE_HZ,0,TS_TIME_OUT_MS,0,
|
||
TS_MASTER_PORT,TS_SLAVE_PORT,1,0,0,0,0,
|
||
NULL, {NULL}, {NULL}
|
||
};
|
||
|
||
extern char* sysBootLine;
|
||
|
||
int TSdirectTimeVar = 0; /* aother way to indicate direct time */
|
||
int TSgoodTimeStamps = 0; /* a way to force use of accurate time stamps */
|
||
|
||
static WDOG_ID wd;
|
||
static long correction_factor = 0;
|
||
static long correction_count = 0;
|
||
|
||
|
||
/* static long TSreturnError() { return -1; } */
|
||
static long TSdirectTimeError() { return -1; }
|
||
static long TShaveReceiverError(int i) { return -1; }
|
||
static long TSgetTicksError(int i,unsigned long* t) { return -1; }
|
||
|
||
static long TSregisterErrorHandlerError(int i, void(*f)())
|
||
{ if(TSdata.has_direct_time==1) return 0; else return -1; }
|
||
static long TSregisterEventHandlerError(int i, void(*f)())
|
||
{ if(TSdata.has_direct_time==1) return 0; else return -1; }
|
||
|
||
|
||
long TSdriverInitError()
|
||
{
|
||
struct timespec ts;
|
||
time_t t;
|
||
|
||
clock_gettime(CLOCK_REALTIME,&ts);
|
||
time(&t);
|
||
Debug(1,"vxWorks time = %s\n",ctime(&t));
|
||
|
||
return 0;
|
||
}
|
||
|
||
unsigned long TSepochNtpToUnix(struct timespec* ts)
|
||
{
|
||
unsigned long nfssecs = (unsigned long)ts->tv_sec;
|
||
unsigned long secs = 0;
|
||
|
||
if(!TSinitialized) TSinit();
|
||
/*If high order bit is not set then nfssecs has overflowed */
|
||
if(!(nfssecs & 0x80000000ul)) {
|
||
/*secs = nfssecs - TS_1900_TO_VXWORKS_EPOCH + 2**32 */
|
||
/* in order to prevent overflows rearrange as */
|
||
/* secs = (2**32 -1) - TS_1900_TO_VXWORKS_EPOCH + nfssecs +1 */
|
||
secs = 0xffffffffUL - TS_1900_TO_VXWORKS_EPOCH + nfssecs +1;
|
||
} else {
|
||
secs = nfssecs - TS_1900_TO_VXWORKS_EPOCH;
|
||
}
|
||
return(secs);
|
||
}
|
||
|
||
unsigned long TSfractionToNano(unsigned long fraction)
|
||
{
|
||
double value;
|
||
|
||
/*value = 1e9 * fraction / 2**32 */
|
||
value = (1e9 * (double)fraction)/4294967296.0;
|
||
return((unsigned long)value);
|
||
}
|
||
|
||
unsigned long TSepochNtpToEpics(struct timespec* ts)
|
||
{
|
||
unsigned long nfssecs = (unsigned long)ts->tv_sec;
|
||
unsigned long secs = 0;
|
||
|
||
/*If high order bit is not set then nfssecs has overflowed */
|
||
if(!(nfssecs & 0x80000000ul)) {
|
||
/*secs = nfssecs - TS_1900_TO_EPICS_EPOCH + 2**32 */
|
||
/* in order to prevent overflows rearrange as */
|
||
/* secs = (2**32 -1) - TS_1900_TO_EPICS_EPOCH + nfssecs +1 */
|
||
secs = 0xffffffffUL - TS_1900_TO_EPICS_EPOCH + nfssecs +1;
|
||
} else {
|
||
secs = nfssecs - TS_1900_TO_EPICS_EPOCH;
|
||
}
|
||
return(secs);
|
||
}
|
||
|
||
unsigned long TSepochUnixToEpics(struct timespec* ts)
|
||
{
|
||
unsigned long unixsecs = (unsigned long)ts->tv_sec;
|
||
unsigned long secs;
|
||
|
||
secs = unixsecs - TS_VXWORKS_TO_EPICS_EPOCH;
|
||
return(secs);
|
||
}
|
||
|
||
unsigned long TSepochEpicsToUnix(struct timespec* ts)
|
||
{
|
||
unsigned long epicssecs = (unsigned long)ts->tv_sec;
|
||
unsigned long secs;
|
||
|
||
secs = epicssecs + TS_VXWORKS_TO_EPICS_EPOCH;
|
||
return(secs);
|
||
}
|
||
|
||
/*-----------------------------------------------------------------------*/
|
||
/*
|
||
TSreport() - report information about the state of the time stamp
|
||
support software
|
||
*/
|
||
long TSreport()
|
||
{
|
||
char buf[64];
|
||
|
||
switch(TSdata.type)
|
||
{
|
||
case TS_direct_master: printf("Direct timing master\n"); break;
|
||
case TS_sync_master: printf("Event timing master\n"); break;
|
||
case TS_async_master: printf("Soft timing master\n"); break;
|
||
case TS_direct_slave: printf("Direct timing slave\n"); break;
|
||
case TS_sync_slave: printf("Event timing slave\n"); break;
|
||
case TS_async_slave: printf("Soft timing slave\n"); break;
|
||
default: break;
|
||
}
|
||
switch(TSdata.state)
|
||
{
|
||
case TS_master_alive: printf("Master timing IOC alive\n"); break;
|
||
case TS_master_dead: printf("Master timing IOC dead\n"); break;
|
||
default: break;
|
||
}
|
||
switch(TSdata.async_type)
|
||
{
|
||
case TS_async_none: printf("No clock synchronization\n"); break;
|
||
case TS_async_private: printf("Sync protocol with master\n"); break;
|
||
case TS_async_ntp: printf("NTP sync with unix server\n"); break;
|
||
case TS_async_time: printf("Time protocol sync with unix\n"); break;
|
||
default: break;
|
||
}
|
||
printf("Clock Rate in Hertz = %lu\n",TSdata.clock_hz);
|
||
printf("Sync Rate in Seconds = %lu\n",TSdata.sync_rate);
|
||
printf("Master communications port = %d\n",TSdata.master_port);
|
||
printf("Slave communications port = %d\n",TSdata.slave_port);
|
||
printf("Total events supported = %d\n",TSdata.total_events);
|
||
printf("Request Time Out = %lu milliseconds\n",TSdata.time_out);
|
||
|
||
ipAddrToDottedIP ((struct sockaddr_in*)&TSdata.hunt, buf, sizeof(buf));
|
||
printf("Broadcast address: %s\n", buf);
|
||
ipAddrToDottedIP ((struct sockaddr_in*)&TSdata.master, buf, sizeof(buf));
|
||
printf("Master address: %s\n", buf);
|
||
|
||
if(TSdata.UserRequestedType)
|
||
printf("\nForced to not use the event system\n");
|
||
|
||
if(TSdata.has_direct_time)
|
||
printf("Event system has time directly available\n");
|
||
return 0;
|
||
}
|
||
|
||
/*-----------------------------------------------------------------------*/
|
||
/*
|
||
TSconfigure() - This is the configuration routine which is meant to
|
||
be called from the vxWorks startup.cmd script before iocInit.
|
||
It's job is to set operating parameters for the time stamp support code.
|
||
|
||
JRW -- if type = 0, then try to config using event system
|
||
if type = 1, then permanantly inhibit use of the event system
|
||
*/
|
||
void TSconfigure(int master, int sync_rate_sec, int clock_rate_hz,
|
||
int master_port, int slave_port, unsigned long time_out, int type)
|
||
{
|
||
if(master) TSdata.master_timing_IOC=1;
|
||
else TSdata.master_timing_IOC=0;
|
||
|
||
if(sync_rate_sec) TSdata.sync_rate=sync_rate_sec;
|
||
else TSdata.sync_rate=TS_SYNC_RATE_SEC;
|
||
|
||
if(clock_rate_hz) TSdata.clock_hz=clock_rate_hz;
|
||
else TSdata.clock_hz=TS_CLOCK_RATE_HZ;
|
||
|
||
TSdata.clock_conv= TS_BILLION / TSdata.clock_hz;
|
||
|
||
if(master_port) TSdata.master_port=master_port;
|
||
else TSdata.master_port=TS_MASTER_PORT;
|
||
|
||
if(slave_port) TSdata.slave_port=slave_port;
|
||
else TSdata.slave_port=TS_SLAVE_PORT;
|
||
|
||
if(time_out) TSdata.time_out=time_out;
|
||
else TSdata.time_out=TS_TIME_OUT_MS;
|
||
|
||
switch(type)
|
||
{
|
||
case DEFAULT_TIME:
|
||
case NO_EVENT_SYSTEM:
|
||
TSdata.UserRequestedType = type;
|
||
break;
|
||
default:
|
||
TSprintf("Invalid type parameter <%d> must be:\n",type);
|
||
TSprintf(" 0 = default time system\n");
|
||
TSprintf(" 1 = force no event system\n");
|
||
TSdata.UserRequestedType = 0;
|
||
break;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
/*
|
||
this sucks, but who cares, user should not be using event number if not
|
||
prepared to handle them by defining an ErUserGetTimeStamp() routine.
|
||
*/
|
||
static long TSuserGetJunk(int event_number,struct timespec* sp)
|
||
{
|
||
return TSgetTimeStamp(0,sp);
|
||
}
|
||
|
||
/*-----------------------------------------------------------------------*/
|
||
/*
|
||
TSgetTimeStamp() - This routine returns the time stamp which represents
|
||
the time when an event event_number occurred. Soft timing will always
|
||
return the current time. Event zero will always return the current time
|
||
also.
|
||
*/
|
||
long TSgetTimeStamp(int event_number,struct timespec* sp)
|
||
{
|
||
/* this is questionable, a sync slave with a dead master will have
|
||
an invalid time stamp, so use the vxworks clock. Also remember that
|
||
the IOC vxWorks clock may not be in sync with time stamps if
|
||
default TSgetTime() in use and master failure is detected */
|
||
if(!TSinitialized) TSinit();
|
||
|
||
if(event_number<epicsTimeEventBestTime) return(0);
|
||
switch(TSdata.type)
|
||
{
|
||
case TS_async_master:
|
||
case TS_async_slave:
|
||
if(event_number==epicsTimeEventCurrentTime
|
||
|| event_number==epicsTimeEventBestTime) {
|
||
*sp = TSdata.event_table[TSdata.sync_event];
|
||
} else {
|
||
return TSuserGet(event_number,sp);
|
||
}
|
||
break;
|
||
case TS_direct_slave:
|
||
case TS_direct_master:
|
||
TSgetTime(sp);
|
||
break;
|
||
case TS_sync_slave:
|
||
case TS_sync_master:
|
||
switch(event_number)
|
||
{
|
||
case epicsTimeEventCurrentTime:
|
||
if(TSgoodTimeStamps==0)
|
||
{
|
||
/* one tick watch dog maintains */
|
||
*sp = TSdata.event_table[0];
|
||
break;
|
||
}
|
||
case epicsTimeEventBestTime:
|
||
{
|
||
struct timespec ts;
|
||
unsigned long ticks;
|
||
|
||
TSgetTicks(0,&ticks); /* add in the board time */
|
||
*sp = TSdata.event_table[TSdata.sync_event];
|
||
|
||
/* calculate a time stamp from the tick count */
|
||
ts.tv_sec = ticks / TSdata.clock_hz;
|
||
ts.tv_nsec=(ticks-(ts.tv_sec*TSdata.clock_hz))*
|
||
TSdata.clock_conv;
|
||
|
||
sp->tv_sec += ts.tv_sec;
|
||
sp->tv_nsec += ts.tv_nsec;
|
||
|
||
/* adjust seconds if needed */
|
||
if(sp->tv_nsec >= TS_BILLION)
|
||
{
|
||
sp->tv_sec++;
|
||
sp->tv_nsec -= TS_BILLION;
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
if(TSdata.state==TS_master_dead)
|
||
TSgetTime(sp);
|
||
else
|
||
*sp = TSdata.event_table[event_number];
|
||
break;
|
||
}
|
||
break;
|
||
default:
|
||
if(event_number==epicsTimeEventCurrentTime
|
||
|| event_number==epicsTimeEventBestTime) {
|
||
*sp = TSdata.event_table[TSdata.sync_event];
|
||
} else {
|
||
*sp = TSdata.event_table[event_number];
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*-----------------------------------------------------------------------*/
|
||
/*
|
||
TSinit() - initialize the driver, determine mode.
|
||
*/
|
||
static int getEvent(TS_STAMP *pDest, int eventNumber)
|
||
{
|
||
return(TSgetTimeStamp(eventNumber,(struct timespec*)pDest));
|
||
}
|
||
|
||
long TSinit(void)
|
||
{
|
||
SYM_TYPE stype;
|
||
|
||
Debug0(5,"In TSinit()\n");
|
||
|
||
if(TSinitialized) return(0);
|
||
/*register with iocClock */
|
||
iocClockRegister((pepicsTimeGetCurrent)TScurrentTimeStamp,getEvent);
|
||
TSinitialized = 1;
|
||
/* 0=default, 1=none, 2=direct */
|
||
|
||
if( TSdata.UserRequestedType==DEFAULT_TIME)
|
||
{
|
||
/* default configuration probe */
|
||
/* ------------------------------------------------------------- */
|
||
/* find the lower level event system functions */
|
||
if(symFindByNameEPICS(sysSymTbl,"_ErHaveReceiver",
|
||
(char**)&TShaveReceiver,&stype)==ERROR)
|
||
TShaveReceiver = TShaveReceiverError;
|
||
if(symFindByNameEPICS(sysSymTbl,"_ErGetTicks",
|
||
(char**)&TSgetTicks,&stype)==ERROR)
|
||
TSgetTicks = TSgetTicksError;
|
||
|
||
if(symFindByNameEPICS(sysSymTbl,"_ErRegisterEventHandler",
|
||
(char**)&TSregisterEventHandler,&stype)==ERROR)
|
||
TSregisterEventHandler = TSregisterEventHandlerError;
|
||
|
||
if(symFindByNameEPICS(sysSymTbl,"_ErRegisterErrorHandler",
|
||
(char**)&TSregisterErrorHandler,&stype)==ERROR)
|
||
TSregisterErrorHandler = TSregisterErrorHandlerError;
|
||
|
||
if(symFindByNameEPICS(sysSymTbl,"_ErForceSync",
|
||
(char**)&TSforceSync,&stype)==ERROR)
|
||
TSforceSync = TSforceSoftSync;
|
||
|
||
if(symFindByNameEPICS(sysSymTbl,"_ErDirectTime",
|
||
(char**)&TSdirectTime,&stype)==ERROR)
|
||
TSdirectTime = TSdirectTimeError;
|
||
|
||
if(symFindByNameEPICS(sysSymTbl,"_ErDriverInit",
|
||
(char**)&TSdriverInit,&stype)==ERROR)
|
||
TSdriverInit = TSdriverInitError;
|
||
|
||
if(symFindByNameEPICS(sysSymTbl,"_ErGetTime",
|
||
(char**)&TSgetTime,&stype)==ERROR)
|
||
TSgetTime = TSgetCurrentTime;
|
||
|
||
if(symFindByNameEPICS(sysSymTbl,"_ErUserGetTimeStamp",
|
||
(char**)&TSuserGet,&stype)==ERROR)
|
||
TSuserGet = TSuserGetJunk;
|
||
|
||
if(symFindByNameEPICS(sysSymTbl,"_ErSyncEvent",
|
||
(char**)&TSsyncEvent,&stype)==ERROR)
|
||
TSdata.sync_event=ER_EVENT_RESET_TICK;
|
||
else
|
||
TSdata.sync_event=TSsyncEvent();
|
||
|
||
/* ------------------------------------------------------------- */
|
||
}
|
||
else
|
||
{
|
||
/* inhibit probe and use of the event system */
|
||
TSprintf("WARNING: drvTS event hardware probe inhibited by user\n");
|
||
TShaveReceiver = TShaveReceiverError;
|
||
TSgetTicks = TSgetTicksError;
|
||
TSregisterEventHandler = TSregisterEventHandlerError;
|
||
TSregisterErrorHandler = TSregisterErrorHandlerError;
|
||
TSforceSync = TSforceSoftSync;
|
||
TSgetTime = TSgetCurrentTime;
|
||
TSdriverInit = TSdriverInitError;
|
||
TSdirectTime = TSdirectTimeError;
|
||
TSuserGet = TSuserGetJunk;
|
||
TSdata.sync_event=ER_EVENT_RESET_TICK;
|
||
}
|
||
|
||
/* set all the known information about the system */
|
||
TSdata.event_table=NULL;
|
||
TSdata.ts_sync_valid=0; /* the sync time stamp invalid */
|
||
TSdata.state=TS_master_dead;
|
||
TSdata.sync_occurred = semBCreate(SEM_Q_PRIORITY,SEM_EMPTY);
|
||
TSdata.has_event_system = 0;
|
||
TSdata.has_direct_time = 0;
|
||
TSdata.async_type=TS_async_none;
|
||
|
||
if( (TSdata.total_events=TShaveReceiver(0))<=0)
|
||
{
|
||
Debug0(5,"TSinit() - no event receiver\n");
|
||
TSdata.total_events=1;
|
||
TSdata.sync_event=0;
|
||
TSdata.clock_hz=sysClkRateGet();
|
||
TSdata.clock_conv=TS_BILLION / TSdata.clock_hz;
|
||
TSdata.has_event_system = 0;
|
||
}
|
||
else
|
||
TSdata.has_event_system = 1;
|
||
|
||
if(TSdirectTime()>0 || TSdirectTimeVar>0) TSdata.has_direct_time=1;
|
||
|
||
/* allocate the event table */
|
||
TSdata.event_table=(struct timespec*)malloc(
|
||
TSdata.total_events*sizeof(struct timespec));
|
||
|
||
if(TSdata.master_timing_IOC)
|
||
{
|
||
/* master */
|
||
Debug0(5,"TSinit() - I am master\n");
|
||
if(TSdata.has_direct_time)
|
||
{
|
||
TSdata.type=TS_direct_master;
|
||
}
|
||
else
|
||
{
|
||
if(TSdata.has_event_system)
|
||
TSdata.type=TS_sync_master;
|
||
else
|
||
TSdata.type=TS_async_master;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* slave */
|
||
Debug0(5,"TSinit() - I am slave\n");
|
||
if(TSdata.has_direct_time)
|
||
{
|
||
TSdata.type=TS_direct_slave;
|
||
}
|
||
else
|
||
{
|
||
if(TSdata.has_event_system)
|
||
TSdata.type=TS_sync_slave;
|
||
else
|
||
TSdata.type=TS_async_slave;
|
||
}
|
||
}
|
||
|
||
/* set up the event system hooks */
|
||
if(TSdata.has_event_system)
|
||
{
|
||
/* register the event handler function */
|
||
if(TSregisterEventHandler(0,TSeventHandler)!=0)
|
||
{
|
||
TSprintf("Failed to register event handler\n");
|
||
return -1;
|
||
}
|
||
|
||
/* register the error handler function */
|
||
if(TSregisterErrorHandler(0,TSerrorHandler)!=0)
|
||
{
|
||
TSprintf("Failed to register error handler\n");
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
/* always start the soft clock */
|
||
if(TSdata.has_direct_time==0) TSstartSoftClock();
|
||
|
||
/* get time from boot server Unix system */
|
||
if(TSdata.master_timing_IOC)
|
||
{
|
||
/* master */
|
||
if(TSsetClockFromUnix()<0)
|
||
{
|
||
/* bad, cannot get time - accessing starts ticking */
|
||
struct timespec tp;
|
||
clock_gettime(CLOCK_REALTIME,&tp);
|
||
TSprintf("Failed to set clock from Unix server\n");
|
||
}
|
||
Debug0(5,"TSinit() - tried to get clock from unix\n");
|
||
|
||
/* start the time stamp info server */
|
||
if(TSstartStampServer()==ERROR)
|
||
{
|
||
TSprintf("Failed to start stamp server\n");
|
||
return -1;
|
||
}
|
||
Debug0(5,"TSinit() - stamp server started \n");
|
||
|
||
TSdata.state = TS_master_alive;
|
||
|
||
/* a direct master may be capable of delivering sync time stamps? */
|
||
if(TSdata.type==TS_sync_master)
|
||
{
|
||
/* start the sync udp server */
|
||
if(TSstartSyncServer()==ERROR)
|
||
{
|
||
TSprintf("Failed to start sync server\n");
|
||
return -1;
|
||
}
|
||
Debug0(5,"TSinit() - sync server started \n");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* slave */
|
||
if(TSsetClockFromUnix()<0)
|
||
{
|
||
struct timespec tp;
|
||
clock_gettime(CLOCK_REALTIME,&tp);
|
||
/* this should work */
|
||
TSprintf("Failed to set time from Unix server\n");
|
||
}
|
||
|
||
if( TSsetClockFromMaster()<0 )
|
||
{
|
||
/* do nothing here */
|
||
/* TSprintf("Could not contact a master timing IOC\n"); */
|
||
}
|
||
else
|
||
TSdata.state = TS_master_alive;
|
||
|
||
if(TSdata.type==TS_async_slave)
|
||
{
|
||
/* this task syncs with master or unix */
|
||
if(TSstartAsyncClient()==ERROR)
|
||
{
|
||
TSprintf("Failed to start async client\n");
|
||
return -1;
|
||
}
|
||
Debug0(5,"TSinit() - async client started \n");
|
||
}
|
||
else
|
||
{
|
||
/* this task sync with master */
|
||
if(TSstartSyncClient()==ERROR)
|
||
{
|
||
TSprintf("Failed to start sync client\n");
|
||
return -1;
|
||
}
|
||
Debug0(5,"TSinit() - sync client started \n");
|
||
}
|
||
}
|
||
/* if TIMEZONE not defined set TIMEZONE from EPICS_TIMEZONE */
|
||
if(getenv("TIMEZONE")==(char*)NULL) {
|
||
char timezone[80];
|
||
char envtimezone[100];
|
||
if(envGetConfigParam(&EPICS_TIMEZONE,sizeof(timezone),timezone)==NULL
|
||
|| strlen(timezone)==0) {
|
||
printf("iocClockInit: No Time Zone Information\n");
|
||
} else {
|
||
sprintf(envtimezone,"TIMEZONE=%s",timezone);
|
||
if(putenv(envtimezone)==ERROR) {
|
||
printf("iocClockInit: TIMEZONE putenv failed\n");
|
||
}
|
||
}
|
||
}
|
||
TSdriverInit(); /* Call the user's driver initialization if supplied */
|
||
return 0;
|
||
}
|
||
|
||
/* following are watch dog routines for soft time support */
|
||
|
||
void TSstartSoftClock() /*start the soft clock watch dog*/
|
||
{
|
||
/* simple watch dog to fire off syncs to slaves */
|
||
Debug(5,"start watch dog at rate %ld\n",
|
||
(long) sysClkRateGet()*TSdata.sync_rate);
|
||
wd = wdCreate();
|
||
if(TSdata.has_event_system)
|
||
{
|
||
Debug0(8,"Starting sync time watch dog\n");
|
||
wdStart(wd,1,(FUNCPTR)TSwdIncTimeSync,NULL);
|
||
}
|
||
else
|
||
{
|
||
Debug0(8,"Starting async time watch dog\n");
|
||
wdStart(wd,1,(FUNCPTR)TSwdIncTime,NULL);
|
||
}
|
||
return;
|
||
}
|
||
|
||
/* NOTE: TSwdIncTime called at interrupt level! */
|
||
static void TSwdIncTime() /*increment the time stamp at a 60 Hz rate*/
|
||
{
|
||
int key;
|
||
|
||
wdStart(wd,1, (FUNCPTR)TSwdIncTime,NULL);
|
||
/* update the event table */
|
||
key=intLock();
|
||
|
||
if(correction_count)
|
||
{
|
||
long nsec = TSdata.event_table[TSdata.sync_event].tv_nsec
|
||
+ TSdata.clock_conv;
|
||
nsec += correction_factor;
|
||
if(nsec<0) {
|
||
nsec += TS_BILLION;
|
||
TSdata.event_table[TSdata.sync_event].tv_sec--;
|
||
}
|
||
TSdata.event_table[TSdata.sync_event].tv_nsec = nsec;
|
||
if(--correction_count == 0) correction_factor = 0;
|
||
}
|
||
else
|
||
TSdata.event_table[TSdata.sync_event].tv_nsec +=
|
||
TSdata.clock_conv;
|
||
|
||
/* adjust seconds if needed */
|
||
if(TSdata.event_table[TSdata.sync_event].tv_nsec >= TS_BILLION)
|
||
{
|
||
TSdata.event_table[TSdata.sync_event].tv_sec++;
|
||
TSdata.event_table[TSdata.sync_event].tv_nsec -= TS_BILLION;
|
||
}
|
||
intUnlock(key);
|
||
return;
|
||
}
|
||
|
||
static void TSwdIncTimeSync()
|
||
{
|
||
wdStart(wd,1, (FUNCPTR)TSwdIncTimeSync,NULL);
|
||
TSaccurateTimeStamp(&TSdata.event_table[0]);
|
||
}
|
||
|
||
/* following are all interrupt service routines */
|
||
|
||
/*
|
||
TSeventHandler() - receive events from event system; update the event table
|
||
|
||
Note: called at interrupt level!
|
||
*/
|
||
static void TSeventHandler(int Card,int EventNum,unsigned long Ticks)
|
||
{
|
||
struct timespec ts;
|
||
struct timespec* st;
|
||
int key;
|
||
|
||
#ifdef DIRECT_WITH_EVENTS
|
||
if(TSdata.has_direct_time==1)
|
||
{
|
||
TSgetTime(&ts);
|
||
key=intLock();
|
||
TSdata.event_table[EventNum].tv_sec = ts.tv_sec;
|
||
TSdata.event_table[EventNum].tv_nsec = ts.tv_nsec;
|
||
intUnlock(key);
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
/* calculate a time stamp from the Tick count */
|
||
ts.tv_sec = Ticks / TSdata.clock_hz;
|
||
ts.tv_nsec = (Ticks - (ts.tv_sec * TSdata.clock_hz)) * TSdata.clock_conv;
|
||
/* obtain a copy of the last sync time */
|
||
st = &TSdata.event_table[TSdata.sync_event];
|
||
/* update the event table */
|
||
key=intLock();
|
||
TSdata.event_table[EventNum].tv_sec = st->tv_sec + ts.tv_sec;
|
||
TSdata.event_table[EventNum].tv_nsec = st->tv_nsec + ts.tv_nsec;
|
||
/* adjust seconds if needed */
|
||
if(TSdata.event_table[EventNum].tv_nsec >= TS_BILLION)
|
||
{
|
||
TSdata.event_table[EventNum].tv_sec++;
|
||
TSdata.event_table[EventNum].tv_nsec -= TS_BILLION;
|
||
}
|
||
intUnlock(key);
|
||
/* signal a time stamp sync if this is sync event */
|
||
if(TSdata.type==TS_sync_master && EventNum==TSdata.sync_event)
|
||
{
|
||
/* validate the soft time for back off in case of event system
|
||
failure, this will not be done in first implementation */
|
||
|
||
/* tell broadcast server to send out sync */
|
||
semGive(TSdata.sync_occurred);
|
||
}
|
||
return;
|
||
}
|
||
|
||
/* TSerrorHandler() - receive errors from event system
|
||
Note: called at interrupt level!
|
||
*/
|
||
static void TSerrorHandler(int Card, int ErrorNum)
|
||
{
|
||
/*
|
||
probably should do the following:
|
||
mark a "bad" state - timestamp is invalid,
|
||
send a request for sync to master,
|
||
if sync doesn't come when in bad state, then install
|
||
clock hook to increment time stamp until event system back up
|
||
keep a count of errors
|
||
Could put the slave on the vxworks timer until next sync
|
||
*/
|
||
|
||
if(MAKE_DEBUG)
|
||
{
|
||
switch(ErrorNum)
|
||
{
|
||
case 1:
|
||
logMsg("***TSerrorHandler: event system error: TAXI violation",
|
||
0,0,0,0,0,0);
|
||
break;
|
||
case 2:
|
||
logMsg("***TSerrorHandler: event system error: lost heartbeat",
|
||
0,0,0,0,0,0);
|
||
break;
|
||
case 3:
|
||
logMsg("***TSerrorHandler: event system error: lost events",
|
||
0,0,0,0,0,0);
|
||
break;
|
||
default:
|
||
logMsg("***TSerrorHandler: unknown error %d from event system",
|
||
ErrorNum,0,0,0,0,0);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
/**************************************************************************/
|
||
/* the following are utilities for initially getting and setting the time */
|
||
/**************************************************************************/
|
||
/*
|
||
TSgetUnixTime() - ask the boot server for the time using the
|
||
time protocol. This is only the time to the nearest second
|
||
A better way would be to use the NTP transactions to boot server
|
||
*/
|
||
|
||
static long TSgetUnixTime(struct timespec* ts)
|
||
{
|
||
BOOT_PARAMS bootParms;
|
||
unsigned long buf_data,timeValue;
|
||
TS_NTP buf_ntp;
|
||
struct sockaddr_in sin;
|
||
int soc;
|
||
char host_addr[BOOT_ADDR_LEN];
|
||
int status;
|
||
|
||
Debug0(2,"in TSgetUnixTime()\n");
|
||
if(envGetConfigParam(&EPICS_TS_NTP_INET,BOOT_ADDR_LEN,host_addr)==NULL ||
|
||
strlen(host_addr)==0)
|
||
{
|
||
/* use boot host if the environment variable not set */
|
||
bootStringToStruct(sysBootLine,&bootParms);
|
||
/* bootParms.had = host IP address */
|
||
strncpy(host_addr,bootParms.had,BOOT_ADDR_LEN);
|
||
}
|
||
if( (soc=TSgetSocket(0,&sin)) <0)
|
||
{ Debug0(1,"TSgetsocket failed\n"); return -1; }
|
||
|
||
/* set up for ntp transaction to boot server */
|
||
Debug(5,"host addr = %s\n",host_addr);
|
||
|
||
/* well known registered NTP port - or whatever port they specify */
|
||
status = aToIPAddr (host_addr, UDP_NTP_PORT, &sin);
|
||
if (status) {
|
||
Debug0(2,"bad host name or IP address\n");
|
||
TSdata.async_type=TS_async_none;
|
||
close(soc);
|
||
return -1;
|
||
}
|
||
|
||
TSdata.async_type=TS_async_ntp;
|
||
memset(&buf_ntp,0,sizeof(buf_ntp));
|
||
buf_ntp.info[0]=0x0b;
|
||
if(TSgetData((char*)&buf_ntp,sizeof(buf_ntp),soc,
|
||
(struct sockaddr*)&sin,NULL,NULL)<0)
|
||
{
|
||
Debug0(2,"no reply from NTP server\n");
|
||
/* well known registered time port */
|
||
sin.sin_port = htons(UDP_TIME_PORT);
|
||
TSdata.async_type=TS_async_time;
|
||
buf_data=0;
|
||
if(TSgetData((char*)&buf_data,sizeof(buf_data),soc,
|
||
(struct sockaddr*)&sin,NULL,NULL)<0)
|
||
{
|
||
Debug0(2,"no reply from Time server\n");
|
||
TSdata.async_type=TS_async_none;
|
||
close(soc);
|
||
return -1;
|
||
}
|
||
}
|
||
/* remember: time and NTP protocol return seconds since 1900, not 1970 */
|
||
/* to convert sec = timeValue - TS_1900_TO_EPICS_EPOCH */
|
||
if(TSdata.async_type==TS_async_time)
|
||
{
|
||
/* this is time protocol */
|
||
timeValue=ntohl(buf_data);
|
||
ts->tv_sec = timeValue;
|
||
ts->tv_nsec = 0;
|
||
Debug(2,"got the Time protocol time %lu= \n",timeValue);
|
||
}
|
||
else
|
||
{
|
||
/* this is ntp - need to convert to ns */
|
||
ts->tv_sec=ntohl(buf_ntp.transmit_ts.tv_sec);
|
||
timeValue=ntohl(buf_ntp.transmit_ts.tv_nsec);
|
||
ts->tv_nsec = TSfractionToNano(timeValue);
|
||
if(MAKE_DEBUG>=2)
|
||
printf("got the NTP time %9.9lu.%9.9lu\n",ts->tv_sec,timeValue);
|
||
}
|
||
close(soc);
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
TSgetMasterTime() - query the master timing IOC for it's current time
|
||
This routine will be converted to a task and block on a semaphore,
|
||
when it gets the semaphore, it will go to the master timing IOC
|
||
and get a time stamp, the time stamp will be used to adjust the
|
||
vxworks clock. To use it for software timing, just put a watchdog
|
||
out there that pokes the semaphore. Another way to manage soft
|
||
timing is to catch broadcasts from the master, round trip
|
||
calculations cannot be made when doing this.
|
||
*/
|
||
static long TSgetMasterTime(struct timespec* tsp)
|
||
{
|
||
TSstampTrans stran;
|
||
struct timespec curr_time,tran_time;
|
||
struct sockaddr_in sin;
|
||
struct sockaddr fs;
|
||
int soc;
|
||
|
||
Debug0(3,"TSgetMasterTime() called\n");
|
||
|
||
if( (soc=TSgetBroadcastSocket(0,&sin)) <0)
|
||
{ Debug0(1,"TSgetBroadcastSocket failed\n"); return -1; }
|
||
sin.sin_port = htons(TSdata.master_port);
|
||
memcpy(&TSdata.hunt,&sin,sizeof(sin));
|
||
stran.type=(TStype)htonl(TS_time_request);
|
||
stran.magic=htonl(TS_MAGIC);
|
||
|
||
if(TSgetData((char*)&stran,sizeof(stran),soc,
|
||
(struct sockaddr*)&sin,&fs,&tran_time)<0)
|
||
{ Debug0(2,"no reply from master server\n"); close(soc); return -1; }
|
||
|
||
/* check the magic number */
|
||
if(ntohl(stran.magic)!=(TS_MAGIC))
|
||
{
|
||
|
||
TSprintf("TSgetMasterTime: invalid packet received\n");
|
||
close(soc);
|
||
return -1;
|
||
}
|
||
/* we now have several pieces of information:
|
||
the master's address,
|
||
the approximate round trip time,
|
||
and the master's processing time. */
|
||
/* set the global data structure for information from master */
|
||
Debug(8,"master port=%d\n",ntohs(((struct sockaddr_in*)&fs)->sin_port));
|
||
TSdata.master = fs;
|
||
TSdata.state = TS_master_alive;
|
||
if(TSdata.type==TS_sync_slave)
|
||
{
|
||
TSdata.clock_hz = ntohl(stran.clock_hz);
|
||
TSdata.sync_rate = ntohl(stran.sync_rate);
|
||
TSdata.clock_conv = TS_BILLION / TSdata.clock_hz;
|
||
}
|
||
|
||
if(MAKE_DEBUG>=6)
|
||
{
|
||
printf("round trip time: %9.9lu.%9.9lu\n",
|
||
tran_time.tv_sec,tran_time.tv_nsec);
|
||
printf("master time: %9.9lu.%9.9lu\n",
|
||
stran.master_time.tv_sec,stran.master_time.tv_nsec);
|
||
}
|
||
|
||
/* Halve the round-trip time to estimate the time stamp error */
|
||
tran_time.tv_nsec >>= 1;
|
||
if (tran_time.tv_sec & 1) tran_time.tv_nsec += (TS_BILLION >> 1);
|
||
tran_time.tv_sec >>= 1;
|
||
|
||
/* add the error estimate to the time stamp from master */
|
||
curr_time.tv_sec = ntohl(stran.current_time.tv_sec);
|
||
curr_time.tv_nsec = ntohl(stran.current_time.tv_nsec);
|
||
TSaddStamp(tsp,&curr_time,&tran_time);
|
||
close(soc);
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
TSsetClockFromUnix() - query the time from boot server and set the
|
||
vxworks clock.
|
||
*/
|
||
long TSsetClockFromUnix(void)
|
||
{
|
||
struct timespec tp;
|
||
int key;
|
||
unsigned long ulongtemp;
|
||
|
||
if(!TSinitialized) TSinit();
|
||
Debug0(3,"in TSsetClockFromUnix()\n");
|
||
|
||
if(TSgetUnixTime(&tp)!=0) return -1;
|
||
|
||
ulongtemp = TSepochNtpToUnix(&tp);
|
||
tp.tv_sec = (long)ulongtemp;
|
||
|
||
if(MAKE_DEBUG>=9)
|
||
printf("set time: %9.9lu.%9.9lu\n", tp.tv_sec,tp.tv_nsec);
|
||
|
||
/* set the vxWorks clock to the correct time */
|
||
if(clock_settime(CLOCK_REALTIME,&tp)<0)
|
||
{ Debug0(1,"clock_settime failed\n"); }
|
||
|
||
/* adjust time to use the EPICS EPOCH of 1990 */
|
||
/* this is wrong if leap seconds accounted for */
|
||
ulongtemp = TSepochUnixToEpics(&tp);
|
||
tp.tv_sec = ulongtemp;
|
||
|
||
/* set the EPICS event time table sync entry (current time) */
|
||
key=intLock();
|
||
TSdata.event_table[TSdata.sync_event]=tp;
|
||
TSdata.event_table[0]=tp;
|
||
intUnlock(key);
|
||
|
||
if(MAKE_DEBUG>=9)
|
||
printf("epics time: %9.9lu.%9.9lu\n",
|
||
TSdata.event_table[TSdata.sync_event].tv_sec,
|
||
TSdata.event_table[TSdata.sync_event].tv_nsec);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
TSsetClockFromMaster() - set the vxworks clock using the time from
|
||
the master timing IOC
|
||
*/
|
||
static long TSsetClockFromMaster()
|
||
{
|
||
struct timespec tp;
|
||
int key;
|
||
unsigned long secs;
|
||
|
||
Debug0(3,"in TSsetClockFromMaster()\n");
|
||
|
||
if(TSgetMasterTime(&tp)<0) return -1;
|
||
|
||
key=intLock();
|
||
TSdata.event_table[TSdata.sync_event]=tp;
|
||
TSdata.event_table[0]=tp;
|
||
intUnlock(key);
|
||
|
||
/* adjust time to use the Unix EPOCH of 1900 - not to good */
|
||
/* making this adjustment is not so good */
|
||
secs = TSepochEpicsToUnix(&tp);
|
||
tp.tv_sec = secs;
|
||
clock_settime(CLOCK_REALTIME,&tp);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**************************************************************************/
|
||
/* broadcast socket creation utilites */
|
||
/**************************************************************************/
|
||
|
||
/*
|
||
TSgetBroadcastSocket() - return a broadcast socket for a port, return
|
||
a sockaddr also.
|
||
*/
|
||
int TSgetBroadcastSocket(int port, struct sockaddr_in* sin)
|
||
{
|
||
int on=1;
|
||
int soc;
|
||
|
||
sin->sin_port=htons(port);
|
||
sin->sin_family=AF_INET;
|
||
sin->sin_addr.s_addr=htonl(INADDR_ANY);
|
||
if( (soc=epicsSocketCreate(AF_INET,SOCK_DGRAM,0)) < 0 )
|
||
{ perror("socket create failed"); return -1; }
|
||
|
||
setsockopt(soc,SOL_SOCKET,SO_BROADCAST,(char*)&on,sizeof(on));
|
||
|
||
if( bind(soc,(struct sockaddr*)sin,sizeof(struct sockaddr_in)) < 0 )
|
||
{ perror("socket bind failed"); close(soc); return -1; }
|
||
|
||
if( TSgetBroadcastAddr(soc,(struct sockaddr*)sin) < 0 ) return -1;
|
||
return soc;
|
||
}
|
||
|
||
/*
|
||
TSgetBroadcastAddr() - use the libCom/osi/osiSock.h calls
|
||
*/
|
||
long TSgetBroadcastAddr(int soc, struct sockaddr* sin)
|
||
{
|
||
ELLLIST tmpList;
|
||
osiSockAddrNode *pNode;
|
||
|
||
ellInit ( &tmpList );
|
||
osiSockDiscoverBroadcastAddresses ( &tmpList,soc,(const osiSockAddr*)sin);
|
||
/* take first */
|
||
pNode = (osiSockAddrNode *)ellFirst(&tmpList);
|
||
if(!pNode) {
|
||
printf("no broadcast address found\n"); return -1;
|
||
}
|
||
memcpy((char*)sin,(char*)&pNode->addr,sizeof(struct sockaddr));
|
||
/* cleanup */
|
||
while((pNode = (osiSockAddrNode *)ellFirst(&tmpList))) {
|
||
ellDelete(&tmpList,(ELLNODE *)pNode);
|
||
free(pNode);
|
||
}
|
||
return(0);
|
||
}
|
||
|
||
/**************************************************************************/
|
||
/* time stamp utility routines */
|
||
/**************************************************************************/
|
||
|
||
/*
|
||
TSsyncTheTime - given the current time (cts), and a compare time
|
||
stamp (ts), correct the current time clock to match ts.
|
||
*/
|
||
static long TSsyncTheTime(struct timespec* cts, struct timespec* ts)
|
||
{
|
||
int key;
|
||
long diffSec;
|
||
long diffNsec;
|
||
long diffSecAbs;
|
||
|
||
diffSec = (long)ts->tv_sec -(long) cts->tv_sec;
|
||
diffNsec = (long)ts->tv_nsec - (long)cts->tv_nsec;
|
||
diffSecAbs = (diffSec<0) ? -diffSec : diffSec;
|
||
/* see if clock can be corrected in sync window - up to 1 secs per tick */
|
||
if(diffSecAbs <= TSdata.sync_rate)
|
||
{
|
||
long diff;
|
||
long count;
|
||
count=sysClkRateGet()*TSdata.sync_rate;
|
||
diff = (TS_BILLION/count)*diffSec + diffNsec/count;
|
||
if(diff) {
|
||
key=intLock();
|
||
correction_count = count;
|
||
correction_factor = diff;
|
||
intUnlock(key);
|
||
}
|
||
Debug(5,"Correction Factor=.%9.9ld\n",correction_factor);
|
||
}
|
||
else
|
||
{
|
||
/* clock can not be corrected - jam it for now */
|
||
TSdata.event_table[TSdata.sync_event]=*ts;
|
||
if(MAKE_DEBUG>=7)
|
||
{
|
||
printf("Slave not in sync: mine=%9.9lu.%9.9lu!=%lu.%lu=other\n",
|
||
cts->tv_sec, cts->tv_nsec, ts->tv_sec, ts->tv_nsec);
|
||
printf("slave diff time: %9.9ld.%9.9ld\n",
|
||
diffSec,diffNsec);
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**************************************************************************/
|
||
/* varies server that can be running */
|
||
/**************************************************************************/
|
||
|
||
long TSstartSyncServer()
|
||
{
|
||
/* run at priority just above CA */
|
||
return taskSpawn("ts_syncS",TS_SYNC_SERVER_PRI,VX_FP_TASK|VX_STDIO,5000,
|
||
(FUNCPTR)TSsyncServer,0,0,0,0,0,0,0,0,0,0);
|
||
}
|
||
|
||
/*
|
||
TSsyncServer() - Server task that broadcasts the sync time stamp every
|
||
time a sync event is receive by the event system.
|
||
*/
|
||
static void TSsyncServer()
|
||
{
|
||
TSstampTrans stran;
|
||
struct sockaddr_in sin;
|
||
int soc;
|
||
|
||
if( (soc=TSgetBroadcastSocket(0,&sin)) <0)
|
||
{ Debug0(1,"TSgetBroadcastSocket failed\n"); return; }
|
||
sin.sin_port = htons(TSdata.slave_port);
|
||
stran.type=(TStype)htonl(TS_sync_msg);
|
||
stran.magic=htonl(TS_MAGIC);
|
||
stran.sync_rate=htonl(TSdata.sync_rate);
|
||
stran.clock_hz=htonl(TSdata.clock_hz);
|
||
while(1)
|
||
{
|
||
/* wait for a sync to occur */
|
||
semTake(TSdata.sync_occurred,WAIT_FOREVER);
|
||
|
||
if(!TSdata.ts_sync_valid)
|
||
{
|
||
int key;
|
||
struct timespec ts;
|
||
/* remember, the ts table has only an approximation to the
|
||
correct time before the event is actually up and running,
|
||
so validate the ts if this is the first sync - the vxWorks
|
||
clock has a good time in it
|
||
*/
|
||
if(TSgetTime(&ts)==0) TSdata.ts_sync_valid=1;
|
||
key=intLock();
|
||
/* don't adjust time from getTime() to EPICS epoch */
|
||
TSdata.event_table[TSdata.sync_event].tv_nsec=ts.tv_nsec;
|
||
TSdata.event_table[TSdata.sync_event].tv_sec=ts.tv_sec;
|
||
intUnlock(key);
|
||
}
|
||
stran.master_time.tv_sec=
|
||
htonl(TSdata.event_table[TSdata.sync_event].tv_sec);
|
||
stran.master_time.tv_nsec=
|
||
htonl(TSdata.event_table[TSdata.sync_event].tv_nsec);
|
||
if(sendto(soc,(char*)&stran,sizeof(stran),0,
|
||
(struct sockaddr*)&sin,sizeof(sin))<0)
|
||
{ perror("sendto failed"); }
|
||
Debug(8,"time send secs = %lu\n",stran.master_time.tv_sec);
|
||
Debug(8,"time send nsecs = %lu\n",stran.master_time.tv_nsec);
|
||
}
|
||
close(soc);
|
||
return;
|
||
}
|
||
|
||
/*-----------------------------------------------------------------------*/
|
||
long TSstartAsyncClient()
|
||
{
|
||
/* run at priority just above CA */
|
||
return taskSpawn("ts_Casync",TS_ASYNC_CLIENT_PRI,VX_FP_TASK|VX_STDIO,5000,
|
||
(FUNCPTR)TSasyncClient,0,0,0,0,0,0,0,0,0,0);
|
||
}
|
||
|
||
/*
|
||
TSasyncClient() - keep the async slave in sync with ntp server
|
||
while checking for master timing IOC to come up
|
||
|
||
algorithm:
|
||
- sync with master if available
|
||
- sync with unix if no master, checking every so often for master
|
||
- sync with unix if master goes away until master comes back
|
||
- continually check for master if no unix sync available
|
||
*/
|
||
long TSasyncClient()
|
||
{
|
||
BOOT_PARAMS bootParms;
|
||
TSstampTrans stran;
|
||
TS_NTP buf_ntp;
|
||
struct sockaddr_in sin_unix,sin_bc,sin_master;
|
||
int count,soc_unix,soc_master,soc_bc,buf_size;
|
||
struct timespec ts,diff_time,cts,curr_time;
|
||
unsigned long nsecs;
|
||
char host_addr[BOOT_ADDR_LEN];
|
||
int status;
|
||
|
||
Debug0(2,"in TSasyncClient()\n");
|
||
|
||
/* could open two sockets here, one to contact unix, one to find master */
|
||
|
||
/*------socket for unix server----------*/
|
||
if(envGetConfigParam(&EPICS_TS_NTP_INET,BOOT_ADDR_LEN,host_addr)==NULL
|
||
|| strlen(host_addr)==0)
|
||
{
|
||
/* use boot host if the environment variable not set */
|
||
bootStringToStruct(sysBootLine,&bootParms);
|
||
/* bootParms.had = host IP address */
|
||
strncpy(host_addr,bootParms.had,BOOT_ADDR_LEN);
|
||
}
|
||
|
||
if( (soc_unix=TSgetSocket(0,&sin_unix)) <0)
|
||
{ printf("TSgetSocket failed\n"); return -1; }
|
||
|
||
status = aToIPAddr (host_addr, UDP_NTP_PORT, &sin_unix);
|
||
if (status) {
|
||
printf("TSasyncClient: bad host name or IP address host %s port %d\n",
|
||
host_addr,UDP_NTP_PORT);
|
||
close (soc_unix);
|
||
return -1;
|
||
}
|
||
|
||
/*------socket for finding master----------*/
|
||
if( (soc_bc=TSgetBroadcastSocket(0,&sin_bc)) <0)
|
||
{ printf("TSgetBroadcastSocket failed\n"); close(soc_unix); return -1; }
|
||
|
||
sin_bc.sin_port = htons(TSdata.master_port);
|
||
/*-----------------------------------------*/
|
||
|
||
while(1)
|
||
{
|
||
/* stop the clock from correcting here - probably better to use
|
||
a watch dog to stop the clock correction */
|
||
|
||
if(TSdata.state==TS_master_alive)
|
||
{
|
||
/* get socket to master */
|
||
Debug0(5,"async_client(): master_alive\n");
|
||
if( (soc_master=TSgetSocket(0,&sin_master)) <0)
|
||
{ Debug0(1,"TSgetSocket failed\n"); }
|
||
|
||
while(TSdata.state==TS_master_alive)
|
||
{
|
||
/* sync with the master as long as it is up */
|
||
Debug0(5,"async_client(): syncing with master\n");
|
||
stran.type=(TStype)htonl(TS_time_request);
|
||
stran.magic=htonl(TS_MAGIC);
|
||
|
||
if(TSgetData((char*)&stran,sizeof(stran),soc_master,
|
||
&TSdata.master,NULL,&diff_time)<0)
|
||
{
|
||
Debug0(2,"no reply from master server\n");
|
||
TSdata.state=TS_master_dead;
|
||
close(soc_master);
|
||
}
|
||
else
|
||
{
|
||
if(ntohl(stran.magic)==TS_MAGIC)
|
||
{
|
||
/* sync the time with master's here - 2 ticks */
|
||
cts=TSdata.event_table[TSdata.sync_event];
|
||
curr_time.tv_sec=ntohl(stran.current_time.tv_sec);
|
||
curr_time.tv_nsec=ntohl(stran.current_time.tv_nsec);
|
||
TSsyncTheTime(&cts,&curr_time);
|
||
Debug(8,"master sec = %lu\n",curr_time.tv_sec);
|
||
Debug(8,"my sec = %lu\n", cts.tv_sec);
|
||
}
|
||
else
|
||
{
|
||
TSprintf("TSasyncClient: invalid packet recved\n");
|
||
}
|
||
taskDelay(sysClkRateGet()*TSdata.sync_rate);
|
||
}
|
||
}
|
||
}
|
||
else if(TSdata.async_type==TS_async_ntp)
|
||
{
|
||
/* sync with unix server using NTP - master check now and then */
|
||
Debug0(5,"async_client(): using NTP sync\n");
|
||
count=0;
|
||
while(TSdata.state==TS_master_dead)
|
||
{
|
||
Debug0(5,"async_client(): syncing with unix\n");
|
||
memset(&buf_ntp,0,sizeof(buf_ntp));
|
||
buf_ntp.info[0]=0x0b;
|
||
buf_size=sizeof(buf_ntp);
|
||
|
||
if(TSgetData((char*)&buf_ntp,sizeof(buf_ntp),soc_unix,
|
||
(struct sockaddr*)&sin_unix,NULL,&diff_time)<0)
|
||
{
|
||
Debug0(2,"no reply from NTP server\n");
|
||
}
|
||
else
|
||
{
|
||
unsigned long ulongtemp;
|
||
/* get the current time */
|
||
cts=TSdata.event_table[TSdata.sync_event];
|
||
/* adjust the ntp time */
|
||
ts.tv_sec = ntohl(buf_ntp.transmit_ts.tv_sec);
|
||
ulongtemp = TSepochNtpToEpics(&ts);
|
||
ts.tv_sec = (long)ulongtemp;
|
||
nsecs=ntohl(buf_ntp.transmit_ts.tv_nsec);
|
||
ts.tv_nsec = TSfractionToNano(nsecs);
|
||
TSsyncTheTime(&cts,&ts);
|
||
}
|
||
if(count==0)
|
||
{
|
||
stran.type=(TStype)htonl(TS_time_request);
|
||
stran.magic=htonl(TS_MAGIC);
|
||
|
||
if(TSgetData((char*)&stran,sizeof(stran),soc_bc,
|
||
(struct sockaddr*)&sin_bc,&TSdata.master,&diff_time)<0)
|
||
{ Debug0(2,"no reply from master server\n"); }
|
||
else
|
||
{
|
||
TSdata.state=TS_master_alive;
|
||
Debug(8,"master port = %d\n",
|
||
ntohs( ((struct sockaddr_in*)
|
||
&TSdata.master)->sin_port));
|
||
}
|
||
|
||
count=(TSdata.sync_rate>TS_SECS_ASYNC_TRY_MASTER)?
|
||
TS_SECS_ASYNC_TRY_MASTER:
|
||
TS_SECS_ASYNC_TRY_MASTER/TSdata.sync_rate;
|
||
}
|
||
else count--;
|
||
taskDelay(sysClkRateGet()*TSdata.sync_rate);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* try to find a master */
|
||
while(TSdata.state==TS_master_dead)
|
||
{
|
||
stran.type=(TStype)htonl(TS_time_request);
|
||
stran.magic=htonl(TS_MAGIC);
|
||
|
||
if(TSgetData((char*)&stran,sizeof(stran),soc_bc,
|
||
(struct sockaddr*)&sin_bc,&TSdata.master,&diff_time)<0)
|
||
{ Debug0(2,"no reply from master server\n"); }
|
||
else
|
||
TSdata.state=TS_master_alive;
|
||
taskDelay(sysClkRateGet()*TS_SECS_SYNC_TRY_MASTER);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static long TSstartSyncClient()
|
||
{
|
||
return taskSpawn("ts_syncC",TS_SYNC_CLIENT_PRI,VX_FP_TASK|VX_STDIO,5000,
|
||
(FUNCPTR)TSsyncClient,0,0,0,0,0,0,0,0,0,0);
|
||
}
|
||
|
||
/*
|
||
TSsyncClient() - Client task that listens for sync time stamp on a port
|
||
and verifies with this IOC sync time stamp. This is only to be used
|
||
with event system timing.
|
||
*/
|
||
static void TSsyncClient()
|
||
{
|
||
TSstampTrans stran;
|
||
struct sockaddr_in sin;
|
||
struct sockaddr fs;
|
||
int num,mlen,soc,fl;
|
||
struct timespec mast_time;
|
||
fd_set readfds;
|
||
int key;
|
||
|
||
/* if no master then use unix and ntp to sync time
|
||
all aync slaves will operate in polled mode */
|
||
|
||
if(TSdata.type!=TS_sync_slave) return;
|
||
|
||
/* check for master to be there every so many minutes */
|
||
if(TSdata.state != TS_master_alive)
|
||
while( TSsetClockFromMaster()<0 )
|
||
taskDelay(sysClkRateGet()*TS_SECS_SYNC_TRY_MASTER);
|
||
if( (soc=TSgetSocket(TSdata.slave_port,&sin)) <0)
|
||
{ Debug0(1,"TSgetSocket failed\n"); return; }
|
||
while(1)
|
||
{
|
||
FD_ZERO(&readfds);
|
||
FD_SET(soc,&readfds);
|
||
|
||
/* could set up timeout for (sync_rate + epsilon)
|
||
to see if one is missed */
|
||
|
||
num=select(FD_SETSIZE,&readfds,(fd_set*)NULL,(fd_set*)NULL,NULL);
|
||
if(num==ERROR) { perror("select failed"); continue; }
|
||
|
||
fl = sizeof(fs);
|
||
if((mlen=recvfrom(soc,(char*)&stran,sizeof(stran),0,&fs,&fl))<0)
|
||
{ perror("recvfrom failed"); continue; }
|
||
|
||
if(ntohl(stran.magic)!=TS_MAGIC)
|
||
{
|
||
TSprintf("TSsyncClient: invalid packet received\n");
|
||
continue;
|
||
}
|
||
|
||
/* get the master time out of packet */
|
||
mast_time.tv_sec=ntohl(stran.master_time.tv_sec);
|
||
mast_time.tv_nsec=ntohl(stran.master_time.tv_nsec);
|
||
|
||
Debug0(6,"Received sync request from master\n");
|
||
if(MAKE_DEBUG>=8)
|
||
{
|
||
printf("time received=%9.9lu.%9.9lu\n",
|
||
mast_time.tv_sec,mast_time.tv_nsec);
|
||
}
|
||
|
||
/* verify the sync event with this one */
|
||
if( TSdata.event_table[TSdata.sync_event].tv_sec!=mast_time.tv_sec
|
||
|| TSdata.event_table[TSdata.sync_event].tv_nsec!=mast_time.tv_nsec)
|
||
{
|
||
TSprintf("sync Slave not in sync: %lu,%lu != %lu,%lu\n",
|
||
TSdata.event_table[TSdata.sync_event].tv_sec,
|
||
TSdata.event_table[TSdata.sync_event].tv_nsec,
|
||
mast_time.tv_sec, mast_time.tv_nsec);
|
||
key=intLock();
|
||
TSdata.event_table[TSdata.sync_event].tv_sec=mast_time.tv_sec;
|
||
TSdata.event_table[TSdata.sync_event].tv_nsec=mast_time.tv_nsec;
|
||
intUnlock(key);
|
||
}
|
||
}
|
||
close(soc);
|
||
return;
|
||
}
|
||
|
||
static long TSstartStampServer()
|
||
{
|
||
return taskSpawn("ts_stamp",TS_STAMP_SERVER_PRI,VX_FP_TASK|VX_STDIO,5000,
|
||
(FUNCPTR)TSstampServer,0,0,0,0,0,0,0,0,0,0);
|
||
}
|
||
|
||
/*
|
||
TSstampServer() - Server task on master timing IOC that listens for
|
||
time stamp requests and sync request from slaves.
|
||
*/
|
||
static void TSstampServer()
|
||
{
|
||
TSstampTrans stran;
|
||
struct sockaddr_in sin;
|
||
struct sockaddr fs;
|
||
struct timespec ts;
|
||
int mlen,soc,fl;
|
||
TStype type;
|
||
|
||
if( (soc=TSgetSocket(TSdata.master_port,&sin)) <0)
|
||
{ Debug0(1,"TSgetSocket failed\n"); return; }
|
||
|
||
stran.type=(TStype)htonl(TS_time_request);
|
||
stran.magic=htonl(TS_MAGIC);
|
||
|
||
while(1)
|
||
{
|
||
fl=sizeof(fs);
|
||
if((mlen=recvfrom(soc,(char*)&stran,sizeof(stran),0,&fs,&fl))<0)
|
||
{ perror("recvfrom failed"); continue; }
|
||
|
||
if(ntohl(stran.magic)!=TS_MAGIC)
|
||
{
|
||
TSprintf("TSstampServer(): invalid packet received\n");
|
||
continue;
|
||
}
|
||
|
||
type=(TStype)ntohl(stran.type);
|
||
switch(type)
|
||
{
|
||
case TS_time_request:
|
||
TSgetTimeStamp(TSdata.sync_event,&ts);
|
||
stran.master_time.tv_sec=htonl(ts.tv_sec);
|
||
stran.master_time.tv_nsec=htonl(ts.tv_nsec);
|
||
|
||
if(TSdata.has_event_system)
|
||
TSaccurateTimeStamp(&ts);
|
||
else
|
||
TScurrentTimeStamp(&ts);
|
||
stran.current_time.tv_sec=htonl(ts.tv_sec);
|
||
stran.current_time.tv_nsec=htonl(ts.tv_nsec);
|
||
stran.sync_rate = htonl(TSdata.sync_rate);
|
||
stran.clock_hz = htonl(TSdata.clock_hz);
|
||
Debug0(4,"Slave requesting time\n");
|
||
if(sendto(soc,(char*)&stran,sizeof(stran),0,&fs,fl)<0)
|
||
{ perror("sendto to slave failed"); }
|
||
break;
|
||
case TS_sync_request: TSforceSync(0); break;
|
||
default:
|
||
Debug0(1,"Unknown transaction type from slave\n");
|
||
}
|
||
}
|
||
close(soc);
|
||
return;
|
||
}
|
||
|
||
/*************************************************************************/
|
||
/* utility routines for managing time retrieval */
|
||
/*************************************************************************/
|
||
|
||
/* get the current time from time stamp support software */
|
||
long TScurrentTimeStamp(struct timespec* sp)
|
||
{
|
||
TSgetTimeStamp(0,sp);
|
||
return 0;
|
||
}
|
||
|
||
/* get the current time from time stamp support software */
|
||
long TSaccurateTimeStamp(struct timespec* sp)
|
||
{
|
||
struct timespec ts;
|
||
unsigned long ticks;
|
||
|
||
if(!TSinitialized) TSinit();
|
||
if(TSdata.has_direct_time==1)
|
||
{
|
||
TSgetTime(sp);
|
||
return 0;
|
||
}
|
||
|
||
TSgetTicks(0,&ticks); /* add in the board time */
|
||
*sp = TSdata.event_table[TSdata.sync_event];
|
||
|
||
/* calculate a time stamp from the tick count */
|
||
ts.tv_sec = ticks / TSdata.clock_hz;
|
||
ts.tv_nsec=(ticks-(ts.tv_sec*TSdata.clock_hz))*TSdata.clock_conv;
|
||
|
||
sp->tv_sec += ts.tv_sec;
|
||
sp->tv_nsec += ts.tv_nsec;
|
||
|
||
/* adjust seconds if needed */
|
||
if(sp->tv_nsec >= TS_BILLION)
|
||
{
|
||
sp->tv_sec++;
|
||
sp->tv_nsec -= TS_BILLION;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* get the current time from vxWorks time clock */
|
||
static long TSgetCurrentTime(struct timespec* ts)
|
||
{
|
||
long rc;
|
||
rc=clock_gettime(CLOCK_REALTIME,ts);
|
||
ts->tv_sec = TSepochUnixToEpics(ts);
|
||
return rc;
|
||
}
|
||
|
||
/* routine for causing sync to occur (not a hardware one) */
|
||
static long TSforceSoftSync(int Card)
|
||
{
|
||
semGive(TSdata.sync_occurred);
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
TSaddStamp - Add time stamp op1 to time stamp op2 giving time stamp
|
||
result
|
||
*/
|
||
static void TSaddStamp( struct timespec* result,
|
||
struct timespec* op1, struct timespec* op2)
|
||
{
|
||
result->tv_sec = op1->tv_sec + op2->tv_sec;
|
||
result->tv_nsec = op1->tv_nsec + op2->tv_nsec;
|
||
|
||
/* adjust seconds if needed */
|
||
if(result->tv_nsec >= TS_BILLION)
|
||
{
|
||
result->tv_sec++;
|
||
result->tv_nsec -= TS_BILLION;
|
||
}
|
||
return;
|
||
}
|
||
|
||
/*
|
||
TScalcDiff() - Calculate the difference between to time stamps. The
|
||
difference between arguments 'a' and 'b' is returned in 'diff'. The
|
||
return value from this routine is the direction: (1)=a>b, (-1)=a<b.
|
||
*/
|
||
static long TScalcDiff(struct timespec* a,
|
||
struct timespec* b, struct timespec* diff)
|
||
{
|
||
struct timespec *big_time,*small_time;
|
||
int dir;
|
||
|
||
/* ---------------------this block sucks--------------------*/
|
||
/* calculate difference - this sucks */
|
||
if(a->tv_sec < b->tv_sec) { dir=-1; big_time=b; small_time=a; }
|
||
else if(a->tv_sec > b->tv_sec) { dir=1; small_time=b; big_time=a; }
|
||
else if(a->tv_nsec == b->tv_nsec){ dir=0; small_time=b; big_time=a;}
|
||
else if(a->tv_nsec < b->tv_nsec) { dir=-1; big_time=b; small_time=a; }
|
||
else { dir=1; small_time=b; big_time=a; }
|
||
|
||
diff->tv_sec=big_time->tv_sec-small_time->tv_sec;
|
||
|
||
if(big_time->tv_nsec>=small_time->tv_nsec)
|
||
diff->tv_nsec=big_time->tv_nsec-small_time->tv_nsec;
|
||
else
|
||
{
|
||
diff->tv_nsec = TS_BILLION + big_time->tv_nsec-small_time->tv_nsec;
|
||
diff->tv_sec--;
|
||
}
|
||
/* ---------------------this block sucks--------------------*/
|
||
return dir;
|
||
}
|
||
|
||
/**************************************************************************/
|
||
/* more routines for managing sockets */
|
||
/**************************************************************************/
|
||
|
||
/* get a UDP socket for a port, return a sockaddr for it. */
|
||
static int TSgetSocket(int port, struct sockaddr_in* sin)
|
||
{
|
||
int soc;
|
||
|
||
sin->sin_port=htons(port);
|
||
sin->sin_family=AF_INET;
|
||
sin->sin_addr.s_addr=htonl(INADDR_ANY);
|
||
if( (soc=epicsSocketCreate(AF_INET,SOCK_DGRAM,0)) < 0 )
|
||
{ perror("socket create failed"); return -1; }
|
||
Debug(5,"sizeof sin = %d\n", (int) sizeof(struct sockaddr_in));
|
||
if( bind(soc,(struct sockaddr*)sin,sizeof(struct sockaddr_in)) < 0 )
|
||
{ perror("socket bind failed"); close(soc); return -1; }
|
||
|
||
return soc;
|
||
}
|
||
|
||
/* attempt to get data from a socket after sending a request to it.
|
||
0ptionally return round trip time and the sockaddr of the responsedent.
|
||
*/
|
||
static long TSgetData(char* buf, int buf_size, int soc,
|
||
struct sockaddr* to_sin, struct sockaddr* from_sin,
|
||
struct timespec* round_trip)
|
||
{
|
||
int retry_count=0;
|
||
int num,mlen,flen;
|
||
fd_set readfds;
|
||
volatile unsigned long s,us;
|
||
struct timeval timeOut;
|
||
struct timespec send_time,recv_time;
|
||
struct sockaddr local_sin;
|
||
|
||
if (!from_sin) {
|
||
/* Tornado 2.2 doesn't like NULLs in recvfrom() */
|
||
from_sin = &local_sin;
|
||
}
|
||
flen = sizeof(*from_sin);
|
||
|
||
/*
|
||
* joh 08-26-99
|
||
* added this code which removes responses laying around from
|
||
* requests made in the past that timed out
|
||
*/
|
||
Debug(8,"removing stale responses %s\n", "");
|
||
while (1) {
|
||
int status;
|
||
|
||
status = ioctl (soc, FIONREAD, (int) &mlen);
|
||
if (status<0) {
|
||
Debug(1,"ioctl FIONREAD failed because \"%s\"?\n", strerror(errno));
|
||
break;
|
||
}
|
||
else if (mlen==0) {
|
||
break;
|
||
}
|
||
Debug(1,"removing stale response of %d bytes\n", mlen);
|
||
recvfrom(soc,buf,buf_size,0,from_sin,&flen);
|
||
}
|
||
|
||
/* convert millisecond time out to seconds/microseconds */
|
||
s=TSdata.time_out/1000; us=(TSdata.time_out-(s*1000))*1000;
|
||
Debug(6,"time_out Second=%lu\n",s);
|
||
Debug(6,"time_out Microsecond=%lu\n",us);
|
||
do
|
||
{
|
||
Debug(8,"sendto port %d\n",
|
||
ntohs(((struct sockaddr_in*)to_sin)->sin_port));
|
||
if(round_trip) clock_gettime(CLOCK_REALTIME,&send_time);
|
||
if( sendto(soc,buf,buf_size,0,to_sin,sizeof(struct sockaddr)) < 0 )
|
||
{ perror("sendto failed"); return -1; }
|
||
FD_ZERO(&readfds); FD_SET(soc,&readfds);
|
||
timeOut.tv_sec=s; timeOut.tv_usec=us;
|
||
num=select(FD_SETSIZE,&readfds,(fd_set*)NULL,(fd_set*)NULL,&timeOut);
|
||
Debug(9,"select returned %d\n", num);
|
||
if(round_trip) clock_gettime(CLOCK_REALTIME,&recv_time);
|
||
if(num==ERROR) { perror("select failed"); return -1; }
|
||
}
|
||
while(num==0 && ++retry_count<TS_RETRY_COUNT);
|
||
if(retry_count >= TS_RETRY_COUNT)
|
||
{ Debug0(5,"TSgetData - retry count exceeded\n"); return -1; }
|
||
else
|
||
{
|
||
/* data available */
|
||
mlen=recvfrom(soc,buf,buf_size,0,from_sin,&flen);
|
||
if(mlen < 0) { perror("recvfrom failed"); return -1; }
|
||
if(from_sin)
|
||
{
|
||
Debug(8,"recvfrom port %d\n",
|
||
ntohs(((struct sockaddr_in*)from_sin)->sin_port));
|
||
Debug(8,"flen = %d\n",flen);
|
||
Debug(8,"mlen = %d\n",mlen);
|
||
}
|
||
if(round_trip) TScalcDiff(&send_time,&recv_time,round_trip);
|
||
}
|
||
return mlen;
|
||
}
|
||
|
||
/*************************************************************************/
|
||
/* all the functions that follow are test functions */
|
||
/*************************************************************************/
|
||
|
||
/* test function to print the IOC real time clock value. */
|
||
void TSprintRealTime()
|
||
{
|
||
struct timespec tp;
|
||
|
||
TSgetTime(&tp);
|
||
printf("real time clock = %lu,%lu\n",tp.tv_sec,tp.tv_nsec);
|
||
printf("EPICS clock = %lu,%lu\n",
|
||
TSdata.event_table[TSdata.sync_event].tv_sec,
|
||
TSdata.event_table[TSdata.sync_event].tv_nsec);
|
||
return;
|
||
}
|
||
|
||
/* test function to print an event time stamp */
|
||
void TSprintTimeStamp(int num)
|
||
{
|
||
struct timespec tp;
|
||
|
||
TSgetTimeStamp(num,&tp);
|
||
printf("event %d occurred: %lu.%lu\n",num,tp.tv_sec,tp.tv_nsec);
|
||
return;
|
||
}
|
||
|
||
/* test function to print the current time using event system */
|
||
void TSprintCurrentTime()
|
||
{
|
||
struct timespec tp;
|
||
|
||
TScurrentTimeStamp(&tp);
|
||
printf("Current Event System time: %lu.%lu\n",tp.tv_sec,tp.tv_nsec);
|
||
TSaccurateTimeStamp(&tp);
|
||
printf("Accurate Event System time: %lu.%lu\n",tp.tv_sec,tp.tv_nsec);
|
||
return;
|
||
}
|
||
|
||
/* test function to query the boot server for the current time. */
|
||
void TSprintUnixTime()
|
||
{
|
||
struct timespec ts;
|
||
|
||
if(TSgetUnixTime(&ts)!=0)
|
||
{
|
||
printf("Could not get Unix time\n");
|
||
return;
|
||
}
|
||
printf("boot server time clock = %lu, %lu\n",ts.tv_sec,ts.tv_nsec);
|
||
return;
|
||
}
|
||
|
||
/* test function to print the master timing IOC time stamp. */
|
||
void TSprintMasterTime()
|
||
{
|
||
struct timespec ts;
|
||
|
||
if(TSgetMasterTime(&ts)!=0)
|
||
{
|
||
printf("Could not get Unix time\n");
|
||
return;
|
||
}
|
||
printf("master time clock = %lu, %lu\n",ts.tv_sec,ts.tv_nsec);
|
||
return;
|
||
}
|
||
|
||
/* gross and horrid example */
|
||
long TSgetFirstOfYearVx(struct timespec* ts)
|
||
{
|
||
time_t tloc;
|
||
struct tm t;
|
||
|
||
if(!TSinitialized) TSinit();
|
||
time(&tloc); /* retrieve the current time */
|
||
localtime_r(&tloc,&t);
|
||
t.tm_sec=0;
|
||
t.tm_min=0;
|
||
t.tm_hour=t.tm_isdst?1:0;
|
||
t.tm_mday=1;
|
||
t.tm_mon=0;
|
||
tloc=mktime(&t);
|
||
ts->tv_sec=tloc;
|
||
ts->tv_nsec=0;
|
||
return 0;
|
||
}
|