From 489af50c7a67aed36b9a68aee65b0c3abf822828 Mon Sep 17 00:00:00 2001 From: "W. Eric Norum" Date: Mon, 14 Apr 2008 19:49:47 +0000 Subject: [PATCH] Preliminary general time changes. --- src/libCom/osi/os/vxWorks/drvNTPTime.c | 245 +++++++++++++++++++++++++ src/libCom/osi/os/vxWorks/drvNTPTime.h | 14 ++ src/libCom/osi/os/vxWorks/drvVxTime.c | 205 +++++++++++++++++++++ src/libCom/osi/os/vxWorks/drvVxTime.h | 14 ++ src/libCom/osi/os/vxWorks/iocClock.c | 39 ---- src/libCom/osi/os/vxWorks/iocClock.h | 5 +- src/libCom/osi/os/vxWorks/osdTime.cpp | 10 + 7 files changed, 489 insertions(+), 43 deletions(-) create mode 100644 src/libCom/osi/os/vxWorks/drvNTPTime.c create mode 100644 src/libCom/osi/os/vxWorks/drvNTPTime.h create mode 100644 src/libCom/osi/os/vxWorks/drvVxTime.c create mode 100644 src/libCom/osi/os/vxWorks/drvVxTime.h diff --git a/src/libCom/osi/os/vxWorks/drvNTPTime.c b/src/libCom/osi/os/vxWorks/drvNTPTime.c new file mode 100644 index 000000000..290cbd6d0 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/drvNTPTime.c @@ -0,0 +1,245 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* iocClock.c */ + +/* Author: Marty Kraimer Date: 16JUN2000 */ + +/* This file is originally named as iocClock.c and provide the NTP time + * as default time source if no other time source registered during initialization. + * Now we made some minor change to add this to the time provider list */ + +/* Sheng Peng @ SNS ORNL 07/2004 */ +/* Version 1.1 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsTypes.h" +#include "cantProceed.h" +#include "errlog.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "epicsTime.h" +#include "envDefs.h" + +#define BILLION 1000000000 +#define NTPTimeSyncInterval 10.0 + +#define NTPTIME_DRV_VERSION "NTPTime Driver Version 1.2" + +#include "epicsGeneralTime.h" + +static void NTPTimeSyncNTP(void); +static int NTPTimeGetCurrent(epicsTimeStamp *pDest); + +long NTPTime_Report(int level); + +typedef struct NTPTimePvt { + int synced; /* if never synced, we can't use it */ + epicsMutexId lock; + epicsTimeStamp clock; + unsigned long lastTick; + epicsUInt32 nanosecondsPerTick; + int tickRate; + int ticksToSkip; + const char *pserverAddr; +}NTPTimePvt; +static NTPTimePvt *pNTPTimePvt = 0; +static int nConsecutiveBad = 0; + +extern char* sysBootLine; + +static void NTPTimeSyncNTP(void) +{ + struct timespec Currtime; + epicsTimeStamp epicsTime; + int status; + int prevStatusBad = 0; + + while(1) { + double diffTime; + epicsThreadSleep(NTPTimeSyncInterval); + pNTPTimePvt->tickRate = sysClkRateGet(); + status = sntpcTimeGet((char *)pNTPTimePvt->pserverAddr, + pNTPTimePvt->tickRate,&Currtime); + if(status) { + ++nConsecutiveBad; + /*wait 1 minute before reporting failure*/ + if(nConsecutiveBad<(60/NTPTimeSyncInterval)) continue; + if(!prevStatusBad) + errlogPrintf("NTPTimeSyncWithNTPserver: sntpcTimeGet %s\n", + strerror(errno)); + prevStatusBad = 1; + pNTPTimePvt->synced = FALSE; + continue; + } + nConsecutiveBad=0; + if(prevStatusBad) { + errlogPrintf("NTPTimeSyncWithNTPserver: sntpcTimeGet OK\n"); + prevStatusBad = 0; + } + epicsTimeFromTimespec(&epicsTime,&Currtime); + epicsMutexMustLock(pNTPTimePvt->lock); + diffTime = epicsTimeDiffInSeconds(&epicsTime,&pNTPTimePvt->clock); + if(diffTime>=0.0) { + pNTPTimePvt->clock = epicsTime; + pNTPTimePvt->ticksToSkip = 0;/* fix bug here */ + } else {/*dont go back in time*/ + pNTPTimePvt->ticksToSkip = (int) ((-1)*diffTime*pNTPTimePvt->tickRate);/* fix bug here */ + } + pNTPTimePvt->lastTick = tickGet(); + pNTPTimePvt->synced = TRUE; + epicsMutexUnlock(pNTPTimePvt->lock); + } +} + +static long NTPTime_InitOnce(int priority) +{ + int status; + struct timespec Currtime; + epicsTimeStamp epicsTime; + + pNTPTimePvt = callocMustSucceed(1,sizeof(NTPTimePvt),"NTPTime_Init"); + + memset(pNTPTimePvt, 0, sizeof(NTPTimePvt)); + pNTPTimePvt->synced = FALSE; + pNTPTimePvt->lock = epicsMutexCreate(); + pNTPTimePvt->nanosecondsPerTick = BILLION/sysClkRateGet(); + pNTPTimePvt->tickRate = sysClkRateGet(); + /* look first for environment variable or CONFIG_SITE_ENV default */ + pNTPTimePvt->pserverAddr = envGetConfigParamPtr(&EPICS_TS_NTP_INET); + if(!pNTPTimePvt->pserverAddr) { /* if neither, use the boot host */ + BOOT_PARAMS bootParms; + static char host_addr[BOOT_ADDR_LEN]; + bootStringToStruct(sysBootLine,&bootParms); + /* bootParms.had = host IP address */ + strncpy(host_addr,bootParms.had,BOOT_ADDR_LEN); + pNTPTimePvt->pserverAddr = host_addr; + } + if(!pNTPTimePvt->pserverAddr) { + errlogPrintf("No NTP server is defined. Clock does not work\n"); + return -1; + } + /* if TIMEZONE not defined, set it from EPICS_TIMEZONE */ + if (getenv("TIMEZONE") == NULL) { + const char *timezone = envGetConfigParamPtr(&EPICS_TIMEZONE); + if(timezone == NULL) { + printf("NTPTime_Init: No Time Zone Information\n"); + } else { + epicsEnvSet("TIMEZONE",timezone); + } + } + + /* try to sync with NTP server once here */ + pNTPTimePvt->tickRate = sysClkRateGet(); + status = sntpcTimeGet((char *)pNTPTimePvt->pserverAddr,pNTPTimePvt->tickRate,&Currtime); + if(status) + {/* sync failed */ + printf("First try to sync with NTP server failed!\n"); + } + else + {/* sync OK */ + epicsTimeFromTimespec(&epicsTime,&Currtime); + epicsMutexMustLock(pNTPTimePvt->lock); + pNTPTimePvt->clock = epicsTime; + pNTPTimePvt->lastTick = tickGet(); + pNTPTimePvt->synced = TRUE; + epicsMutexUnlock(pNTPTimePvt->lock); + printf("First try to sync with NTP server succeed!\n"); + } + + epicsThreadCreate("NTPTimeSyncNTP", + epicsThreadPriorityHigh, + epicsThreadGetStackSize(epicsThreadStackSmall), + (EPICSTHREADFUNC)NTPTimeSyncNTP,0); + + /* register to link list */ + generalTimeCurrentTpRegister("NTP", priority, NTPTimeGetCurrent); + + return 0; +} + +struct InitInfo { + int priority; + long retval; +}; +static void NTPTime_InitOnceWrapper(void *arg) +{ + struct InitInfo *pargs = (struct InitInfo *)arg; + pargs->retval = NTPTime_InitOnce(pargs->priority); +} + +long NTPTime_Init(int priority) +{ + struct InitInfo args; + static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; + + args.priority = priority; + epicsThreadOnce(&onceId, NTPTime_InitOnceWrapper, &args); + return args.retval; +} + +static int NTPTimeGetCurrent(epicsTimeStamp *pDest) +{ + unsigned long currentTick,nticks,nsecs; + + if(!pNTPTimePvt->synced) return epicsTimeERROR; + + epicsMutexMustLock(pNTPTimePvt->lock); + currentTick = tickGet(); + while(currentTick!=pNTPTimePvt->lastTick) { + nticks = (currentTick>pNTPTimePvt->lastTick) + ? (currentTick - pNTPTimePvt->lastTick) + : (currentTick + (ULONG_MAX - pNTPTimePvt->lastTick)); + if(pNTPTimePvt->ticksToSkip>0) {/*dont go back in time*/ + if(nticksticksToSkip) { + /*pNTPTimePvt->ticksToSkip -= nticks;*/ /* fix bug here */ + break; + } + nticks -= pNTPTimePvt->ticksToSkip; + pNTPTimePvt->ticksToSkip = 0; /* fix bug here */ + } + pNTPTimePvt->lastTick = currentTick; + pNTPTimePvt->tickRate = sysClkRateGet(); + nsecs = nticks/pNTPTimePvt->tickRate; + nticks = nticks - nsecs*pNTPTimePvt->tickRate; + pNTPTimePvt->clock.nsec += nticks * pNTPTimePvt->nanosecondsPerTick; + if(pNTPTimePvt->clock.nsec>=BILLION) { + ++nsecs; + pNTPTimePvt->clock.nsec -= BILLION; + } + pNTPTimePvt->clock.secPastEpoch += nsecs; + } + *pDest = pNTPTimePvt->clock; + epicsMutexUnlock(pNTPTimePvt->lock); + return(0); +} + +long NTPTime_Report(int level) +{ + printf(NTPTIME_DRV_VERSION"\n"); + + if(!pNTPTimePvt) + {/* drvNTPTime is not used, we just report version then quit */ + printf("NTP time driver is not initialized yet!\n\n"); + } + else + { + printf("\n"); + } + return 0; +} diff --git a/src/libCom/osi/os/vxWorks/drvNTPTime.h b/src/libCom/osi/os/vxWorks/drvNTPTime.h new file mode 100644 index 000000000..4242f26b5 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/drvNTPTime.h @@ -0,0 +1,14 @@ +#ifndef _INC_drvDevNTPTime +#define _INC_drvDevNTPTime + +#ifdef __cplusplus +extern "C" { +#endif + +long NTPTime_Init(int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libCom/osi/os/vxWorks/drvVxTime.c b/src/libCom/osi/os/vxWorks/drvVxTime.c new file mode 100644 index 000000000..8cd464f72 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/drvVxTime.c @@ -0,0 +1,205 @@ +/* Sheng Peng @ SNS ORNL 07/2004 */ +/* Version 1.1 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsTypes.h" +#include "cantProceed.h" +#include "errlog.h" +#include "epicsThread.h" +#include "osdThread.h" +#include "epicsMutex.h" +#include "epicsTime.h" +#include "epicsTimer.h" +#include "epicsInterrupt.h" +#include "envDefs.h" + +#include +#include +#include + +#define BILLION 1000000000 +#define VXWORKS_TO_EPICS_EPOCH 631152000UL + +#define VXTIME_DRV_VERSION "vxWorks Ticks Time Driver Version 1.2" +#define SYNC_PERIOD 60.0 + +#include "epicsGeneralTime.h" + +static void VxTimeSyncTime(void *param); +long VxTime_Init(int priority); +static int VxTimeGetCurrent(epicsTimeStamp *pDest); +static void VxTime_StartSync(int junk); + +long VxTime_Report(int level); + +typedef struct VxTimePvt { + BOOL synced; /* if never synced, we can't use it */ + epicsMutexId lock; + epicsTimerId sync_timer; + epicsTimeStamp lastReportedTS; + epicsTimeStamp lastSyncedTS; + struct timespec lastSyncedVxTime; + int priority; + int synced_priority; +}VxTimePvt; + +static VxTimePvt *pVxTimePvt = 0; + +static void VxTimeSyncTime(void *param) +{ + epicsTimeStamp now; + struct timespec ticks_now; + + /* Ask for the best time available, not including ourselves */ + if (generalTimeGetExceptPriority(&now, &pVxTimePvt->synced_priority, pVxTimePvt->priority) == epicsTimeOK) { + /* It's a good time, we unconditionally sync ourselves to it, as + this driver is the bottom of the heap */ + clock_gettime( CLOCK_REALTIME,&ticks_now ); + + epicsMutexMustLock(pVxTimePvt->lock); + pVxTimePvt->lastSyncedTS = now; + pVxTimePvt->lastSyncedVxTime = ticks_now; + pVxTimePvt->synced = TRUE; + epicsMutexUnlock(pVxTimePvt->lock); + } + /* Restart the timer, to call us again in SYNC_PERIOD seconds */ + epicsTimerStartDelay(pVxTimePvt->sync_timer, SYNC_PERIOD); +} + +long VxTime_Init(int priority) +{ + taskLock(); + if(pVxTimePvt) { + taskUnlock(); + return OK; + } + pVxTimePvt = callocMustSucceed(1,sizeof(VxTimePvt),"VxTime_Init"); + taskUnlock(); + + bzero((char *)pVxTimePvt, sizeof(VxTimePvt)); + pVxTimePvt->synced = FALSE; + pVxTimePvt->lock = epicsMutexCreate(); + pVxTimePvt->priority = priority; + + /* register to link list */ + generalTimeCurrentTpRegister("vxWorks Ticks", priority, VxTimeGetCurrent); + + /* Don't start the syncing until the time system is up properly. + * This cannot be done using EPICS initHooks, as they are external + * to libCom, but this code is vxWorks-specific anyway, so use the + * vxWorks primitives. wdLib (watchdogs cannot be used as the called + * routines cannot take semaphores. */ + taskSpawn("VxTime Start Sync", 150, VX_FP_TASK, + epicsThreadGetStackSize(epicsThreadStackMedium), + (FUNCPTR) VxTime_StartSync, 0,0,0,0,0,0,0,0,0,0); + + return OK; +} + +static int VxTimeGetCurrent(epicsTimeStamp *pDest) +{ + struct timespec cur_vwtime; + unsigned long diff_sec, diff_nsec; + epicsTimeStamp epicsTime; + + double diffTime; + + epicsMutexMustLock(pVxTimePvt->lock); + + clock_gettime( CLOCK_REALTIME,&cur_vwtime ); + + if(!pVxTimePvt->synced) + { + if( cur_vwtime.tv_sec < VXWORKS_TO_EPICS_EPOCH ) + { /* Earlier than 1990 is clearly wrong. Set the time to 1/1/90 + + 1 day (to avoid timezone problems) and set the VxWorks + clock to that point */ + cur_vwtime.tv_sec = VXWORKS_TO_EPICS_EPOCH + 86400; + cur_vwtime.tv_nsec = 0; + clock_settime( CLOCK_REALTIME, &cur_vwtime); + epicsInterruptContextMessage("****************************" + "***************************************************\n" + "WARNING: VxWorks time not set. Initialised to 2/1/90\n" + "*****************************************************" + "**************************\n"); + } + epicsTimeFromTimespec(&epicsTime, &cur_vwtime); + } + else/* synced, need calculation */ + {/* the vxWorks time is always monotonic */ + if( cur_vwtime.tv_nsec >= pVxTimePvt->lastSyncedVxTime.tv_nsec ) + { + diff_sec = cur_vwtime.tv_sec - pVxTimePvt->lastSyncedVxTime.tv_sec; + diff_nsec = cur_vwtime.tv_nsec - pVxTimePvt->lastSyncedVxTime.tv_nsec; + } + else + { + diff_sec = cur_vwtime.tv_sec - pVxTimePvt->lastSyncedVxTime.tv_sec - 1; + diff_nsec = BILLION - pVxTimePvt->lastSyncedVxTime.tv_nsec + cur_vwtime.tv_nsec; + } + + epicsTime.nsec = pVxTimePvt->lastSyncedTS.nsec + diff_nsec; + epicsTime.secPastEpoch = pVxTimePvt->lastSyncedTS.secPastEpoch + diff_sec; + if( epicsTime.nsec >= BILLION ) + { + epicsTime.nsec -= BILLION; + epicsTime.secPastEpoch ++; + } + } + + diffTime = epicsTimeDiffInSeconds(&epicsTime,&pVxTimePvt->lastReportedTS); + if(diffTime >= 0.0) + {/* time is monotonic */ + *pDest = epicsTime; + pVxTimePvt->lastReportedTS = epicsTime; + } + else + {/* time never goes back */ + *pDest = pVxTimePvt->lastReportedTS; + } + + epicsMutexUnlock(pVxTimePvt->lock); + return(0); +} + +long VxTime_Report(int level) +{ + printf(VXTIME_DRV_VERSION"\n"); + + if(!pVxTimePvt) + {/* drvVxTime is not used, we just report version then quit */ + printf("vxWorks ticks time driver is not initialized yet!\n\n"); + } + else + { + printf("%synced. Last time = %lds, %ldns. ", + pVxTimePvt->synced ?"S":"Not s", + pVxTimePvt->lastSyncedVxTime.tv_sec, + pVxTimePvt->lastSyncedVxTime.tv_nsec); + printf("Timer = %p\n", pVxTimePvt->sync_timer); + } + return OK; +} + +static void VxTime_StartSync(int junk) +{ + /* Wait some time before trying to start the timer up */ + taskDelay(10 * sysClkRateGet()); + pVxTimePvt->sync_timer = generalTimeCreateSyncTimer(VxTimeSyncTime, 0); + /* Sync the first time in one second, then drop back to less frequently */ + epicsTimerStartDelay(pVxTimePvt->sync_timer, 1.0); +} diff --git a/src/libCom/osi/os/vxWorks/drvVxTime.h b/src/libCom/osi/os/vxWorks/drvVxTime.h new file mode 100644 index 000000000..c2c6b7267 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/drvVxTime.h @@ -0,0 +1,14 @@ +#ifndef _INC_drvVxTime +#define _INC_drvVxTime + +#ifdef __cplusplus +extern "C" { +#endif + +long VxTime_Init(int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libCom/osi/os/vxWorks/iocClock.c b/src/libCom/osi/os/vxWorks/iocClock.c index 5617b3ed0..676590e74 100644 --- a/src/libCom/osi/os/vxWorks/iocClock.c +++ b/src/libCom/osi/os/vxWorks/iocClock.c @@ -47,8 +47,6 @@ typedef struct iocClockPvt { epicsUInt32 nanosecondsPerTick; int tickRate; int ticksToSkip; - pepicsTimeGetCurrent getCurrent; - pepicsTimeGetEvent getEvent; const char *pserverAddr; }iocClockPvt; static iocClockPvt *piocClockPvt = 0; @@ -105,8 +103,6 @@ void iocClockInit() piocClockPvt->lock = epicsMutexCreate(); piocClockPvt->nanosecondsPerTick = BILLION/sysClkRateGet(); piocClockPvt->tickRate = sysClkRateGet(); - piocClockPvt->getCurrent = iocClockGetCurrent; - piocClockPvt->getEvent = iocClockGetEvent; /* look first for environment variable or CONFIG_SITE_ENV default */ piocClockPvt->pserverAddr = envGetConfigParamPtr(&EPICS_TS_NTP_INET); if(!piocClockPvt->pserverAddr) { /* if neither, use the boot host */ @@ -141,18 +137,6 @@ void iocClockInit() return; } -void iocClockRegister(pepicsTimeGetCurrent getCurrent, - pepicsTimeGetEvent getEvent) -{ - if(piocClockPvt) { - printf("iocClockRegister: iocClock already initialized\n"); - return; - } - piocClockPvt = callocMustSucceed(1,sizeof(iocClockPvt),"iocClockRegister"); - piocClockPvt->getCurrent = getCurrent; - piocClockPvt->getEvent = getEvent; -} - int iocClockGetCurrent(epicsTimeStamp *pDest) { unsigned long currentTick,nticks,nsecs; @@ -194,26 +178,3 @@ int iocClockGetEvent(epicsTimeStamp *pDest, int eventNumber) return(epicsTimeERROR); } -int epicsTimeGetCurrent (epicsTimeStamp *pDest) -{ - if(!piocClockPvt) { - iocClockInit(); - /*wait two seconds for syncNTP to contact network time server*/ - epicsThreadSleep(2.0); - } - if(piocClockPvt->getCurrent) return((*piocClockPvt->getCurrent)(pDest)); - return(epicsTimeERROR); -} - -int epicsTimeGetEvent (epicsTimeStamp *pDest, int eventNumber) -{ - if(!piocClockPvt) { - iocClockInit(); - /*wait two seconds for syncNTP to contact network time server*/ - epicsThreadSleep(2.0); - } - if(piocClockPvt->getEvent) - return((*piocClockPvt->getEvent)(pDest,eventNumber)); - return(epicsTimeERROR); -} - diff --git a/src/libCom/osi/os/vxWorks/iocClock.h b/src/libCom/osi/os/vxWorks/iocClock.h index 91b6813dc..83f94f069 100644 --- a/src/libCom/osi/os/vxWorks/iocClock.h +++ b/src/libCom/osi/os/vxWorks/iocClock.h @@ -12,9 +12,6 @@ /* Author: Marty Kraimer Date: 16JUN2000 */ #include "epicsTime.h" - -typedef int (*pepicsTimeGetCurrent)(epicsTimeStamp *pDest); -typedef int (*pepicsTimeGetEvent)(epicsTimeStamp *pDest,int eventNumber); +#include "epicsGeneralTime.h" void iocClockInit(void); -void iocClockRegister(pepicsTimeGetCurrent getCurrent,pepicsTimeGetEvent getEvent); diff --git a/src/libCom/osi/os/vxWorks/osdTime.cpp b/src/libCom/osi/os/vxWorks/osdTime.cpp index a8e2947f3..d2bbdfcbe 100644 --- a/src/libCom/osi/os/vxWorks/osdTime.cpp +++ b/src/libCom/osi/os/vxWorks/osdTime.cpp @@ -9,9 +9,19 @@ \*************************************************************************/ #include "epicsTime.h" +#include "drvNTPTime.h" +#include "drvVxTime.h" +#include "epicsGeneralTime.h" // epicsTimeGetCurrent and epicsTimeGetEvent are implemented in iocClock.c +extern "C" epicsShareFunc int epicsShareAPI osdTimeInit(void) +{ + NTPTime_Init(100); /* init NTP first so it can be used to sync VW */ + VxTime_Init(LAST_RESORT_PRIORITY); + return epicsTimeOK; +} + // vxWorks localtime_r interface does not match POSIX standards int epicsTime_localtime ( const time_t *clock, struct tm *result ) {