Includes the changes from third party vendor Solarflare to make
ptpd use hardware assistance provided by some of their NICs.
This patch also include changes developed in-house to support
Solaris in general and Solaris on sparc platform. Solarflare
related changes will not be fed back upstream. Solaris related
changes will be contributed back upstream.
diff -r 3f1e7d35d0ab -r 821e8eadeaff COPYRIGHT
--- a/COPYRIGHT Tue May 14 17:07:59 2013 -0700
+++ b/COPYRIGHT Sun Oct 27 22:49:29 2013 -0700
@@ -1,4 +1,6 @@
/*-
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
* Copyright (c) 2009-2012 George V. Neville-Neil,
* Steven Kreuzer,
* Martin Burnicki,
diff -r 3f1e7d35d0ab -r 821e8eadeaff Makefile
--- a/Makefile Tue May 14 17:07:59 2013 -0700
+++ b/Makefile Sun Oct 27 22:49:29 2013 -0700
@@ -2,16 +2,12 @@
VERSION = ptpd-2.2.0
-release:
- (cd src; make clean)
- mkdir $(VERSION)
- (cd $(VERSION); \
- ln -s ../src .; \
- ln -s ../doc .; \
- ln -s ../tools .; \
- ln -s ../COPYRIGHT .; \
- ln -s ../ChangeLog .; \
- ln -s ../Makefile .; \
- ln -s ../README .; \
- ln -s ../RELEASE_NOTES .)
- tar cvzf $(VERSION).tar.gz -L --exclude .o --exclude Doxygen --exclude .svn --exclude .dep --exclude core $(VERSION)
+build:
+ (cd src; gmake clean; gmake)
+
+install:
+ mkdir -p $(DESTDIR)/usr/lib/inet
+ ginstall -D -p -m 755 src/ptpd $(DESTDIR)/usr/lib/inet/ptpd
+ mkdir -p $(DESTDIR)/usr/share/man/man8/
+ ginstall -D -p -m 444 src/ptpd2.8 $(DESTDIR)/usr/share/man/man8/ptpd.8
+
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/Makefile
--- a/src/Makefile Tue May 14 17:07:59 2013 -0700
+++ b/src/Makefile Sun Oct 27 22:49:29 2013 -0700
@@ -1,4 +1,4 @@
-# Makefile for ptpd2
+# Makefile for ptpd
#
# Compile time defines:
@@ -13,6 +13,7 @@
# -DPTPD_DBGV adds all debug messages
#
# -DPTPD_NO_DAEMON forces option -c
+# -DBSD_INTERFACE_FUNCTIONS: for OpenBSD
#
# -PTP_EXPERIMENTAL Allows non-standard compliant experimental options:
# -U: Hybrid mode
@@ -27,29 +28,35 @@
RM = rm -f
# start with CFLAGS += ..., so additional CFLAGs can be specified e.g. on the make command line
-CFLAGS += -Wall -g
-
+CFLAGS = -Wall -g -D_ISOC99_SOURCE -D_BSD_SOURCE -std=gnu99 -m64
+CPPFLAGS += -D_XOPEN_SOURCE=600 -D__EXTENSIONS__ -D_XPG4_2
CFLAGS += -DRUNTIME_DEBUG
#CFLAGS += -DPTPD_DBG
#CFLAGS += -DPTPD_DBG2
#CFLAGS += -DPTPD_DBGV
+#CFLAGS += -DPTPD_DUMP
#CFLAGS += -DPTPD_NO_DAEMON
+#CFLAGS += -DBSD_INTERFACE_FUNCTIONS
#CFLAGS += -DDBG_SIGUSR2_CHANGE_DOMAIN
-CFLAGS += -DDBG_SIGUSR2_CHANGE_DEBUG
+#CFLAGS += -DDBG_SIGRTMIN_LEAP_SECOND
+#CFLAGS += -DDBG_SIGUSR2_CHANGE_DEBUG
+#CFLAGS += -DPTPD_FULL_OPTIONS
-CFLAGS += -DPTP_EXPERIMENTAL
+#CFLAGS += -DPTP_EXPERIMENTAL
LDFLAGS+= -lm -lrt
+LDFLAGS += -lnsl -lsocket -m64
-PROG = ptpd2
+PROG = ptpd
OBJS = $(SRCS:.c=.o)
CSCOPE = cscope
GTAGS = gtags
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/arith.c
--- a/src/arith.c Tue May 14 17:07:59 2013 -0700
+++ b/src/arith.c Sun Oct 27 22:49:29 2013 -0700
@@ -1,5 +1,7 @@
/*-
- * Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
+ * Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
* Martin Burnicki, Gael Mace, Alexandre Van Kempen
* Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
*
@@ -36,9 +38,12 @@
*
*/
+#include <math.h>
#include "ptpd.h"
+double round (double __x);
+
void
integer64_to_internalTime(Integer64 bigint, TimeInternal * internal)
{
@@ -245,7 +250,7 @@
}
-
+#if 0
int check_timestamp_is_fresh2(TimeInternal * timeA, TimeInternal * timeB)
{
int ret;
@@ -264,7 +269,7 @@
return check_timestamp_is_fresh2(timeA, &timeB);
}
-
+#endif
int
isTimeInternalNegative(const TimeInternal * p)
{
@@ -272,4 +277,52 @@
}
+int64_t
+internalTime_to_scalar(TimeInternal *internal)
+{
+ return ((int64_t)internal->seconds * 1000000000LL)
+ + (int64_t)internal->nanoseconds;
+}
+float
+secondsToMidnight(void)
+{
+ TimeInternal now;
+
+ timerNow(&now);
+
+ // TODO should be 86400 - (now.seconds % 86400)
+ Integer32 stmI = (now.seconds - (now.seconds % 86400) + 86400) -
+ now.seconds;
+
+ return (stmI + 0.0 - now.nanoseconds / 1E9);
+}
+
+
+float
+getPauseBeforeMidnight(Integer8 announceInterval)
+{
+ return ((secondsToMidnight() <= getPauseAfterMidnight(announceInterval)) ?
+ secondsToMidnight() : getPauseAfterMidnight(announceInterval));
+}
+
+
+float
+getPauseAfterMidnight(Integer8 announceInterval)
+{
+ return((LEAP_SECOND_PAUSE_PERIOD > 2 * pow(2,announceInterval)) ?
+ LEAP_SECOND_PAUSE_PERIOD + 0.0 : 2 * pow(2,announceInterval) +
+ 0.0);
+}
+
+
+int
+log2IntegerSaturateAtZero(LongDouble number)
+{
+ if (number <= 0) return 0;
+
+ number = log(number)/log(2);
+
+ return round(number);
+}
+
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/bmc.c
--- a/src/bmc.c Tue May 14 17:07:59 2013 -0700
+++ b/src/bmc.c Sun Oct 27 22:49:29 2013 -0700
@@ -1,4 +1,5 @@
/*-
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
* Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
* Martin Burnicki, Gael Mace, Alexandre Van Kempen
* Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
@@ -77,7 +78,7 @@
ptpClock->domainNumber = rtOpts->domainNumber;
ptpClock->slaveOnly = rtOpts->slaveOnly;
- if(rtOpts->slaveOnly)
+ if(rtOpts->master_slave_mode == PTP_MODE_SLAVE)
rtOpts->clockQuality.clockClass = 255;
/* Port configuration data set */
@@ -91,16 +92,17 @@
ptpClock->portIdentity.portNumber = NUMBER_PORTS;
/* select the initial rate of delayreqs until we receive the first announce message */
- ptpClock->logMinDelayReqInterval = rtOpts->initial_delayreq;
+ ptpClock->minDelayReqInterval = rtOpts->initial_delayreq;
+ ptpClock->logMinDelayReqInterval = log2IntegerSaturateAtZero(rtOpts->initial_delayreq);
clearTime(&ptpClock->peerMeanPathDelay);
- ptpClock->logAnnounceInterval = rtOpts->announceInterval;
+ ptpClock->logAnnounceInterval = log2IntegerSaturateAtZero(rtOpts->announceInterval);
ptpClock->announceReceiptTimeout = rtOpts->announceReceiptTimeout;
- ptpClock->logSyncInterval = rtOpts->syncInterval;
+ ptpClock->logSyncInterval = log2IntegerSaturateAtZero(rtOpts->syncInterval);
ptpClock->delayMechanism = rtOpts->delayMechanism;
- ptpClock->logMinPdelayReqInterval = DEFAULT_PDELAYREQ_INTERVAL;
- ptpClock->versionNumber = VERSION_PTP;
+ ptpClock->logMinPdelayReqInterval = DEFAULT_LOG_PDELAYREQ_INTERVAL;
+ ptpClock->versionNumber = VERSION_PTP_PROTOCOL;
/*
* Initialize random number generator using same method as ptpv1:
@@ -145,9 +147,10 @@
ptpClock->timeSource = INTERNAL_OSCILLATOR;
/* UTC vs TAI timescales */
- ptpClock->currentUtcOffsetValid = DEFAULT_UTC_VALID;
+ ptpClock->currentUtcOffsetValid = rtOpts->currentUtcOffsetValid;
ptpClock->currentUtcOffset = rtOpts->currentUtcOffset;
+ /* TODO Should we clear the leap second flags? Unprogram leap second in kernel? */
}
@@ -156,8 +159,10 @@
{
/* make sure we revert to ARB timescale in Passive mode*/
if(ptpClock->portState == PTP_PASSIVE){
- ptpClock->currentUtcOffsetValid = DEFAULT_UTC_VALID;
+ ptpClock->currentUtcOffsetValid = rtOpts->currentUtcOffsetValid;
ptpClock->currentUtcOffset = rtOpts->currentUtcOffset;
+
+ /* TODO Should we clear the leap second flags? Unprogram leap second in kernel? */
}
}
@@ -166,6 +171,16 @@
/*Local clock is synchronized to Ebest Table 16 (9.3.5) of the spec*/
void s1(MsgHeader *header,MsgAnnounce *announce,PtpClock *ptpClock, RunTimeOpts *rtOpts)
{
+
+ Boolean previousLeap59 = FALSE, previousLeap61 = FALSE;
+ Integer16 previousUtcOffset = 0;
+
+ if (ptpClock->portState == PTP_SLAVE) {
+ previousLeap59 = ptpClock->leap59;
+ previousLeap61 = ptpClock->leap61;
+ previousUtcOffset = ptpClock->currentUtcOffset;
+ }
+
/* Current DS */
ptpClock->stepsRemoved = announce->stepsRemoved + 1;
@@ -193,12 +208,95 @@
/* set PTP_PASSIVE-specific state */
p1(ptpClock, rtOpts);
- ptpClock->leap59 = ((header->flagField[1] & PTP_LI_61) == PTP_LI_59);
- ptpClock->leap61 = ((header->flagField[1] & PTP_LI_61) == PTP_LI_61);
ptpClock->timeTraceable = ((header->flagField[1] & TIME_TRACEABLE) == TIME_TRACEABLE);
ptpClock->frequencyTraceable = ((header->flagField[1] & FREQUENCY_TRACEABLE) == FREQUENCY_TRACEABLE);
ptpClock->ptpTimescale = ((header->flagField[1] & PTP_TIMESCALE) == PTP_TIMESCALE);
ptpClock->timeSource = announce->timeSource;
+
+
+ /* Leap second handling */
+
+ if (ptpClock->portState == PTP_SLAVE) {
+ /* We must not take leap second updates while a leap second
+ * is in progress. This shouldn't happen but we should not
+ * rely on the master behaving. We'll take the update when
+ * the first announce arrives after the leap second has
+ * completed.
+ */
+ if (!ptpClock->leapSecondInProgress) {
+ ptpClock->leap59 = ((header->flagField[1] & PTP_LI_59) == PTP_LI_59);
+ ptpClock->leap61 = ((header->flagField[1] & PTP_LI_61) == PTP_LI_61);
+
+ if(ptpClock->leap59 && ptpClock->leap61) {
+ ERROR("Both Leap59 and Leap61 flags set!\n");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ return;
+ }
+
+ if ((previousLeap59 != ptpClock->leap59) ||
+ (previousLeap61 != ptpClock->leap61)) {
+ /* Either a leap second has been set. Start
+ * by cancelling any leap seconds already
+ * programmed. */
+ ptpClock->leapSecondPending = FALSE;
+ timerStop(LEAP_SECOND_PENDING_TIMER, ptpClock->itimer);
+ timerStop(LEAP_SECOND_NOW_TIMER, ptpClock->itimer);
+#if !defined(__APPLE__)
+ unsetTimexFlags(STA_INS | STA_DEL,TRUE);
+#endif /* apple */
+ /* If one of the leap seconds is set, program
+ * the kernel to make the change. Set a timer
+ * to expire just before the leap second will
+ * occur. */
+ if (ptpClock->leap59 || ptpClock->leap61) {
+#if !defined(__APPLE__)
+ if (rtOpts->noResetClock ||
+ rtOpts->resetClockStartupOnly) {
+ WARNING("=== Leap second pending! "
+ "One second will be %s "
+ "at midnight\n",
+ ptpClock->leap61 ?
+ "added" : "deleted");
+ } else {
+ WARNING("=== Leap second pending! "
+ "Setting kernel to %s "
+ "one second at midnight\n",
+ ptpClock->leap61 ?
+ "add" : "delete");
+
+ setTimexFlags(ptpClock->leap61 ?
+ STA_INS : STA_DEL,
+ FALSE);
+ }
+#else
+ WARNING("=== Leap second pending! "
+ "No kernel leap second "
+ "API support - expect a "
+ "clock jump at midnight!\n");
+#endif /* apple */
+ ptpClock->leapSecondPending = TRUE;
+ timerStart(LEAP_SECOND_PENDING_TIMER,
+ secondsToMidnight() -
+ getPauseBeforeMidnight(ptpClock->logAnnounceInterval),
+ ptpClock->itimer);
+ } else {
+ WARNING("=== Leap second event "
+ "aborted by GM!\n");
+ }
+ }
+ }
+
+ if((previousUtcOffset != ptpClock->currentUtcOffset) &&
+ !ptpClock->leapSecondPending &&
+ !ptpClock->leapSecondInProgress ) {
+ WARNING("=== UTC offset changed from %d to %d with "
+ "no leap second pending!\n",
+ previousUtcOffset, ptpClock->currentUtcOffset);
+ } else if( previousUtcOffset != ptpClock->currentUtcOffset) {
+ WARNING("=== UTC offset changed from %d to %d\n",
+ previousUtcOffset,ptpClock->currentUtcOffset);
+ }
+ }
}
@@ -342,9 +440,7 @@
RunTimeOpts *rtOpts, PtpClock *ptpClock)
{
Integer8 comp;
-
-
- if (rtOpts->slaveOnly) {
+ if (rtOpts->master_slave_mode == PTP_MODE_SLAVE) {
s1(header,announce,ptpClock, rtOpts);
return PTP_SLAVE;
}
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/constants.h
--- a/src/constants.h Tue May 14 17:07:59 2013 -0700
+++ b/src/constants.h Sun Oct 27 22:49:29 2013 -0700
@@ -1,3 +1,33 @@
+/*-
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
+ * Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
+ * Martin Burnicki, Gael Mace, Alexandre Van Kempen
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
#ifndef CONSTANTS_H_
#define CONSTANTS_H_
@@ -9,7 +39,8 @@
* and enumeration defined in the spec
*/
- #define MANUFACTURER_ID \
+#define PTP_VERSION_STRING "ptpdv2_2_0_0"
+#define MANUFACTURER_ID \
"MaceG VanKempen;2.0.0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
@@ -17,21 +48,26 @@
#define DEFAULT_INBOUND_LATENCY 0 /* in nsec */
#define DEFAULT_OUTBOUND_LATENCY 0 /* in nsec */
#define DEFAULT_NO_RESET_CLOCK FALSE
+#define DEFAULT_RESET_CLOCK_STARTUP_ONLY FALSE
+#define DEFAULT_NO_ADJUST_CLOCK FALSE
#define DEFAULT_DOMAIN_NUMBER 0
-#define DEFAULT_DELAY_MECHANISM E2E // TODO
+#define DEFAULT_DELAY_MECHANISM E2E
#define DEFAULT_AP 10
#define DEFAULT_AI 1000
#define DEFAULT_DELAY_S 6
-#define DEFAULT_ANNOUNCE_INTERVAL 1 /* 0 in 802.1AS */
+#define DEFAULT_ANNOUNCE_INTERVAL 2.0 /* 0 in 802.1AS */
+#define LEAP_SECOND_PAUSE_PERIOD 2 /* how long before/after leap */
+ /* second event we pause offset */
+ /* calculation */
/* Master mode operates in ARB (UTC) timescale, without TAI+leap seconds */
#define DEFAULT_UTC_OFFSET 0
#define DEFAULT_UTC_VALID FALSE
-#define DEFAULT_PDELAYREQ_INTERVAL 1 /* -4 in 802.1AS */
+#define DEFAULT_LOG_PDELAYREQ_INTERVAL 1 /* -4 in 802.1AS */
-#define DEFAULT_DELAYREQ_INTERVAL 0 /* new value from page 237 of the standard */
+#define DEFAULT_DELAYREQ_INTERVAL 1.0 /* new value from page 237 of the standard */
-#define DEFAULT_SYNC_INTERVAL 0 /* -7 in 802.1AS */ /* from page 237 of the standard */
+#define DEFAULT_SYNC_INTERVAL 1.0 /* -7 in 802.1AS */ /* from page 237 of the standard */
/* number of announces we need to lose until a time out occurs. Thus it is 12 seconds */
#define DEFAULT_ANNOUNCE_RECEIPT_TIMEOUT 6 /* 3 by default */
@@ -49,7 +85,7 @@
section 7.6.2.4, page 55:
248 Default. This clockClass shall be used if none of the other clockClass definitions apply.
13 Shall designate a clock that is synchronized to an application-specific source of time. The timescale distributed
- shall be ARB. A clockClass 13 clock shall not be a slave to another clock in the domain.
+ shall be ARB. A clockClass 13 clock shall not be a slave to another clock in the domain.
*/
#define DEFAULT_CLOCK_CLASS 248
#define DEFAULT_CLOCK_CLASS__APPLICATION_SPECIFIC_TIME_SOURCE 13
@@ -63,22 +99,34 @@
*/
#define DEFAULT_CLOCK_ACCURACY 0xFE
-#define DEFAULT_PRIORITY1 128
-#define DEFAULT_PRIORITY2 128 /* page 238, default priority is the midpoint, to allow easy control of the BMC algorithm */
+#define DEFAULT_PRIORITY1 248
+#define DEFAULT_PRIORITY2 248
-/* page 238: τ, see 7.6.3.2: The default initialization value shall be 1.0 s. */
-#define DEFAULT_CLOCK_VARIANCE 28768 /* To be determined in 802.1AS. */
-
+#define DEFAULT_CLOCK_VARIANCE -4000 /* To be determined in 802.1AS. */
+ /* We use the same value as in */
+ /* ptpdv1. */
+
#define DEFAULT_MAX_FOREIGN_RECORDS 5
#define DEFAULT_PARENTS_STATS FALSE
+/* Default time mode for master and slave modes */
+#define DEFAULT_MASTER_TIME_MODE TIME_SYSTEM
+#define DEFAULT_SLAVE_TIME_MODE TIME_SYSTEM
+
+/* For time both, default system time update interval */
+#define DEFAULT_SYSTEM_TIME_UPDATE_INTERVAL 1.0
+
+/* We don't allow the time to be set to before 1/1/1971 because this almost */
+/* certainly means something has gone very wrong */
+#define UTC_TIME_VALID_MINIMUM (31536000)
+
/* features, only change to refelect changes in implementation */
#define NUMBER_PORTS 1
-#define VERSION_PTP 2
-#define TWO_STEP_FLAG TRUE
+#define VERSION_PTP_PROTOCOL 2
+#define TWO_STEP_FLAG 0x02
#define BOUNDARY_CLOCK FALSE
#define SLAVE_ONLY FALSE
#define NO_ADJUST FALSE
@@ -107,7 +155,7 @@
* \brief Domain Number (Table 2 in the spec)*/
enum {
- DFLT_DOMAIN_NUMBER = 0, ALT1_DOMAIN_NUMBER, ALT2_DOMAIN_NUMBER, ALT3_DOMAIN_NUMBER
+ DFLT_DOMAIN_NUMBER = 0, ALT1_DOMAIN_NUMBER, ALT2_DOMAIN_NUMBER, ALT3_DOMAIN_NUMBER
};
/**
@@ -148,9 +196,13 @@
/* non-spec timers */
OPERATOR_MESSAGES_TIMER, /* used to limit the operator messages */
- TIMER_ARRAY_SIZE
+ LEAP_SECOND_PENDING_TIMER, /**<\brief timer used for handling leap second operations */
+ LEAP_SECOND_NOW_TIMER, /**<\brief Timer used to time moment of leap second */
+ TIMER_ARRAY_SIZE /* this one is non-spec */
};
+extern char *PTP_timer_dbg_string[];
+
/**
* \brief PTP states
*/
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/datatypes.h
--- a/src/datatypes.h Tue May 14 17:07:59 2013 -0700
+++ b/src/datatypes.h Sun Oct 27 22:49:29 2013 -0700
@@ -1,7 +1,37 @@
+/*-
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
+ * Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
+ * Martin Burnicki, Gael Mace, Alexandre Van Kempen
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
#ifndef DATATYPES_H_
#define DATATYPES_H_
-#include <stdio.h>
+#include <stdio.h>
/*Struct defined in spec*/
@@ -12,233 +42,241 @@
*
* This header file defines structures defined by the spec,
* main program data structure, and all messages structures
- */
+*/
/**
* \brief The TimeInterval type represents time intervals
- */
+*/
typedef struct {
- Integer64 scaledNanoseconds;
+ Integer64 scaledNanoseconds;
} TimeInterval;
/**
* \brief The Timestamp type represents a positive time with respect to the epoch
- */
+*/
typedef struct {
- UInteger48 secondsField;
- UInteger32 nanosecondsField;
+ UInteger48 secondsField;
+ UInteger32 nanosecondsField;
} Timestamp;
/**
* \brief The ClockIdentity type identifies a clock
- */
+*/
typedef Octet ClockIdentity[CLOCK_IDENTITY_LENGTH];
/**
* \brief The PortIdentity identifies a PTP port.
- */
+*/
typedef struct {
- ClockIdentity clockIdentity;
- UInteger16 portNumber;
+ ClockIdentity clockIdentity;
+ UInteger16 portNumber;
} PortIdentity;
/**
* \brief The PortAdress type represents the protocol address of a PTP port
- */
+*/
typedef struct {
- Enumeration16 networkProtocol;
- UInteger16 adressLength;
- Octet* adressField;
+ Enumeration16 networkProtocol;
+ UInteger16 adressLength;
+ Octet* adressField;
} PortAdress;
/**
* \brief The ClockQuality represents the quality of a clock
- */
+*/
typedef struct {
- UInteger8 clockClass;
- Enumeration8 clockAccuracy;
- UInteger16 offsetScaledLogVariance;
+ UInteger8 clockClass;
+ Enumeration8 clockAccuracy;
+ UInteger16 offsetScaledLogVariance;
} ClockQuality;
/**
* \brief The TLV type represents TLV extension fields
- */
+*/
typedef struct {
- Enumeration16 tlvType;
- UInteger16 lengthField;
- Octet* valueField;
+ Enumeration16 tlvType;
+ UInteger16 lengthField;
+ Octet* valueField;
} TLV;
/**
* \brief The PTPText data type is used to represent textual material in PTP messages
- */
+*/
typedef struct {
- UInteger8 lengthField;
- Octet* textField;
+ UInteger8 lengthField;
+ Octet* textField;
} PTPText;
/**
* \brief The FaultRecord type is used to construct fault logs
- */
+*/
typedef struct {
- UInteger16 faultRecordLength;
- Timestamp faultTime;
- Enumeration8 severityCode;
- PTPText faultName;
- PTPText faultValue;
- PTPText faultDescription;
+ UInteger16 faultRecordLength;
+ Timestamp faultTime;
+ Enumeration8 severityCode;
+ PTPText faultName;
+ PTPText faultValue;
+ PTPText faultDescription;
} FaultRecord;
/**
* \brief The common header for all PTP messages (Table 18 of the spec)
- */
+*/
/* Message header */
typedef struct {
- Nibble transportSpecific;
- Enumeration4 messageType;
- UInteger4 versionPTP;
- UInteger16 messageLength;
- UInteger8 domainNumber;
- Octet flagField[2];
- Integer64 correctionfield;
- PortIdentity sourcePortIdentity;
- UInteger16 sequenceId;
- UInteger8 controlField;
- Integer8 logMessageInterval;
+ Nibble transportSpecific;
+ Enumeration4 messageType;
+ UInteger4 versionPTP;
+ UInteger16 messageLength;
+ UInteger8 domainNumber;
+ Octet flagField[2];
+ Integer64 correctionfield;
+ PortIdentity sourcePortIdentity;
+ UInteger16 sequenceId;
+ UInteger8 controlField;
+ Integer8 logMessageInterval;
} MsgHeader;
/**
* \brief Announce message fields (Table 25 of the spec)
- */
+*/
/*Announce Message */
typedef struct {
- Timestamp originTimestamp;
- Integer16 currentUtcOffset;
- UInteger8 grandmasterPriority1;
- ClockQuality grandmasterClockQuality;
- UInteger8 grandmasterPriority2;
- ClockIdentity grandmasterIdentity;
- UInteger16 stepsRemoved;
- Enumeration8 timeSource;
+ Timestamp originTimestamp;
+ Integer16 currentUtcOffset;
+ UInteger8 grandmasterPriority1;
+ ClockQuality grandmasterClockQuality;
+ UInteger8 grandmasterPriority2;
+ ClockIdentity grandmasterIdentity;
+ UInteger16 stepsRemoved;
+ Enumeration8 timeSource;
}MsgAnnounce;
/**
* \brief Sync message fields (Table 26 of the spec)
- */
+*/
/*Sync Message */
typedef struct {
- Timestamp originTimestamp;
+ Timestamp originTimestamp;
}MsgSync;
/**
* \brief DelayReq message fields (Table 26 of the spec)
- */
+*/
/*DelayReq Message */
typedef struct {
- Timestamp originTimestamp;
+ Timestamp originTimestamp;
}MsgDelayReq;
/**
* \brief DelayResp message fields (Table 30 of the spec)
- */
+*/
/*delayResp Message*/
typedef struct {
- Timestamp receiveTimestamp;
- PortIdentity requestingPortIdentity;
+ Timestamp receiveTimestamp;
+ PortIdentity requestingPortIdentity;
}MsgDelayResp;
/**
* \brief FollowUp message fields (Table 27 of the spec)
- */
+*/
/*Follow-up Message*/
typedef struct {
- Timestamp preciseOriginTimestamp;
+ Timestamp preciseOriginTimestamp;
}MsgFollowUp;
/**
* \brief PDelayReq message fields (Table 29 of the spec)
- */
+*/
/*PdelayReq Message*/
typedef struct {
- Timestamp originTimestamp;
+ Timestamp originTimestamp;
}MsgPDelayReq;
/**
* \brief PDelayResp message fields (Table 30 of the spec)
- */
+*/
/*PdelayResp Message*/
typedef struct {
- Timestamp requestReceiptTimestamp;
- PortIdentity requestingPortIdentity;
+ Timestamp requestReceiptTimestamp;
+ PortIdentity requestingPortIdentity;
}MsgPDelayResp;
/**
* \brief PDelayRespFollowUp message fields (Table 31 of the spec)
- */
+*/
/*PdelayRespFollowUp Message*/
typedef struct {
- Timestamp responseOriginTimestamp;
- PortIdentity requestingPortIdentity;
+ Timestamp responseOriginTimestamp;
+ PortIdentity requestingPortIdentity;
}MsgPDelayRespFollowUp;
/**
* \brief Signaling message fields (Table 33 of the spec)
- */
+*/
/*Signaling Message*/
typedef struct {
- PortIdentity targetPortIdentity;
- char* tlv;
+ PortIdentity targetPortIdentity;
+ char* tlv;
}MsgSignaling;
/**
* \brief Management message fields (Table 37 of the spec)
- */
+*/
/*management Message*/
typedef struct {
- PortIdentity targetPortIdentity;
- UInteger8 startingBoundaryHops;
- UInteger8 boundaryHops;
- Enumeration4 actionField;
- char* tlv;
+ PortIdentity targetPortIdentity;
+ UInteger8 startingBoundaryHops;
+ UInteger8 boundaryHops;
+ Enumeration4 actionField;
+ char* tlv;
}MsgManagement;
/**
-* \brief Time structure to handle Linux time information
- */
-typedef struct {
- Integer32 seconds;
- Integer32 nanoseconds;
-} TimeInternal;
-
-/**
* \brief Structure used as a timer
- */
+*/
typedef struct {
- Integer32 interval;
- Integer32 left;
- Boolean expire;
+ Integer32 interval;
+ Integer32 left;
+ Boolean expire;
} IntervalTimer;
/**
* \brief ForeignMasterRecord is used to manage foreign masters
+*/
+typedef struct
+{
+ PortIdentity foreignMasterPortIdentity;
+ UInteger16 foreignMasterAnnounceMessages;
+
+ //This one is not in the spec
+ MsgAnnounce announce;
+ MsgHeader header;
+} ForeignMasterRecord;
+
+/**
+ * \brief Structure used to collect statistics
*/
typedef struct
{
- PortIdentity foreignMasterPortIdentity;
- UInteger16 foreignMasterAnnounceMessages;
+ UInteger32 ts_sync_failures;
+} Statistics;
- //This one is not in the spec
- MsgAnnounce announce;
- MsgHeader header;
-} ForeignMasterRecord;
-
+/**
+* \brief Timestamp method in use
+*/
+typedef enum {
+ TS_METHOD_SYSTEM,
+ TS_METHOD_SO_TIMESTAMPING,
+ TS_METHOD_DRIVER_IOCTL
+} TsMethod;
/**
* \struct PtpClock
@@ -246,241 +284,329 @@
*/
/* main program data structure */
typedef struct {
- /* Default data set */
+ /* Default data set */
- /*Static members*/
- Boolean twoStepFlag;
- ClockIdentity clockIdentity;
- UInteger16 numberPorts;
+ /*Static members*/
+ Boolean twoStepFlag;
+ ClockIdentity clockIdentity;
+ UInteger16 numberPorts;
- /*Dynamic members*/
- ClockQuality clockQuality;
+ /*Dynamic members*/
+ ClockQuality clockQuality;
- /*Configurable members*/
- UInteger8 priority1;
- UInteger8 priority2;
- UInteger8 domainNumber;
- Boolean slaveOnly;
+ /*Configurable members*/
+ UInteger8 priority1;
+ UInteger8 priority2;
+ UInteger8 domainNumber;
+ Boolean slaveOnly;
- /* Current data set */
+ /* Current data set */
- /*Dynamic members*/
- UInteger16 stepsRemoved;
- TimeInternal offsetFromMaster;
- TimeInternal meanPathDelay;
+ /*Dynamic members*/
+ UInteger16 stepsRemoved;
+ TimeInternal offsetFromMaster;
+ TimeInternal meanPathDelay;
- /* Parent data set */
+ /* Parent data set */
- /*Dynamic members*/
- PortIdentity parentPortIdentity;
- Boolean parentStats;
- UInteger16 observedParentOffsetScaledLogVariance;
- Integer32 observedParentClockPhaseChangeRate;
- ClockIdentity grandmasterIdentity;
- ClockQuality grandmasterClockQuality;
- UInteger8 grandmasterPriority1;
- UInteger8 grandmasterPriority2;
+ /*Dynamic members*/
+ PortIdentity parentPortIdentity;
+ Boolean parentStats;
+ UInteger16 observedParentOffsetScaledLogVariance;
+ Integer32 observedParentClockPhaseChangeRate;
+ ClockIdentity grandmasterIdentity;
+ ClockQuality grandmasterClockQuality;
+ UInteger8 grandmasterPriority1;
+ UInteger8 grandmasterPriority2;
- /* Global time properties data set */
+ /* Global time properties data set */
- /*Dynamic members*/
- Integer16 currentUtcOffset;
- Boolean currentUtcOffsetValid;
- Boolean leap59;
- Boolean leap61;
- Boolean timeTraceable;
- Boolean frequencyTraceable;
- Boolean ptpTimescale;
- Enumeration8 timeSource;
+ /*Dynamic members*/
+ Integer16 currentUtcOffset;
+ Boolean currentUtcOffsetValid;
+ Boolean leap59;
+ Boolean leap61;
+ Boolean timeTraceable;
+ Boolean frequencyTraceable;
+ Boolean ptpTimescale;
+ Boolean leapSecondInProgress;
+ Boolean leapSecondPending;
+ Enumeration8 timeSource;
- /* Port configuration data set */
+ /* Port configuration data set */
- /*Static members*/
- PortIdentity portIdentity;
+ /*Static members*/
+ PortIdentity portIdentity;
- /*Dynamic members*/
- Enumeration8 portState;
- Integer8 logMinDelayReqInterval;
- TimeInternal peerMeanPathDelay;
-
- /*Configurable members*/
- Integer8 logAnnounceInterval;
- UInteger8 announceReceiptTimeout;
- Integer8 logSyncInterval;
- Enumeration8 delayMechanism;
- Integer8 logMinPdelayReqInterval;
- UInteger4 versionNumber;
+ /*Dynamic members*/
+ Enumeration8 portState;
+ LongDouble minDelayReqInterval;
+ Integer8 logMinDelayReqInterval;
+ TimeInternal peerMeanPathDelay;
+ /*Configurable members*/
+ Integer8 logAnnounceInterval;
+ UInteger8 announceReceiptTimeout;
+ Integer8 logSyncInterval;
+ Enumeration8 delayMechanism;
+ Integer8 logMinPdelayReqInterval;
+ UInteger4 versionNumber;
- /* Foreign master data set */
- ForeignMasterRecord *foreign;
- /* Other things we need for the protocol */
- UInteger16 number_foreign_records;
- Integer16 max_foreign_records;
- Integer16 foreign_record_i;
- Integer16 foreign_record_best;
- UInteger32 random_seed;
- Boolean record_update; /* should we run bmc() after receiving an announce message? */
+ /* Foreign master data set */
+ ForeignMasterRecord *foreign;
+ /* Other things we need for the protocol */
+ UInteger16 number_foreign_records;
+ Integer16 max_foreign_records;
+ Integer16 foreign_record_i;
+ Integer16 foreign_record_best;
+ UInteger32 random_seed;
+ Boolean record_update; /* should we run bmc() after receiving an announce message? */
- MsgHeader msgTmpHeader;
- union {
- MsgSync sync;
- MsgFollowUp follow;
- MsgDelayReq req;
- MsgDelayResp resp;
- MsgPDelayReq preq;
- MsgPDelayResp presp;
- MsgPDelayRespFollowUp prespfollow;
- MsgManagement manage;
- MsgAnnounce announce;
- MsgSignaling signaling;
- } msgTmp;
+ MsgHeader msgTmpHeader;
+ union {
+ MsgSync sync;
+ MsgFollowUp follow;
+ MsgDelayReq req;
+ MsgDelayResp resp;
+ MsgPDelayReq preq;
+ MsgPDelayResp presp;
+ MsgPDelayRespFollowUp prespfollow;
+ MsgManagement manage;
+ MsgAnnounce announce;
+ MsgSignaling signaling;
+ } msgTmp;
- Octet msgObuf[PACKET_SIZE];
- Octet msgIbuf[PACKET_SIZE];
-/*
- 20110630: These variables were deprecated in favor of the ones that appear in the stats log (delayMS and delaySM)
-
- TimeInternal master_to_slave_delay;
- TimeInternal slave_to_master_delay;
+ Octet msgObuf[PACKET_SIZE];
+ Octet msgIbuf[PACKET_SIZE];
- */
- Integer32 observed_drift;
+ TimeInternal master_to_slave_delay;
+ TimeInternal slave_to_master_delay;
+ LongDouble observed_drift;
+ LongDouble frequency_adjustment;
- TimeInternal pdelay_req_receive_time;
- TimeInternal pdelay_req_send_time;
- TimeInternal pdelay_resp_receive_time;
- TimeInternal pdelay_resp_send_time;
- TimeInternal sync_receive_time;
- TimeInternal delay_req_send_time;
- TimeInternal delay_req_receive_time;
- MsgHeader PdelayReqHeader;
- MsgHeader delayReqHeader;
- TimeInternal pdelayMS;
- TimeInternal pdelaySM;
- TimeInternal delayMS;
- TimeInternal delaySM;
- TimeInternal lastSyncCorrectionField;
- TimeInternal lastPdelayRespCorrectionField;
+ TimeInternal pdelay_req_receive_time;
+ TimeInternal pdelay_req_send_time;
+ TimeInternal pdelay_resp_receive_time;
+ TimeInternal pdelay_resp_send_time;
+ TimeInternal sync_receive_time;
+ TimeInternal delay_req_send_time;
+ TimeInternal delay_req_receive_time;
+ MsgHeader PdelayReqHeader;
+ MsgHeader delayReqHeader;
+ TimeInternal pdelayMS;
+ TimeInternal pdelaySM;
+ TimeInternal delayMS;
+ TimeInternal delaySM;
+ TimeInternal lastSyncCorrectionField;
+ TimeInternal lastPdelayRespCorrectionField;
+ /**
+ * TRUE when the clock is used to synchronize NIC and system time and
+ * the process is the PTP_MASTER. The offset and adjustment calculation
+ * is always "master (= NIC) to slave (= system time)" and PTP_SLAVEs
+ * update their system time, but the master needs to invert the
+ * clock adjustment and control NIC time instead. This way
+ * the master's system time is propagated to slaves.
+ */
+ Boolean nic_instead_of_system;
+ /**
+ * Specifies the method for retrieving timestamps. Depending on the kernel
+ * version and operating mode, timestamping is done by either system
+ * timestamps made by the kernel,
+ * queue, the socket error queue or via proprietary ioctl operations.
+ */
+ TsMethod tsMethod;
- Boolean sentPDelayReq;
- UInteger16 sentPDelayReqSequenceId;
- UInteger16 sentDelayReqSequenceId;
- UInteger16 sentSyncSequenceId;
- UInteger16 sentAnnounceSequenceId;
- UInteger16 recvPDelayReqSequenceId;
- UInteger16 recvSyncSequenceId;
- UInteger16 recvPDelayRespSequenceId;
- Boolean waitingForFollow;
- Boolean waitingForDelayResp;
-
+ /**
+ * a prefix to be inserted before messages about the clock:
+ * may be empty, but not NULL
+ *
+ * used to distinguish multiple active clock servos per process
+ */
+ const char *name;
- offset_from_master_filter ofm_filt;
- one_way_delay_filter owd_filt;
+ Boolean sentPDelayReq;
+ UInteger16 sentPDelayReqSequenceId;
+ UInteger16 sentDelayReqSequenceId;
+ UInteger16 sentSyncSequenceId;
+ UInteger16 sentAnnounceSequenceId;
+ UInteger16 recvPDelayReqSequenceId;
+ UInteger16 recvSyncSequenceId;
+ UInteger16 recvPDelayRespSequenceId;
+ Boolean waitingForFollow;
+ Boolean waitingForDelayResp;
- Boolean message_activity;
- IntervalTimer itimer[TIMER_ARRAY_SIZE];
+ offset_from_master_filter ofm_filt;
+ one_way_delay_filter owd_filt;
- NetPath netPath;
+ Boolean message_activity;
- /*Usefull to init network stuff*/
- UInteger8 port_communication_technology;
- Octet port_uuid_field[PTP_UUID_LENGTH];
+ IntervalTimer itimer[TIMER_ARRAY_SIZE];
- int reset_count;
- int current_init_clock;
- int can_step_clock;
- int warned_operator_slow_slewing;
- int warned_operator_fast_slewing;
+ NetPath netPath;
- char char_last_msg; /* representation of last message processed by servo */
- Boolean last_packet_was_sync; /* used to limit logging of Sync messages */
-
- int waiting_for_first_sync; /* we'll only start the delayReq timer after the first sync */
- int waiting_for_first_delayresp; /* Just for information purposes */
- Boolean startup_in_progress;
-
-#ifdef PTP_EXPERIMENTAL
- Integer32 MasterAddr; // used for hybrid mode, when receiving announces
- Integer32 LastSlaveAddr; // used for hybrid mode, when receiving delayreqs
-#endif
+ /*Usefull to init network stuff*/
+ UInteger8 port_communication_technology;
+ Octet port_uuid_field[PTP_UUID_LENGTH];
+ int reset_count;
+ int warned_operator_slow_slewing;
+ int warned_operator_fast_slewing;
+
+ char char_last_msg; /* representation of last message processed by servo */
+ Boolean last_packet_was_sync; /* used to limit logging of Sync messages */
+
+ int waiting_for_first_sync; /* we'll only start the delayReq timer after the first sync */
+ int waiting_for_first_delayresp; /* Just for information purposes */
+
+ Boolean offset_first_updated;
+ Boolean clock_first_updated;
+
+ Statistics statistics;
+
} PtpClock;
/**
- * \struct RunTimeOpts
- * \brief Program options set at run-time
- */
+* \enum Time mode enumeration
+* \brief Time mode for PTP clock
+*/
+typedef enum {
+ /**
+ * Use and control system time
+ */
+ TIME_SYSTEM,
+ /**
+ * Use and control network interface time using Solarflare IOCTLs and
+ * net_tstamp.h API.
+ */
+ TIME_NIC,
+ /**
+ * A combination of PTP between NICs plus a local synchronization
+ * between NIC and system time:
+ *
+ * - NIC time is controlled via PTP packets, main time seen
+ * by PTPd is the NIC time
+ * - system_to_nic and nic_to_system delays are provided by
+ * device driver on request
+ * - time.c adapts NIC time to system time on master and system time
+ * to NIC time on slaves by feeding these offsets into another
+ * instance of the clock servo (as in TIME_SYSTEM)
+ *
+ * The command line options only apply to one clock sync and
+ * the defaults are used for the other:
+ * - NIC time: default values for clock control (adjust and reset)
+ * and servo (coefficients), configurable PTP
+ * - system time: configurable clock control and servo, PTP options do
+ * not apply
+ */
+ TIME_BOTH,
+ /**
+ * Time used and controlled by PTP is the system time with hardware
+ * packet time stamping via Linux net_tstamp.h API or using driver
+ * proprietary IOCTLs.
+ */
+ TIME_SYSTEM_LINUX_HW,
+ /**
+ * Time used and controlled by PTP is the system time with software
+ * packet time stamping via standard Linux net_tstamp.h API.
+ */
+ TIME_SYSTEM_LINUX_SW,
+
+ TIME_MAX
+} TimeMode;
+
+/**
+* \enum Master slave mode enumeration
+* \brief Master/slave mode for PTP clock
+*/
+typedef enum {
+ PTP_MODE_NULL,
+ PTP_MODE_MASTER_NO_NTP,
+ PTP_MODE_MASTER_WITH_NTP,
+ PTP_MODE_SLAVE
+} MasterSlaveMode;
+
+/**
+* \struct RunTimeOpts
+* \brief Program options set at run-time
+*/
/* program options set at run-time */
typedef struct {
- Integer8 announceInterval;
- Integer8 announceReceiptTimeout;
-
- Integer8 syncInterval;
- ClockQuality clockQuality;
- UInteger8 priority1;
- UInteger8 priority2;
- UInteger8 domainNumber;
+ LongDouble announceInterval;
+ Integer8 announceReceiptTimeout;
+
+ LongDouble syncInterval;
+ ClockQuality clockQuality;
+ UInteger8 priority1;
+ UInteger8 priority2;
+ UInteger8 domainNumber;
#ifdef PTP_EXPERIMENTAL
UInteger8 mcast_group_Number;
#endif
Boolean slaveOnly;
- Integer16 currentUtcOffset;
- Octet ifaceName[IFACE_NAME_LENGTH];
- Boolean noResetClock;
- Integer32 maxReset; /* Maximum number of nanoseconds to reset */
- Integer32 maxDelay; /* Maximum number of nanoseconds of delay */
- Boolean noAdjust;
- Boolean displayStats;
- Boolean csvStats;
- Boolean displayPackets;
- Octet unicastAddress[MAXHOSTNAMELEN];
- Integer32 ap, ai;
- Integer16 s;
- TimeInternal inboundLatency, outboundLatency;
- Integer16 max_foreign_records;
- Boolean ethernet_mode;
- Enumeration8 delayMechanism;
- Boolean offset_first_updated;
- char file[PATH_MAX];
- int logFd;
- Boolean useSysLog;
- int ttl;
- char recordFile[PATH_MAX];
- FILE *recordFP;
+ Integer16 currentUtcOffset;
+ Boolean currentUtcOffsetValid;
+ Octet ifaceName[IFACE_NAME_LENGTH];
+ Boolean noResetClock;
+ Boolean resetClockStartupOnly;
+ Integer32 maxReset; /* Maximum number of nanoseconds to reset */
+ TimeInternal maxDelay; /* Maximum number of nanoseconds of delay */
+ Boolean noAdjust;
+ Boolean displayStats;
+ Boolean verboseStats;
+ Boolean csvStats;
+ Boolean displayPackets;
+ Octet unicastAddress[MAXHOSTNAMELEN];
+ Integer32 ap, ai;
+ Integer16 s;
+ TimeInternal inboundLatency, outboundLatency;
+ Integer16 max_foreign_records;
+ Boolean ethernet_mode;
+ Enumeration8 delayMechanism;
+// Boolean offset_first_updated;
+ char file[PATH_MAX];
+ int logFd;
+ Boolean useSysLog;
+#ifndef __sun
+ int ttl;
+#else
+ uchar_t ttl;
+#endif
+ char recordFile[PATH_MAX];
+ FILE *recordFP;
- int log_seconds_between_message;
+ int log_seconds_between_message;
- Boolean ignore_daemon_lock;
- Boolean do_IGMP_refresh;
- int nonDaemon;
- Boolean do_log_to_file;
- Boolean do_record_quality_file;
-
- int initial_delayreq;
- int subsequent_delayreq;
+ Boolean ignore_daemon_lock;
+ Boolean do_IGMP_refresh;
+ int nonDaemon;
+ Boolean do_log_to_file;
+ Boolean do_record_quality_file;
+
+ LongDouble initial_delayreq;
+ int subsequent_delayreq;
Boolean ignore_delayreq_interval_master;
- Boolean syslog_startup_messages_also_to_stdout;
-
+ Boolean syslog_startup_messages_also_to_stdout;
+
#ifdef PTP_EXPERIMENTAL
int do_hybrid_mode;
#endif
- int do_unicast_mode;
-
-#ifdef RUNTIME_DEBUG
- int debug_level;
-#endif
+ TimeMode time_mode;
+ MasterSlaveMode master_slave_mode;
+ LongDouble system_time_update_interval;
+ int do_unicast_mode;
+
+ int debug_level;
} RunTimeOpts;
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/dep/constants_dep.h
--- a/src/dep/constants_dep.h Tue May 14 17:07:59 2013 -0700
+++ b/src/dep/constants_dep.h Sun Oct 27 22:49:29 2013 -0700
@@ -1,3 +1,33 @@
+/*-
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
+ * Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
+ * Martin Burnicki, Gael Mace, Alexandre Van Kempen
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
/* constants_dep.h */
@@ -16,7 +46,7 @@
/* platform dependent */
#if !defined(linux) && !defined(__NetBSD__) && !defined(__FreeBSD__) && \
- !defined(__APPLE__)
+ !defined(__APPLE__) && !defined(__sun)
#error Not ported to this architecture, please update.
#endif
@@ -38,7 +68,7 @@
#endif /* linux */
-#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__)
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(__sun)
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
@@ -48,6 +78,9 @@
# if defined(__FreeBSD__) || defined(__APPLE__)
# include <net/ethernet.h>
# include <sys/uio.h>
+# elif defined(__sun)
+# include <sys/ethernet.h>
+# include <sys/uio.h>
# else
# include <net/if_ether.h>
# endif
@@ -55,25 +88,35 @@
# define IFACE_NAME_LENGTH IF_NAMESIZE
# define NET_ADDRESS_LENGTH INET_ADDRSTRLEN
-# define IFCONF_LENGTH 10
+# define IFCONF_LENGTH 20
# define adjtimex ntp_adjtime
+#if !defined(__sun)
# include <machine/endian.h>
# if BYTE_ORDER == LITTLE_ENDIAN
# define PTPD_LSBF
# elif BYTE_ORDER == BIG_ENDIAN
# define PTPD_MSBF
# endif
+#else
+# if defined(__sparc__)
+# define PTPD_MSBF
+# else
+# define PTPD_LSBF
+# endif
+#endif
#endif
+#define SFC_FIRMWARE_VERSION_NUMBER_MIN 6039
+
#define CLOCK_IDENTITY_LENGTH 8
-#define ADJ_FREQ_MAX 512000
+/* The system clock can handle frequency adjustments up to 100,000,000ppb */
+#define ADJ_FREQ_MAX_SYSTEM 100000000.0
+/* Our NIC can only handle frequency adjustments up to 1,000,000ppb */
+#define ADJ_FREQ_MAX_NIC 1000000.0
/* UDP/IPv4 dependent */
-#ifndef INADDR_LOOPBACK
-#define INADDR_LOOPBACK 0x7f000001UL
-#endif
#define SUBDOMAIN_ADDRESS_LENGTH 4
#define PORT_ADDRESS_LENGTH 2
@@ -110,6 +153,8 @@
#define NANOSECONDS_MAX 999999999
+/* our own errno for failure to retrieve timestamp */
+#define ENOTIMESTAMP 1000
// limit operator messages to once every X seconds
#define OPERATOR_MESSAGES_INTERVAL 300.0
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/dep/datatypes_dep.h
--- a/src/dep/datatypes_dep.h Tue May 14 17:07:59 2013 -0700
+++ b/src/dep/datatypes_dep.h Sun Oct 27 22:49:29 2013 -0700
@@ -1,12 +1,44 @@
+/*-
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
+ * Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
+ * Martin Burnicki, Gael Mace, Alexandre Van Kempen
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
#ifndef DATATYPES_DEP_H_
#define DATATYPES_DEP_H_
+#include <sys/ioctl.h>
+#include <net/if.h>
+
/**
*\file
* \brief Implementation specific datatype
*/
-/* FIXME: shouldn't uint32_t and friends be used here? */
typedef enum {FALSE=0, TRUE} Boolean;
typedef char Octet;
typedef signed char Integer8;
@@ -20,12 +52,13 @@
typedef unsigned char Enumeration4;
typedef unsigned char UInteger4;
typedef unsigned char Nibble;
+typedef long double LongDouble;
/**
* \brief Implementation specific of UInteger48 type
*/
typedef struct {
- unsigned int lsb; /* FIXME: shouldn't uint32_t and uint16_t be used here? */
+ unsigned int lsb;
unsigned short msb;
} UInteger48;
@@ -33,7 +66,7 @@
* \brief Implementation specific of Integer64 type
*/
typedef struct {
- unsigned int lsb; /* FIXME: shouldn't uint32_t and int32_t be used here? */
+ unsigned int lsb;
int msb;
} Integer64;
@@ -58,20 +91,48 @@
Integer32 s_exp;
} one_way_delay_filter;
+
+#define TX_STACK_SIZE 1
+
+/**
+* \brief Time structure to handle Linux time information
+ */
+typedef struct TimeInternal {
+ Integer32 seconds;
+ Integer32 nanoseconds;
+} TimeInternal;
+
+
+struct tx_item {
+ int len;
+ TimeInternal ts;
+ unsigned char buf[PACKET_SIZE];
+};
+
/**
* \brief Struct used to store network datas
*/
typedef struct {
Integer32 eventSock, generalSock, multicastAddr, peerMulticastAddr,unicastAddr;
+#if defined(linux)
+ /** for further ioctl() calls on eventSock */
+ struct ifreq eventSockIFR;
+#elif defined(__sun)
+ Integer32 devFd;
+ struct lifreq eventSockIFR;
+ void *ioctl_req;
+#endif
+
+
+ /* @ioctl_timestamping Added support for hw based timestamping */
+ struct {
+ struct tx_item data[TX_STACK_SIZE];
+ int count;
+ } tx_stack;
+
/* used by IGMP refresh */
struct in_addr interfaceAddr;
-
-#ifdef PTP_EXPERIMENTAL
- /* used for Hybrid mode */
- Integer32 lastRecvAddr;
-#endif
-
} NetPath;
#endif /*DATATYPES_DEP_H_*/
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/dep/msg.c
--- a/src/dep/msg.c Tue May 14 17:07:59 2013 -0700
+++ b/src/dep/msg.c Sun Oct 27 22:49:29 2013 -0700
@@ -1,4 +1,5 @@
/*-
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
* Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
* Martin Burnicki, Gael Mace, Alexandre Van Kempen
* Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
@@ -39,10 +40,6 @@
#include "../ptpd.h"
-#ifdef PTP_EXPERIMENTAL
-extern RunTimeOpts rtOpts;
-#endif
-
/*Unpack Header from IN buffer to msgTmpHeader field */
void
msgUnpackHeader(Octet * buf, MsgHeader * header)
@@ -73,23 +70,34 @@
/*Pack header message into OUT buffer of ptpClock*/
void
-msgPackHeader(Octet * buf, PtpClock * ptpClock)
+msgPackHeader(Octet * buf, PtpClock * ptpClock, unsigned int messageType)
{
- Nibble transport = 0x80;
+ const UInteger8 transportSpecific = 0x00;
+ UInteger8 octet0 = transportSpecific | (UInteger8)messageType;
+
+ /* (spec annex D) */
+ *(UInteger8 *) (buf + 0) = octet0;
+ *(UInteger4 *) (buf + 1) = ptpClock->versionNumber;
+ *(UInteger8 *) (buf + 2) = 0; /* messageLength */
+ *(UInteger8 *) (buf + 3) = 0; /* messageLength */
+ *(UInteger8 *) (buf + 4) = ptpClock->domainNumber;
+ *(UInteger8 *) (buf + 5) = 0;
- /* (spec annex D) */
- *(UInteger8 *) (buf + 0) = transport;
- *(UInteger4 *) (buf + 1) = ptpClock->versionNumber;
- *(UInteger8 *) (buf + 4) = ptpClock->domainNumber;
+ if (((messageType == SYNC) || (messageType == PDELAY_RESP)) &&
+ (ptpClock->twoStepFlag)) {
+ *(UInteger8 *) (buf + 6) = PTP_TWO_STEP;
+ } else {
+ *(UInteger8 *) (buf + 6) = 0;
+ }
+ *(UInteger8 *) (buf + 7) = 0;
- /* TODO: this bit should have been active only for sync and PdelayResp */
- if (ptpClock->twoStepFlag)
- *(UInteger8 *) (buf + 6) = PTP_TWO_STEP;
-
- memset((buf + 8), 0, 8);
+ memset((buf + 8), 0, 12);
memcpy((buf + 20), ptpClock->portIdentity.clockIdentity,
CLOCK_IDENTITY_LENGTH);
*(UInteger16 *) (buf + 28) = flip16(ptpClock->portIdentity.portNumber);
+ *(UInteger8 *) (buf + 30) = 0; /* sequenceId */
+ *(UInteger8 *) (buf + 31) = 0; /* sequenceId */
+ *(UInteger8 *) (buf + 32) = 0; /* controlField */
*(UInteger8 *) (buf + 33) = 0x7F;
/* Default value(spec Table 24) */
}
@@ -100,13 +108,9 @@
void
msgPackSync(Octet * buf, Timestamp * originTimestamp, PtpClock * ptpClock)
{
- msgPackHeader(buf, ptpClock);
+ msgPackHeader(buf, ptpClock, SYNC);
/* changes in header */
- *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
- /* RAZ messageType */
- *(char *)(buf + 0) = *(char *)(buf + 0) | 0x00;
- /* Table 19 */
*(UInteger16 *) (buf + 2) = flip16(SYNC_LENGTH);
*(UInteger16 *) (buf + 30) = flip16(ptpClock->sentSyncSequenceId);
*(UInteger8 *) (buf + 32) = 0x00;
@@ -142,15 +146,26 @@
void
msgPackAnnounce(Octet * buf, PtpClock * ptpClock)
{
- msgPackHeader(buf, ptpClock);
+ UInteger16 tmp;
+ msgPackHeader(buf, ptpClock, ANNOUNCE);
+
+ if (ptpClock->leap59) {
+ *(UInteger8 *) (buf + 7) |= 0x02;
+ }
+ if (ptpClock->leap61) {
+ *(UInteger8 *) (buf + 7) |= 0x01;
+ }
+
+ if (ptpClock->currentUtcOffsetValid)
+ *(UInteger8 *) (buf + 7) |= 0x04;
/* changes in header */
- *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
- /* RAZ messageType */
- *(char *)(buf + 0) = *(char *)(buf + 0) | 0x0B;
/* Table 19 */
- *(UInteger16 *) (buf + 2) = flip16(ANNOUNCE_LENGTH);
+ /* *(UInteger16 *) (buf + 2) = flip16(ANNOUNCE_LENGTH);
+ */
+ tmp = flip16(ANNOUNCE_LENGTH);
+ memcpy((buf + 2), &tmp, sizeof(UInteger16));
*(UInteger16 *) (buf + 30) = flip16(ptpClock->sentAnnounceSequenceId);
*(UInteger8 *) (buf + 32) = 0x05;
/* Table 23 */
@@ -166,7 +181,10 @@
flip16(ptpClock->clockQuality.offsetScaledLogVariance);
*(UInteger8 *) (buf + 52) = ptpClock->grandmasterPriority2;
memcpy((buf + 53), ptpClock->grandmasterIdentity, CLOCK_IDENTITY_LENGTH);
- *(UInteger16 *) (buf + 61) = flip16(ptpClock->stepsRemoved);
+/* *(UInteger16 *) (buf + 61) = flip16(ptpClock->stepsRemoved);
+ */
+ tmp = flip16(ptpClock->stepsRemoved);
+ memcpy((buf+61), &tmp, sizeof(UInteger16));
*(Enumeration8 *) (buf + 63) = ptpClock->timeSource;
}
@@ -174,6 +192,8 @@
void
msgUnpackAnnounce(Octet * buf, MsgAnnounce * announce)
{
+ UInteger16 tmp;
+
announce->originTimestamp.secondsField.msb =
flip16(*(UInteger16 *) (buf + 34));
announce->originTimestamp.secondsField.lsb =
@@ -191,7 +211,10 @@
announce->grandmasterPriority2 = *(UInteger8 *) (buf + 52);
memcpy(announce->grandmasterIdentity, (buf + 53),
CLOCK_IDENTITY_LENGTH);
- announce->stepsRemoved = flip16(*(UInteger16 *) (buf + 61));
+ memcpy(&tmp, (buf+61), sizeof(UInteger16));
+ announce->stepsRemoved = flip16(tmp);
+ /*announce->stepsRemoved = flip16(*(UInteger16 *) (buf + 61));
+ */
announce->timeSource = *(Enumeration8 *) (buf + 63);
#ifdef PTPD_DBG
@@ -203,12 +226,9 @@
void
msgPackFollowUp(Octet * buf, Timestamp * preciseOriginTimestamp, PtpClock * ptpClock)
{
- msgPackHeader(buf, ptpClock);
+ msgPackHeader(buf, ptpClock, FOLLOW_UP);
/* changes in header */
- *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
- /* RAZ messageType */
- *(char *)(buf + 0) = *(char *)(buf + 0) | 0x08;
/* Table 19 */
*(UInteger16 *) (buf + 2) = flip16(FOLLOW_UP_LENGTH);
*(UInteger16 *) (buf + 30) = flip16(ptpClock->sentSyncSequenceId - 1);
@@ -248,13 +268,10 @@
void
msgPackPDelayReq(Octet * buf, Timestamp * originTimestamp, PtpClock * ptpClock)
{
- msgPackHeader(buf, ptpClock);
+ msgPackHeader(buf, ptpClock, PDELAY_REQ);
/* changes in header */
- *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
- /* RAZ messageType */
- *(char *)(buf + 0) = *(char *)(buf + 0) | 0x02;
/* Table 19 */
*(UInteger16 *) (buf + 2) = flip16(PDELAY_REQ_LENGTH);
*(UInteger16 *) (buf + 30) = flip16(ptpClock->sentPDelayReqSequenceId);
@@ -277,12 +294,9 @@
void
msgPackDelayReq(Octet * buf, Timestamp * originTimestamp, PtpClock * ptpClock)
{
- msgPackHeader(buf, ptpClock);
+ msgPackHeader(buf, ptpClock, DELAY_REQ);
/* changes in header */
- *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
- /* RAZ messageType */
- *(char *)(buf + 0) = *(char *)(buf + 0) | 0x01;
/* Table 19 */
*(UInteger16 *) (buf + 2) = flip16(DELAY_REQ_LENGTH);
@@ -308,21 +322,12 @@
void
msgPackDelayResp(Octet * buf, MsgHeader * header, Timestamp * receiveTimestamp, PtpClock * ptpClock)
{
- msgPackHeader(buf, ptpClock);
+ msgPackHeader(buf, ptpClock, DELAY_RESP);
/* changes in header */
- *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
- /* RAZ messageType */
- *(char *)(buf + 0) = *(char *)(buf + 0) | 0x09;
/* Table 19 */
*(UInteger16 *) (buf + 2) = flip16(DELAY_RESP_LENGTH);
*(UInteger8 *) (buf + 4) = header->domainNumber;
-
-#ifdef PTP_EXPERIMENTAL
- if(rtOpts.do_hybrid_mode)
- *(char *)(buf + 6) |= PTP_UNICAST;
-#endif
-
memset((buf + 8), 0, 8);
/* Copy correctionField of PdelayReqMessage */
@@ -355,12 +360,9 @@
void
msgPackPDelayResp(Octet * buf, MsgHeader * header, Timestamp * requestReceiptTimestamp, PtpClock * ptpClock)
{
- msgPackHeader(buf, ptpClock);
+ msgPackHeader(buf, ptpClock, PDELAY_RESP);
/* changes in header */
- *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
- /* RAZ messageType */
- *(char *)(buf + 0) = *(char *)(buf + 0) | 0x03;
/* Table 19 */
*(UInteger16 *) (buf + 2) = flip16(PDELAY_RESP_LENGTH);
*(UInteger8 *) (buf + 4) = header->domainNumber;
@@ -465,12 +467,9 @@
void
msgPackPDelayRespFollowUp(Octet * buf, MsgHeader * header, Timestamp * responseOriginTimestamp, PtpClock * ptpClock)
{
- msgPackHeader(buf, ptpClock);
+ msgPackHeader(buf, ptpClock, PDELAY_RESP_FOLLOW_UP);
/* changes in header */
- *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
- /* RAZ messageType */
- *(char *)(buf + 0) = *(char *)(buf + 0) | 0x0A;
/* Table 19 */
*(UInteger16 *) (buf + 2) = flip16(PDELAY_RESP_FOLLOW_UP_LENGTH);
*(UInteger16 *) (buf + 30) = flip16(ptpClock->PdelayReqHeader.sequenceId);
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/dep/net.c
--- a/src/dep/net.c Tue May 14 17:07:59 2013 -0700
+++ b/src/dep/net.c Sun Oct 27 22:49:29 2013 -0700
@@ -1,4 +1,6 @@
/*-
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
* Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
* Martin Burnicki, Gael Mace, Alexandre Van Kempen,
* Jan Breuer
@@ -37,6 +39,14 @@
*
*/
+#ifdef __sun
+#include <net/if_arp.h>
+#include <signal.h>
+#include "sfxge_ioctl.h"
+#include <string.h>
+extern void catch_alarm(int sig);
+#endif /* __sun */
+
#include "../ptpd.h"
/* choose kernel-level nanoseconds or microseconds resolution on the client-side */
@@ -44,6 +54,27 @@
#error kernel-level timestamps not detected
#endif
+/* Function that wraps up call to send message */
+static int sendMessage(int sockfd, const void *buf, size_t length,
+ const struct sockaddr *addr, socklen_t addrLen,
+ const char *messageType)
+{
+ int rc = sendto(sockfd, buf, length, 0, addr, addrLen);
+
+ if (rc < 0) {
+ DBG("error sending %s message, errno %d\n", messageType, errno);
+ return errno;
+ }
+
+ if (rc != length) {
+ DBG("error sending %s message, sent %d bytes, expected %d\n",
+ messageType, rc, length);
+ return EIO;
+ }
+
+ return 0;
+}
+
/**
* shutdown the IPv4 multicast for specific address
*
@@ -149,12 +180,11 @@
return TRUE;
}
-
/*Test if network layer is OK for PTP*/
UInteger8
lookupCommunicationTechnology(UInteger8 communicationTechnology)
{
-#if defined(linux)
+#if defined(linux) || defined(__sun)
switch (communicationTechnology) {
case ARPHRD_ETHER:
case ARPHRD_EETHER:
@@ -164,170 +194,456 @@
default:
break;
}
-#endif /* defined(linux) */
+#endif /* defined(linux) || defined (__sun)*/
return PTP_DEFAULT;
}
+#if defined(__sun)
+/* Find the local network interfacei; For solaris use lifreq */
+UInteger32
+findIface(Octet * ifaceName, UInteger8 * communicationTechnology,
+ Octet * uuid, NetPath * netPath)
+{
+
+ /* depends on linux specific ioctls (see 'netdevice' man page) */
+ int i, flags;
+ struct lifconf data;
+ struct lifreq device[IFCONF_LENGTH];
+ struct sockaddr_dl *sdl;
+
+
+ /* an interface specified */
+ if (ifaceName[0] != '\0') {
+ i = 0;
+ memcpy(device[i].lifr_name, ifaceName, IFACE_NAME_LENGTH);
+ sdl = (struct sockaddr_dl *)&device[i].lifr_addr;
+
+ if (ioctl(netPath->eventSock, SIOCGLIFHWADDR, &device[i]) < 0)
+ DBGV("failed to get hardware address\n");
+ else if ((*communicationTechnology = sdl->sdl_type) != 0x4)
+ DBGV("unsupported communication technology (%d)\n", *communicationTechnology);
+ else {
+ memcpy(uuid, sdl->sdl_data, PTP_UUID_LENGTH);
+ clockIdentity_display(uuid);
+ }
+
+ } else {
+ /* no iface specified */
+ /* get list of network interfaces */
+
+ data.lifc_family = AF_INET;
+ data.lifc_len = sizeof(device);
+ data.lifc_req = device;
+
+ memset(data.lifc_buf, 0, data.lifc_len);
+
+ flags = IFF_UP | IFF_RUNNING | IFF_MULTICAST;
+
+ if (ioctl(netPath->eventSock, SIOCGLIFCONF, &data) < 0) {
+ PERROR("failed query network interfaces");
+ return 0;
+ }
+ if (data.lifc_len >= sizeof(device))
+ DBG("device list may exceed allocated space\n");
+
+ /* search through interfaces */
+ for (i = 0; i < data.lifc_len / sizeof(device[0]); ++i) {
+
+ sdl = (struct sockaddr_dl *)&device[i].lifr_addr;
+
+ DBGV("%d %s %s\n", i, device[i].lifr_name,
+ inet_ntoa(((struct sockaddr_in *)
+ &device[i].lifr_addr)->sin_addr));
+
+ if (ioctl(netPath->eventSock, SIOCGLIFFLAGS,
+ &device[i]) < 0)
+ DBGV("failed to get device flags\n");
+ else if ((device[i].lifr_flags & flags) != flags)
+ DBGV("does not meet requirements"
+ "(%08x, %08x)\n", device[i].lifr_flags,
+ flags);
+ else if (ioctl(netPath->eventSock, SIOCGLIFHWADDR, &device[i]) < 0)
+ DBGV("failed to get hardware address\n");
+ else if ((*communicationTechnology = sdl->sdl_type) != 0x4)
+ DBGV("unsupported communication technology (%d)\n", *communicationTechnology);
+ else {
+ DBGV("found interface (%s)\n", device[i].lifr_name);
+ memcpy(uuid, sdl->sdl_data, PTP_UUID_LENGTH);
+ clockIdentity_display(uuid);
+ memcpy(ifaceName, device[i].lifr_name, IFACE_NAME_LENGTH);
+ break;
+ }
+ }
+ }
+
+ if (ifaceName[0] == '\0') {
+ ERROR("failed to find a usable interface\n");
+ return 0;
+ }
+ if (ioctl(netPath->eventSock, SIOCGLIFADDR, &device[i]) < 0) {
+ PERROR("failed to get ip address");
+ return 0;
+ }
+
+ /* @ioctl_timestamping Added support for IOCTL based timestamping for older kernels */
+ netPath->eventSockIFR = device[i];
+ return ((struct sockaddr_in *)&device[i].lifr_addr)->sin_addr.s_addr;
+}
+
+#else /* __sun */
/* Find the local network interface */
UInteger32
findIface(Octet * ifaceName, UInteger8 * communicationTechnology,
Octet * uuid, NetPath * netPath)
{
-#if defined(linux)
+#if defined(linux) || defined(__sun)
- /* depends on linux specific ioctls (see 'netdevice' man page) */
- int i, flags;
- struct ifconf data;
- struct ifreq device[IFCONF_LENGTH];
+ /* depends on linux specific ioctls (see 'netdevice' man page) */
+ int i, flags;
+ struct ifconf data;
+ struct ifreq device[IFCONF_LENGTH];
- data.ifc_len = sizeof(device);
- data.ifc_req = device;
+ data.ifc_len = sizeof(device);
+ data.ifc_req = device;
- memset(data.ifc_buf, 0, data.ifc_len);
+ memset(data.ifc_buf, 0, data.ifc_len);
- flags = IFF_UP | IFF_RUNNING | IFF_MULTICAST;
+ flags = IFF_UP | IFF_RUNNING | IFF_MULTICAST;
- /* look for an interface if none specified */
- if (ifaceName[0] != '\0') {
- i = 0;
- memcpy(device[i].ifr_name, ifaceName, IFACE_NAME_LENGTH);
+ /* an interface specified */
+ if (ifaceName[0] != '\0') {
+ i = 0;
+ memcpy(device[i].ifr_name, ifaceName, IFACE_NAME_LENGTH);
- if (ioctl(netPath->eventSock, SIOCGIFHWADDR, &device[i]) < 0)
- DBGV("failed to get hardware address\n");
- else if ((*communicationTechnology =
- lookupCommunicationTechnology(
- device[i].ifr_hwaddr.sa_family))
- == PTP_DEFAULT)
- DBGV("unsupported communication technology (%d)\n",
- *communicationTechnology);
- else
- memcpy(uuid, device[i].ifr_hwaddr.sa_data,
- PTP_UUID_LENGTH);
- } else {
- /* no iface specified */
- /* get list of network interfaces */
- if (ioctl(netPath->eventSock, SIOCGIFCONF, &data) < 0) {
- PERROR("failed query network interfaces");
- return 0;
- }
- if (data.ifc_len >= sizeof(device))
- DBG("device list may exceed allocated space\n");
+ if (ioctl(netPath->eventSock, SIOCGIFHWADDR, &device[i]) < 0)
+ DBGV("failed to get hardware address\n");
+ else if ((*communicationTechnology =
+ lookupCommunicationTechnology(
+ device[i].ifr_hwaddr.sa_family)) == PTP_DEFAULT)
+ DBGV("unsupported communication technology1 (%d)\n",
+ *communicationTechnology);
+ else
+ memcpy(uuid, device[i].ifr_hwaddr.sa_data, PTP_UUID_LENGTH);
- /* search through interfaces */
- for (i = 0; i < data.ifc_len / sizeof(device[0]); ++i) {
- DBGV("%d %s %s\n", i, device[i].ifr_name,
- inet_ntoa(((struct sockaddr_in *)
- &device[i].ifr_addr)->sin_addr));
+ } else {
+ /* no iface specified */
+ /* get list of network interfaces */
+ if (ioctl(netPath->eventSock, SIOCGIFCONF, &data) < 0) {
+ PERROR("failed query network interfaces");
+ return 0;
+ }
+ if (data.ifc_len >= sizeof(device))
+ DBG("device list may exceed allocated space\n");
- if (ioctl(netPath->eventSock, SIOCGIFFLAGS,
- &device[i]) < 0)
- DBGV("failed to get device flags\n");
- else if ((device[i].ifr_flags & flags) != flags)
- DBGV("does not meet requirements"
- "(%08x, %08x)\n", device[i].ifr_flags,
- flags);
- else if (ioctl(netPath->eventSock, SIOCGIFHWADDR,
- &device[i]) < 0)
- DBGV("failed to get hardware address\n");
- else if ((*communicationTechnology =
- lookupCommunicationTechnology(
- device[i].ifr_hwaddr.sa_family))
- == PTP_DEFAULT)
- DBGV("unsupported communication technology"
- "(%d)\n", *communicationTechnology);
- else {
- DBGV("found interface (%s)\n",
- device[i].ifr_name);
- memcpy(uuid, device[i].ifr_hwaddr.sa_data,
- PTP_UUID_LENGTH);
- memcpy(ifaceName, device[i].ifr_name,
- IFACE_NAME_LENGTH);
- break;
- }
- }
- }
+ /* search through interfaces */
+ for (i = 0; i < data.ifc_len / sizeof(device[0]); ++i) {
+ DBGV("%d %s %s\n", i, device[i].ifr_name,
+ inet_ntoa(((struct sockaddr_in *)
+ &device[i].ifr_addr)->sin_addr));
- if (ifaceName[0] == '\0') {
- ERROR("failed to find a usable interface\n");
- return 0;
- }
- if (ioctl(netPath->eventSock, SIOCGIFADDR, &device[i]) < 0) {
- PERROR("failed to get ip address");
- return 0;
- }
- return ((struct sockaddr_in *)&device[i].ifr_addr)->sin_addr.s_addr;
+ if (ioctl(netPath->eventSock, SIOCGIFFLAGS,
+ &device[i]) < 0)
+ DBGV("failed to get device flags\n");
+ else if ((device[i].ifr_flags & flags) != flags)
+ DBGV("does not meet requirements"
+ "(%08x, %08x)\n", device[i].ifr_flags,
+ flags);
+ else if (ioctl(netPath->eventSock, SIOCGIFHWADDR,
+ &device[i]) < 0)
+ DBGV("failed to get hardware address\n");
+ else if ((*communicationTechnology =
+ lookupCommunicationTechnology(
+ device[i].ifr_hwaddr.sa_family)) == PTP_DEFAULT)
+ DBGV("unsupported communication technology2"
+ "(%d)\n", *communicationTechnology);
+ else {
+ DBGV("found interface (%s)\n",
+ device[i].ifr_name);
+ memcpy(uuid, device[i].ifr_hwaddr.sa_data, PTP_UUID_LENGTH);
+ memcpy(ifaceName, device[i].ifr_name, IFACE_NAME_LENGTH);
+ break;
+ }
+ }
+ }
+
+ if (ifaceName[0] == '\0') {
+ ERROR("failed to find a usable interface\n");
+ return 0;
+ }
+ if (ioctl(netPath->eventSock, SIOCGIFADDR, &device[i]) < 0) {
+ PERROR("failed to get ip address");
+ return 0;
+ }
+
+ /* @ioctl_timestamping Added support for IOCTL based timestamping for older kernels */
+ netPath->eventSockIFR = device[i];
+ return ((struct sockaddr_in *)&device[i].ifr_addr)->sin_addr.s_addr;
#else /* usually *BSD */
- struct ifaddrs *if_list, *ifv4, *ifh;
+ struct ifaddrs *if_list, *ifv4, *ifh;
- if (getifaddrs(&if_list) < 0) {
- PERROR("getifaddrs() failed");
- return FALSE;
- }
- /* find an IPv4, multicast, UP interface, right name(if supplied) */
- for (ifv4 = if_list; ifv4 != NULL; ifv4 = ifv4->ifa_next) {
- if ((ifv4->ifa_flags & IFF_UP) == 0)
- continue;
- if ((ifv4->ifa_flags & IFF_RUNNING) == 0)
- continue;
- if ((ifv4->ifa_flags & IFF_LOOPBACK))
- continue;
- if ((ifv4->ifa_flags & IFF_MULTICAST) == 0)
- continue;
- /* must have IPv4 address */
- if (ifv4->ifa_addr->sa_family != AF_INET)
- continue;
- if (ifaceName[0] && strncmp(ifv4->ifa_name, ifaceName,
- IF_NAMESIZE) != 0)
- continue;
- break;
- }
+ if (getifaddrs(&if_list) < 0) {
+ PERROR("getifaddrs() failed");
+ return FALSE;
+ }
+ /* find an IPv4, multicast, UP interface, right name(if supplied) */
+ for (ifv4 = if_list; ifv4 != NULL; ifv4 = ifv4->ifa_next) {
+ if ((ifv4->ifa_flags & IFF_UP) == 0)
+ continue;
+ if ((ifv4->ifa_flags & IFF_RUNNING) == 0)
+ continue;
+ if ((ifv4->ifa_flags & IFF_LOOPBACK))
+ continue;
+ if ((ifv4->ifa_flags & IFF_MULTICAST) == 0)
+ continue;
+ /* must have IPv4 address */
+ if (ifv4->ifa_addr->sa_family != AF_INET)
+ continue;
+ if (ifaceName[0] && strncmp(ifv4->ifa_name, ifaceName,
+ IF_NAMESIZE) != 0)
+ continue;
+ break;
+ }
- if (ifv4 == NULL) {
- if (ifaceName[0]) {
- ERROR("interface \"%s\" does not exist,"
- "or is not appropriate\n", ifaceName);
- return FALSE;
- }
- ERROR("no suitable interfaces found!");
- return FALSE;
- }
- /* find the AF_LINK info associated with the chosen interface */
- for (ifh = if_list; ifh != NULL; ifh = ifh->ifa_next) {
- if (ifh->ifa_addr->sa_family != AF_LINK)
- continue;
- if (strncmp(ifv4->ifa_name, ifh->ifa_name, IF_NAMESIZE) == 0)
- break;
- }
+ if (ifv4 == NULL) {
+ if (ifaceName[0]) {
+ ERROR("interface \"%s\" does not exist,"
+ "or is not appropriate\n", ifaceName);
+ return FALSE;
+ }
+ ERROR("no suitable interfaces found!");
+ return FALSE;
+ }
+ /* find the AF_LINK info associated with the chosen interface */
+ for (ifh = if_list; ifh != NULL; ifh = ifh->ifa_next) {
+#ifndef __sun
+ if (ifh->ifa_addr->sa_family != AF_LINK)
+ continue;
+#endif
+ if (strncmp(ifv4->ifa_name, ifh->ifa_name, IF_NAMESIZE) == 0)
+ break;
+ }
- if (ifh == NULL) {
- ERROR("could not get hardware address for interface \"%s\"\n",
- ifv4->ifa_name);
- return FALSE;
- }
- /* check that the interface TYPE is OK */
- if (((struct sockaddr_dl *)ifh->ifa_addr)->sdl_type != IFT_ETHER) {
- ERROR("\"%s\" is not an ethernet interface!\n", ifh->ifa_name);
- return FALSE;
- }
- DBG("==> %s %s %s\n", ifv4->ifa_name,
- inet_ntoa(((struct sockaddr_in *)ifv4->ifa_addr)->sin_addr),
- ether_ntoa((struct ether_addr *)
- LLADDR((struct sockaddr_dl *)ifh->ifa_addr))
- );
+ if (ifh == NULL) {
+ ERROR("could not get hardware address for interface \"%s\"\n",
+ ifv4->ifa_name);
+ return FALSE;
+ }
+ /* check that the interface TYPE is OK */
+ if (((struct sockaddr_dl *)ifh->ifa_addr)->sdl_type != IFT_ETHER) {
+ ERROR("\"%s\" is not an ethernet interface!\n", ifh->ifa_name);
+ return FALSE;
+ }
+ DBG("==> %s %s %s\n", ifv4->ifa_name,
+ inet_ntoa(((struct sockaddr_in *)ifv4->ifa_addr)->sin_addr),
+ ether_ntoa((struct ether_addr *)
+ LLADDR((struct sockaddr_dl *)ifh->ifa_addr))
+ );
- *communicationTechnology = PTP_ETHER;
- memcpy(ifaceName, ifh->ifa_name, IFACE_NAME_LENGTH);
- memcpy(uuid, LLADDR((struct sockaddr_dl *)ifh->ifa_addr),
- PTP_UUID_LENGTH);
+ *communicationTechnology = PTP_ETHER;
+ memcpy(ifaceName, ifh->ifa_name, IFACE_NAME_LENGTH);
+ memcpy(uuid, LLADDR((struct sockaddr_dl *)ifh->ifa_addr),
+ PTP_UUID_LENGTH);
- return ((struct sockaddr_in *)ifv4->ifa_addr)->sin_addr.s_addr;
+ return ((struct sockaddr_in *)ifv4->ifa_addr)->sin_addr.s_addr;
#endif
}
+#endif /* __sun */
+
+/* @pdelay_req TODO timestamp is not being collected when PDELAY_REQ messages are */
+/* sent. Collect the timestamp and throw it away for now */
+static int txTimestamp(PtpClock *ptpClock, char *pdu, int pdulen, TimeMode timeMode, Boolean keep)
+{
+ TimeInternal ts;
+ int haveTs = 0;
+ int matchedPkt = 0;
+ NetPath *netPath = &ptpClock->netPath;
+
+ DUMP("PDU", pdu, pdulen);
+
+ switch (ptpClock->tsMethod) {
+ case TS_METHOD_SYSTEM:
+ /* Time stamp will appear on the multicast loopback. */
+ return 0;
+ break;
+
+ case TS_METHOD_DRIVER_IOCTL:
+ /* fast path: get send time stamp directly */
+ haveTs = getSendTime(&ts, timeMode, netPath);
+ if(haveTs) {
+ DBGV("got send time stamp in first attempt\n");
+ } else {
+ /*
+ * need to wait for it: need to check system time, counting
+ * the number of nanoSleep()s is too inaccurate because it
+ * each call sleeps much longer than requested
+ */
+ TimeInternal start, now;
+ timerNow(&start);
+ while(TRUE) {
+ TimeInternal delayAfterPacketSend;
+ delayAfterPacketSend.seconds = 0;
+ delayAfterPacketSend.nanoseconds = 1000;
+ nanoSleep(&delayAfterPacketSend);
+ haveTs = getSendTime(&ts, timeMode, netPath);
+ timerNow(&now);
+ subTime(&now, &now, &start);
+ /* 0.1 seconds is the maximum we wait... */
+ if(haveTs || now.seconds >= 1 || now.nanoseconds >= 100000000) {
+ DBGV("%s send time stamp after %d.%09ds\n",
+ haveTs ? "got" : "failed to get",
+ break;
+ }
+ }
+ }
+
+ if(haveTs) {
+ /* TODO can we match the packet in this case? */
+ matchedPkt = 1;
+ }
+ break;
+
+#ifndef __sun /* Do not compile if __sun as we don't support non-ioctl method yet */
+ case TS_METHOD_SO_TIMESTAMPING:
+ {
+ struct cmsghdr *cmsg;
+ struct iovec vec[1];
+ struct msghdr msg;
+ struct sock_extended_err *err;
+ struct timespec *tmp;
+
+ int cnt, level, type;
+ char control[512];
+ unsigned char buf[PACKET_SIZE];
+ const char *ts_text;
+
+ vec[0].iov_base = buf;
+ vec[0].iov_len = sizeof(buf);
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = vec;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ /*
+ * need to wait for it: need to check system time, counting
+ * the number of nanoSleep()s is too inaccurate because it
+ * each call sleeps much longer than requested
+ */
+ TimeInternal start, now;
+ timerNow(&start);
+ while((cnt = recvmsg(netPath->eventSock, &msg, MSG_ERRQUEUE)) < 0) {
+ if ((errno == EAGAIN) || (errno == EINTR)) {
+ TimeInternal delay;
+ delay.seconds = 0;
+ delay.nanoseconds = 1000;
+ nanoSleep(&delay);
+ timerNow(&now);
+ subTime(&now, &now, &start);
+ /* 0.1 second is the maximum we wait... */
+ if(now.seconds >= 1 || now.nanoseconds >= 100000000) {
+ DBGV("Failed to get send time stamp after %d.%09ds\n",
+ return -1;
+ }
+ } else {
+ ERROR("recvmsg failed: %s\n", strerror(errno));
+ return -1;
+ }
+ }
+
+ if (cnt < pdulen) {
+ ERROR("recvmsg returned only %d of %d bytes\n", cnt, pdulen);
+ return -1;
+ }
+
+ DUMP("cmsg all", buf, cnt);
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+
+ level = cmsg->cmsg_level;
+ type = cmsg->cmsg_type;
+
+ DUMP("cmsg", cmsg, cmsg->cmsg_len);
+
+ if (SOL_SOCKET == level && SO_TIMESTAMPING == type) {
+ if (cmsg->cmsg_len < sizeof(*tmp)*3) {
+ ERROR("received short so_timestamping\n");
+ return -1;
+ }
+ /* array of three time stamps: software, HW, raw HW */
+ tmp = (struct timespec*)CMSG_DATA(cmsg);
+
+ switch (timeMode) {
+ case TIME_SYSTEM_LINUX_SW:
+ /* Desired timestamp is first in array */
+ ts_text = "SW SYS";
+ break;
+ case TIME_SYSTEM_LINUX_HW:
+ /* Desired timestamp is second in array */
+ ts_text = "HW SYS";
+ tmp++;
+ break;
+ case TIME_NIC:
+ case TIME_BOTH:
+ /* Desired timestamp is third in array */
+ ts_text = "HW RAW";
+ tmp += 2;
+ break;
+ default:
+ ERROR("Invalid case in switch %d\n", timeMode);
+ break;
+ }
+
+ if (tmp->tv_sec && tmp->tv_nsec) {
+ DBG("%s Tx TIMESTAMP: %d.%09d\n", ts_text, tmp->tv_sec, tmp->tv_nsec);
+ ts.seconds = tmp->tv_sec;
+ ts.nanoseconds = tmp->tv_nsec;
+ haveTs = 1;
+ }
+ } else if (IPPROTO_IP == level && IP_RECVERR == type) {
+ err = (struct sock_extended_err*)CMSG_DATA(cmsg);
+ if (err->ee_errno == ENOMSG &&
+ err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING &&
+ !memcmp(pdu, buf + cnt - pdulen, pdulen)) {
+ matchedPkt = 1;
+ }
+ }
+ }
+ }
+ break;
+#endif /* ifndef __sun */
+
+ default:
+ ERROR("!!! txTimestamp() Unexpected time mode %d\n", timeMode);
+ return 0;
+ break;
+ }
+
+ if (!haveTs || !matchedPkt) {
+ return -1;
+ }
+
+ if (keep) {
+ /* Push packet onto stack. */
+ int index = netPath->tx_stack.count;
+ if (TX_STACK_SIZE == index) {
+ ERROR("out of stack space\n");
+ return -1;
+ }
+ memcpy(netPath->tx_stack.data[index].buf, pdu, pdulen);
+ netPath->tx_stack.data[index].len = pdulen;
+ netPath->tx_stack.data[index].ts = ts;
+ netPath->tx_stack.count++;
+ }
+
+ return 0;
+}
/**
* Init the multcast for specific IPv4 address
@@ -390,7 +706,7 @@
/* Init Peer multicast IP address */
- memcpy(addrStr, PEER_PTP_DOMAIN_ADDRESS, NET_ADDRESS_LENGTH);
+ strncpy(addrStr, PEER_PTP_DOMAIN_ADDRESS, NET_ADDRESS_LENGTH);
if (!inet_aton(addrStr, &netAddr)) {
ERROR("failed to encode multi-cast address: %s\n", addrStr);
@@ -413,9 +729,9 @@
* @return TRUE if successful
*/
Boolean
-netInitTimestamping(NetPath * netPath)
+netInitTimestamping(NetPath * netPath, Boolean useSystemTimeStamps)
{
- int val = 1;
+ int val = useSystemTimeStamps;
Boolean result = TRUE;
#if defined(SO_TIMESTAMPNS) /* Linux, Apple */
@@ -457,70 +773,76 @@
/**
* start all of the UDP stuff
- * must specify 'subdomainName', and optionally 'ifaceName',
+ * must specify 'subdomainName', and optionally 'ifaceName',
* if not then pass ifaceName == ""
- * on socket options, see the 'socket(7)' and 'ip' man pages
+ * on socket options, see the 'socket(7)' and 'ip' man pages
*
* @param netPath
- * @param rtOpts
+ * @param rtOpts
* @param ptpClock
- *
+ *
* @return TRUE if successful
*/
-Boolean
+Boolean
netInit(NetPath * netPath, RunTimeOpts * rtOpts, PtpClock * ptpClock)
{
- int temp;
- struct in_addr interfaceAddr, netAddr;
- struct sockaddr_in addr;
+ int temp;
+#ifdef __sun
+ uchar_t temp_char;
+ char devpath[MAXLINKNAMELEN];
+#endif
- DBG("netInit\n");
+ struct in_addr interfaceAddr, netAddr;
+ struct sockaddr_in addr;
+ Boolean useSystemTimeStamps = (rtOpts->time_mode == TIME_SYSTEM);
- /* open sockets */
- if ((netPath->eventSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0
- || (netPath->generalSock = socket(PF_INET, SOCK_DGRAM,
- IPPROTO_UDP)) < 0) {
- PERROR("failed to initalize sockets");
- return FALSE;
- }
- /* find a network interface */
- if (!(interfaceAddr.s_addr =
- findIface(rtOpts->ifaceName,
- &ptpClock->port_communication_technology,
- ptpClock->port_uuid_field, netPath)))
- return FALSE;
+ DBG("netInit\n");
- /* save interface address for IGMP refresh */
- netPath->interfaceAddr = interfaceAddr;
-
- DBG("Local IP address used : %s \n", inet_ntoa(interfaceAddr));
+ /* open sockets */
+ if ((netPath->eventSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0
+ || (netPath->generalSock = socket(PF_INET, SOCK_DGRAM,
+ IPPROTO_UDP)) < 0) {
+ PERROR("failed to initalize sockets");
+ return FALSE;
+ }
+ /* find a network interface */
+ if (!(interfaceAddr.s_addr =
+ findIface(rtOpts->ifaceName,
+ &ptpClock->port_communication_technology,
+ ptpClock->port_uuid_field, netPath)))
+ return FALSE;
- temp = 1; /* allow address reuse */
- if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_REUSEADDR,
- &temp, sizeof(int)) < 0
- || setsockopt(netPath->generalSock, SOL_SOCKET, SO_REUSEADDR,
- &temp, sizeof(int)) < 0) {
- DBG("failed to set socket reuse\n");
- }
- /* bind sockets */
- /*
- * need INADDR_ANY to allow receipt of multi-cast and uni-cast
- * messages
- */
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
- addr.sin_port = htons(PTP_EVENT_PORT);
- if (bind(netPath->eventSock, (struct sockaddr *)&addr,
- sizeof(struct sockaddr_in)) < 0) {
- PERROR("failed to bind event socket");
- return FALSE;
- }
- addr.sin_port = htons(PTP_GENERAL_PORT);
- if (bind(netPath->generalSock, (struct sockaddr *)&addr,
- sizeof(struct sockaddr_in)) < 0) {
- PERROR("failed to bind general socket");
- return FALSE;
- }
+ /* save interface address for IGMP refresh */
+ netPath->interfaceAddr = interfaceAddr;
+
+ DBG("Local IP address used : %s \n", inet_ntoa(interfaceAddr));
+
+ temp = 1; /* allow address reuse */
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_REUSEADDR,
+ &temp, sizeof(int)) < 0
+ || setsockopt(netPath->generalSock, SOL_SOCKET, SO_REUSEADDR,
+ &temp, sizeof(int)) < 0) {
+ DBG("failed to set socket reuse\n");
+ }
+ /* bind sockets */
+ /*
+ * need INADDR_ANY to allow receipt of multi-cast and uni-cast
+ * messages
+ */
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(PTP_EVENT_PORT);
+ if (bind(netPath->eventSock, (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in)) < 0) {
+ PERROR("failed to bind event socket");
+ return FALSE;
+ }
+ addr.sin_port = htons(PTP_GENERAL_PORT);
+ if (bind(netPath->generalSock, (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in)) < 0) {
+ PERROR("failed to bind general socket");
+ return FALSE;
+ }
@@ -545,111 +867,256 @@
PERROR("failed to call SO_BINDTODEVICE on the interface");
return FALSE;
}
+#endif /* linux */
+#endif /* USE_BINDTODEVICE */
+
+
+ /* send a uni-cast address if specified (useful for testing) */
+ if (rtOpts->unicastAddress[0]) {
+ /* Attempt a DNS lookup first. */
+ struct hostent *host;
+#ifdef __sun
+ host = gethostbyname(rtOpts->unicastAddress);
+#else
+ host = gethostbyname2(rtOpts->unicastAddress, AF_INET);
#endif
-#endif
-
-
- /* send a uni-cast address if specified (useful for testing) */
- if (rtOpts->unicastAddress[0]) {
- /* Attempt a DNS lookup first. */
- struct hostent *host;
- host = gethostbyname2(rtOpts->unicastAddress, AF_INET);
- if (host != NULL) {
- if (host->h_length != 4) {
- PERROR("unicast host resolved to non ipv4"
- "address");
- return FALSE;
- }
- netPath->unicastAddr =
- *(uint32_t *)host->h_addr_list[0];
- } else {
- /* Maybe it's a dotted quad. */
- if (!inet_aton(rtOpts->unicastAddress, &netAddr)) {
- ERROR("failed to encode uni-cast address: %s\n",
- rtOpts->unicastAddress);
- return FALSE;
- netPath->unicastAddr = netAddr.s_addr;
- }
- }
- } else {
- netPath->unicastAddr = 0;
- }
+ if (host != NULL) {
+ if (host->h_length != 4) {
+ PERROR("unicast host resolved to non ipv4"
+ "address");
+ return FALSE;
+ }
+ netPath->unicastAddr =
+ *(uint32_t *)host->h_addr_list[0];
+ } else {
+ /* Maybe it's a dotted quad. */
+ if (!inet_aton(rtOpts->unicastAddress, &netAddr)) {
+ ERROR("failed to encode uni-cast address: %s\n",
+ rtOpts->unicastAddress);
+ return FALSE;
+ netPath->unicastAddr = netAddr.s_addr;
+ }
+ }
+ } else {
+ netPath->unicastAddr = 0;
+ }
/* init UDP Multicast on both Default and Pear addresses */
if (!netInitMulticast(netPath, rtOpts)) {
return FALSE;
}
- /* set socket time-to-live to 1 */
+ /* set socket time-to-live to 1 */
- if (setsockopt(netPath->eventSock, IPPROTO_IP, IP_MULTICAST_TTL,
- &rtOpts->ttl, sizeof(int)) < 0
- || setsockopt(netPath->generalSock, IPPROTO_IP, IP_MULTICAST_TTL,
- &rtOpts->ttl, sizeof(int)) < 0) {
- PERROR("failed to set the multi-cast time-to-live");
- return FALSE;
- }
+ if (setsockopt(netPath->eventSock, IPPROTO_IP, IP_MULTICAST_TTL,
+ &rtOpts->ttl, sizeof(rtOpts->ttl)) < 0
+ || setsockopt(netPath->generalSock, IPPROTO_IP, IP_MULTICAST_TTL,
+ &rtOpts->ttl, sizeof(rtOpts->ttl)) < 0) {
+ PERROR("failed to set the multi-cast time-to-live");
+ return FALSE;
+ }
- /* enable loopback */
- temp = 1;
+#ifndef __sun
+ /* enable loopback */
+ temp = useSystemTimeStamps;
DBG("Going to set IP_MULTICAST_LOOP with %d \n", temp);
- if (setsockopt(netPath->eventSock, IPPROTO_IP, IP_MULTICAST_LOOP,
- &temp, sizeof(int)) < 0
- || setsockopt(netPath->generalSock, IPPROTO_IP, IP_MULTICAST_LOOP,
- &temp, sizeof(int)) < 0) {
- PERROR("failed to enable multi-cast loopback");
- return FALSE;
- }
+ if (setsockopt(netPath->eventSock, IPPROTO_IP, IP_MULTICAST_LOOP,
+ &temp, sizeof(int)) < 0
+ || setsockopt(netPath->generalSock, IPPROTO_IP, IP_MULTICAST_LOOP,
+ &temp, sizeof(int)) < 0) {
+ PERROR("failed to enable multi-cast loopback");
+ return FALSE;
+ }
+#else
+ /* enable loopback */
+ /* On Solaris, the IP_MULTICAST_LOOP takes uchar_t as data */
+ temp_char = useSystemTimeStamps;
+
+ DBG("Going to set IP_MULTICAST_LOOP with %d \n", temp);
+
+ if (setsockopt(netPath->eventSock, IPPROTO_IP, IP_MULTICAST_LOOP,
+ &temp_char, sizeof(temp_char)) < 0
+ || setsockopt(netPath->generalSock, IPPROTO_IP, IP_MULTICAST_LOOP,
+ &temp_char, sizeof(temp_char)) < 0) {
+ PERROR("failed to enable multi-cast loopback");
+ return FALSE;
+ }
+#endif
+
+#ifdef __sun
+ /* Open a socket for hw assist IOCTLs only if we need it */
+ if (rtOpts->time_mode == TIME_BOTH || rtOpts->time_mode == TIME_NIC) {
+ /*
+ * On Solaris, we cannot use netPath->eventSock to send ioctl to
+ * NIC driver as eventSock is a datagram socket. In the plumbed
+ * network stack, ip module act as a driver which means ip will
+ * not pass unknown ioctl down. So, we create a new fd to be
+ * used for ioctls to NIC.
+ */
+ snprintf(devpath, sizeof(devpath), "/dev/net/%s", rtOpts->ifaceName);
+ if ((netPath->devFd = open(devpath, O_RDWR)) < 0) {
+ PERROR("failed to open device %s\n", devpath);
+ return FALSE;
+ } else {
+ DBG("open()ed %s\n", devpath);
+ }
+ }
+
+#endif
/* make timestamps available through recvmsg() */
- if (!netInitTimestamping(netPath)) {
+ if (!netInitTimestamping(netPath, useSystemTimeStamps)) {
ERROR("failed to enable receive time stamps");
return FALSE;
}
- return TRUE;
+ return TRUE;
}
/*Check if data have been received*/
-int
+int
netSelect(TimeInternal * timeout, NetPath * netPath)
{
- int ret, nfds;
- fd_set readfds;
- struct timeval tv, *tv_ptr;
+ int ret, nfds;
+ fd_set readfds;
+ struct timespec tv;
+ const struct timespec *tv_ptr;
+ sigset_t ignore_sigs;
- if (timeout < 0)
- return FALSE;
+ if (netPath->tx_stack.count)
+ return TRUE;
- FD_ZERO(&readfds);
- FD_SET(netPath->eventSock, &readfds);
- FD_SET(netPath->generalSock, &readfds);
+ FD_ZERO(&readfds);
+ FD_SET(netPath->eventSock, &readfds);
+ FD_SET(netPath->generalSock, &readfds);
- if (timeout) {
- tv.tv_sec = timeout->seconds;
- tv.tv_usec = timeout->nanoseconds / 1000;
- tv_ptr = &tv;
- } else
- tv_ptr = 0;
+ if (timeout) {
+ tv.tv_sec = timeout->seconds;
+ tv.tv_nsec = timeout->nanoseconds;
+ tv_ptr = &tv;
+ } else
+ tv_ptr = 0;
- if (netPath->eventSock > netPath->generalSock)
- nfds = netPath->eventSock;
- else
- nfds = netPath->generalSock;
+ if (netPath->eventSock > netPath->generalSock)
+ nfds = netPath->eventSock;
+ else
+ nfds = netPath->generalSock;
- ret = select(nfds + 1, &readfds, 0, 0, tv_ptr) > 0;
+ ret = pselect(nfds + 1, &readfds, 0, 0, tv_ptr, &ignore_sigs) > 0;
- if (ret < 0) {
- if (errno == EAGAIN || errno == EINTR)
- return 0;
- }
- return ret;
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+ }
+ return ret;
}
+#ifdef linux
+/* Used to get receive timestamps when in the SYSTEM LINUX_HW or LINUX_SW time modes */
+static int do_rx_timestamping(struct msghdr *msg, TimeInternal *time, RunTimeOpts *rtOpts)
+{
+ struct cmsghdr *cm;
+ struct timeval *tv;
+ Boolean have_time = FALSE;
+ /* @ioctl_timestamping As v1 code, added bool to break out of loop on discovery for ts. */
+ for (cm = CMSG_FIRSTHDR(msg); !have_time && cm != NULL; cm = CMSG_NXTHDR(msg, cm)) {
+ struct timespec *stamp;
+
+ DUMP("CM", cm, cm->cmsg_len);
+
+ if (cm->cmsg_level != SOL_SOCKET)
+ continue;
+
+ switch (cm->cmsg_type) {
+
+ case SCM_TIMESTAMP:
+ tv = (struct timeval *)CMSG_DATA(cm);
+ if(cm->cmsg_len < sizeof(*tv))
+ {
+ ERROR("received short SCM_TIMESTAMP (%d/%d)\n",
+ cm->cmsg_len, sizeof(*tv));
+ return -1;
+ }
+
+ time->seconds = tv->tv_sec;
+ time->nanoseconds = tv->tv_usec*1000;
+ have_time = TRUE;
+ break;
+
+#ifdef SO_TIMESTAMPNS
+ case SCM_TIMESTAMPNS:
+ stamp = (struct timespec *)CMSG_DATA(cm);
+ if(cm->cmsg_len < sizeof(*stamp))
+ {
+ ERROR("received short SCM_TIMESTAMPNS (%d/%d)\n",
+ cm->cmsg_len, sizeof(*stamp));
+ return -1;
+ }
+ time->seconds = stamp->tv_sec;
+ time->nanoseconds = stamp->tv_nsec;
+ have_time = TRUE;
+ break;
+#endif
+
+ case SO_TIMESTAMPING:
+ {
+ const char *ts_text;
+
+ /* array of three time stamps: software, HW, raw HW */
+ stamp = (struct timespec*)CMSG_DATA(cm);
+
+ if (cm->cmsg_len < sizeof(*stamp)*3) {
+ ERROR("received short SO_TIMESTAMPING (%d/%d)\n",
+ cm->cmsg_len, (int)sizeof(*stamp)*3);
+ return -1;
+ }
+
+ switch (rtOpts->time_mode) {
+ case TIME_SYSTEM_LINUX_SW:
+ /* Desired timestamp is first in array */
+ ts_text = "SW SYS";
+ break;
+ case TIME_SYSTEM_LINUX_HW:
+ /* Desired timestamp is second in array */
+ ts_text = "HW SYS";
+ stamp++;
+ break;
+ case TIME_NIC:
+ case TIME_BOTH:
+ /* Desired timestamp is third in array */
+ ts_text = "HW RAW";
+ stamp += 2;
+ break;
+ default:
+ ERROR("Invalid case in switch %d\n", rtOpts->time_mode);
+ break;
+ }
+
+ if (stamp->tv_sec && stamp->tv_nsec) {
+ DBG2("%s Rx TIMESTAMP %d.%09d\n", ts_text, stamp->tv_sec, stamp->tv_nsec);
+ time->seconds = stamp->tv_sec;
+ time->nanoseconds = stamp->tv_nsec;
+
+ have_time = TRUE;
+ }
+ }
+ break;
+ }
+ }
+
+ if (!have_time) {
+ DBG("no receive time stamp\n");
+ return -1;
+ }
+ DBG("kernel recv time stamp %us %dns\n", time->seconds, time->nanoseconds);
+
+ return 0;
+}
+#endif /* linux */
/**
* store received data from network to "buf" , get and store the
@@ -668,16 +1135,17 @@
*/
ssize_t
-netRecvEvent(Octet * buf, TimeInternal * time, NetPath * netPath)
+netRecvEvent(Octet * buf, TimeInternal * time, PtpClock *ptpClock, RunTimeOpts *rtOpts, Boolean *loopedBackTx)
{
ssize_t ret;
struct msghdr msg;
struct iovec vec[1];
struct sockaddr_in from_addr;
+ NetPath *netPath = &ptpClock->netPath;
union {
struct cmsghdr cm;
- char control[CMSG_SPACE(sizeof(struct timeval))];
+ char control[512];
} cmsg_un;
struct cmsghdr *cmsg;
@@ -690,10 +1158,25 @@
#endif
#if defined(SO_TIMESTAMP)
- struct timeval * tv;
+ struct timeval tv;
+ struct timeval * tv1;
#endif
Boolean timestampValid = FALSE;
+ /* Pop packet from stack. */
+ if (netPath->tx_stack.count) {
+ int index = netPath->tx_stack.count - 1;
+ ret = netPath->tx_stack.data[index].len;
+ memcpy(buf, netPath->tx_stack.data[index].buf, ret);
+ time->seconds = netPath->tx_stack.data[index].ts.seconds;
+ time->nanoseconds = netPath->tx_stack.data[index].ts.nanoseconds;
+ netPath->tx_stack.count--;
+ DBGV("netRecvEvent: ts from stack %d bytes\n", ret);
+ *loopedBackTx = TRUE;
+ return ret;
+ }
+
+ *loopedBackTx = FALSE;
vec[0].iov_base = buf;
vec[0].iov_len = PACKET_SIZE;
@@ -735,10 +1218,19 @@
netPath->lastRecvAddr = from_addr.sin_addr.s_addr;
#endif
+ /* If we are collecting timestamps using the driver IOCTL method, we
+ * need to wait until we've decoded the header before processing the
+ * timestamps. For other methods, call do_rx_timestamping().
+ */
+ if (ptpClock->tsMethod == TS_METHOD_DRIVER_IOCTL) {
+ return ret;
+ }
-
+#ifdef linux
+ return (do_rx_timestamping(&msg, time, rtOpts) == 0)? ret: 0;
+#else
if (msg.msg_controllen <= 0) {
- ERROR("received short ancillary data (%ld/%ld)\n",
+ ERROR("netRecvEvent: received short ancillary data (%ld/%ld)\n",
(long)msg.msg_controllen, (long)sizeof(cmsg_un.control));
return 0;
@@ -772,9 +1264,16 @@
#if defined(SO_TIMESTAMP)
if(cmsg->cmsg_type == SCM_TIMESTAMP) {
- tv = (struct timeval *)CMSG_DATA(cmsg);
- time->seconds = tv->tv_sec;
- time->nanoseconds = tv->tv_usec * 1000;
+ tv1 = (struct timeval *)CMSG_DATA(cmsg);
+ /*
+ * tv->sec is 8 byte field,
+ * tv may not be 8 byte aligned.
+ * Use memcpy();
+ *
+ */
+ memcpy(&tv, tv1, sizeof(struct timeval));
+ time->seconds = tv.tv_sec;
+ time->nanoseconds = tv.tv_usec * 1000;
timestampValid = TRUE;
DBGV("kernel MICRO recv time stamp %us %dns\n",
time->seconds, time->nanoseconds);
@@ -795,6 +1294,7 @@
}
return ret;
+#endif /* ifdef linux */
}
@@ -811,13 +1311,34 @@
* @return
*/
+ssize_t
+netRecvGeneral_old(Octet * buf, TimeInternal * time, NetPath * netPath)
+{
+ ssize_t ret;
+ struct sockaddr_in from_addr;
+ socklen_t addr_len = sizeof(struct sockaddr_in);
+
+ ret = recvfrom(netPath->generalSock, buf, PACKET_SIZE, MSG_DONTWAIT,
+ (struct sockaddr *)&from_addr, &addr_len);
+ if (ret <= 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ return ret;
+ }
+
+ DBGV("netRecvGeneral: rxed %d bytes\n", ret);
+ return ret;
+}
+
ssize_t
-netRecvGeneral(Octet * buf, TimeInternal * time, NetPath * netPath)
+netRecvGeneral(Octet * buf, TimeInternal * time, PtpClock *ptpClock, RunTimeOpts *rtOpts)
{
ssize_t ret;
struct msghdr msg;
struct iovec vec[1];
struct sockaddr_in from_addr;
+ NetPath *netPath = &ptpClock->netPath;
union {
struct cmsghdr cm;
@@ -825,7 +1346,7 @@
} cmsg_un;
struct cmsghdr *cmsg;
-
+
#if defined(SO_TIMESTAMPNS)
struct timespec * ts;
#elif defined(SO_BINTIME)
@@ -834,7 +1355,8 @@
#endif
#if defined(SO_TIMESTAMP)
- struct timeval * tv;
+ struct timeval tv;
+ struct timeval * tv1;
#endif
Boolean timestampValid = FALSE;
@@ -880,11 +1402,18 @@
#ifdef PTP_EXPERIMENTAL
netPath->lastRecvAddr = from_addr.sin_addr.s_addr;
#endif
-
+
+ /* If we are collecting timestamps using the driver IOCTL method, we
+ * need to wait until we've decoded the header before processing the
+ * timestamps. For other methods, call do_rx_timestamping().
+ */
+ if (ptpClock->tsMethod == TS_METHOD_DRIVER_IOCTL) {
+ return ret;
+ }
if (msg.msg_controllen <= 0) {
- ERROR("received short ancillary data (%ld/%ld)\n",
+ ERROR("netRecvGeneral : received short ancillary data (%ld/%ld)\n",
(long)msg.msg_controllen, (long)sizeof(cmsg_un.control));
return 0;
@@ -918,9 +1447,10 @@
#if defined(SO_TIMESTAMP)
if(cmsg->cmsg_type == SCM_TIMESTAMP) {
- tv = (struct timeval *)CMSG_DATA(cmsg);
- time->seconds = tv->tv_sec;
- time->nanoseconds = tv->tv_usec * 1000;
+ tv1 = (struct timeval *)CMSG_DATA(cmsg);
+ memcpy(&tv, tv1, sizeof (struct timeval));
+ time->seconds = tv.tv_sec;
+ time->nanoseconds = tv.tv_usec * 1000;
timestampValid = TRUE;
DBGV("kernel MICRO recv time stamp %us %dns\n",
time->seconds, time->nanoseconds);
@@ -955,11 +1485,12 @@
///
/// TODO: merge these 2 functions into one
///
-ssize_t
-netSendEvent(Octet * buf, UInteger16 length, NetPath * netPath, Integer32 alt_dst)
+int
+netSendEvent(Octet * buf, UInteger16 length, PtpClock *ptpClock, RunTimeOpts *rtOpts, Integer32 alt_dst)
{
- ssize_t ret;
+ int ret;
struct sockaddr_in addr;
+ NetPath *netPath = &ptpClock->netPath;
addr.sin_family = AF_INET;
addr.sin_port = htons(PTP_EVENT_PORT);
@@ -971,75 +1502,71 @@
addr.sin_addr.s_addr = alt_dst;
}
- ret = sendto(netPath->eventSock, buf, length, 0,
- (struct sockaddr *)&addr,
- sizeof(struct sockaddr_in));
- if (ret <= 0)
- DBG("error sending uni-cast event message\n");
- /*
+ ret = sendMessage(netPath->eventSock, buf, length,
+ (struct sockaddr *)&addr, sizeof(addr),
+ "unicast event");
+ if (ret == 0) {
+ /*
* Need to forcibly loop back the packet since
- * we are not using multicast.
+ * we are not using multicast.
*/
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- ret = sendto(netPath->eventSock, buf, length, 0,
- (struct sockaddr *)&addr,
- sizeof(struct sockaddr_in));
- if (ret <= 0)
- DBG("error looping back uni-cast event message\n");
-
+ ret = sendMessage(netPath->eventSock, buf, length,
+ (struct sockaddr *)&addr, sizeof(addr),
+ "loopback unicast event");
+ }
} else {
addr.sin_addr.s_addr = netPath->multicastAddr;
+
+ ret = sendMessage(netPath->eventSock, buf, length,
+ (struct sockaddr *)&addr, sizeof(addr),
+ "multicast event");
+ }
- ret = sendto(netPath->eventSock, buf, length, 0,
- (struct sockaddr *)&addr,
- sizeof(struct sockaddr_in));
- if (ret <= 0)
- DBG("error sending multi-cast event message\n");
+ if (ret == 0) {
+ if (txTimestamp(ptpClock, (char *)buf, length, rtOpts->time_mode, TRUE) != 0) {
+ ERROR("txTimestamp failed\n");
+ ret = ENOTIMESTAMP;
+ }
}
return ret;
}
-ssize_t
+int
netSendGeneral(Octet * buf, UInteger16 length, NetPath * netPath, Integer32 alt_dst)
{
- ssize_t ret;
+ int ret;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PTP_GENERAL_PORT);
if(netPath->unicastAddr || alt_dst ){
- if (netPath->unicastAddr) {
- addr.sin_addr.s_addr = netPath->unicastAddr;
+ if (netPath->unicastAddr) {
+ addr.sin_addr.s_addr = netPath->unicastAddr;
} else {
addr.sin_addr.s_addr = alt_dst;
}
-
-
- ret = sendto(netPath->generalSock, buf, length, 0,
- (struct sockaddr *)&addr,
- sizeof(struct sockaddr_in));
- if (ret <= 0)
- DBG("error sending uni-cast general message\n");
+ ret = sendMessage(netPath->eventSock, buf, length,
+ (struct sockaddr *)&addr, sizeof(addr),
+ "unicast general");
} else {
addr.sin_addr.s_addr = netPath->multicastAddr;
+ ret = sendMessage(netPath->generalSock, buf, length,
+ (struct sockaddr *)&addr, sizeof(addr),
+ "multicast general");
+ }
- ret = sendto(netPath->generalSock, buf, length, 0,
- (struct sockaddr *)&addr,
- sizeof(struct sockaddr_in));
- if (ret <= 0)
- DBG("error sending multi-cast general message\n");
- }
return ret;
}
-ssize_t
+int
netSendPeerGeneral(Octet * buf, UInteger16 length, NetPath * netPath)
{
- ssize_t ret;
+ int ret;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
@@ -1048,64 +1575,52 @@
if (netPath->unicastAddr) {
addr.sin_addr.s_addr = netPath->unicastAddr;
- ret = sendto(netPath->generalSock, buf, length, 0,
- (struct sockaddr *)&addr,
- sizeof(struct sockaddr_in));
- if (ret <= 0)
- DBG("error sending uni-cast general message\n");
-
+ ret = sendMessage(netPath->eventSock, buf, length,
+ (struct sockaddr *)&addr, sizeof(addr),
+ "unicast general");
} else {
addr.sin_addr.s_addr = netPath->peerMulticastAddr;
- ret = sendto(netPath->generalSock, buf, length, 0,
- (struct sockaddr *)&addr,
- sizeof(struct sockaddr_in));
- if (ret <= 0)
- DBG("error sending multi-cast general message\n");
+ ret = sendMessage(netPath->generalSock, buf, length,
+ (struct sockaddr *)&addr, sizeof(addr),
+ "multicast general");
}
return ret;
}
-ssize_t
-netSendPeerEvent(Octet * buf, UInteger16 length, NetPath * netPath)
+int
+netSendPeerEvent(Octet * buf, UInteger16 length, PtpClock * ptpClock, RunTimeOpts * rtOpts)
{
- ssize_t ret;
+ int ret;
struct sockaddr_in addr;
+ NetPath *netPath = &ptpClock->netPath;
addr.sin_family = AF_INET;
addr.sin_port = htons(PTP_EVENT_PORT);
if (netPath->unicastAddr) {
addr.sin_addr.s_addr = netPath->unicastAddr;
-
- ret = sendto(netPath->eventSock, buf, length, 0,
- (struct sockaddr *)&addr,
- sizeof(struct sockaddr_in));
- if (ret <= 0)
- DBG("error sending uni-cast event message\n");
-
- /*
- * Need to forcibly loop back the packet since
- * we are not using multicast.
- */
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
- ret = sendto(netPath->eventSock, buf, length, 0,
- (struct sockaddr *)&addr,
- sizeof(struct sockaddr_in));
- if (ret <= 0)
- DBG("error looping back uni-cast event message\n");
+ ret = sendMessage(netPath->eventSock, buf, length,
+ (struct sockaddr *)&addr, sizeof(addr),
+ "unicast event");
} else {
addr.sin_addr.s_addr = netPath->peerMulticastAddr;
+ ret = sendMessage(netPath->eventSock, buf, length,
+ (struct sockaddr *)&addr, sizeof(addr),
+ "multicast event");
+ }
- ret = sendto(netPath->eventSock, buf, length, 0,
- (struct sockaddr *)&addr,
- sizeof(struct sockaddr_in));
- if (ret <= 0)
- DBG("error sending multi-cast event message\n");
- }
- return ret;
+ /* @pdelay_req TODO timestamp is not being collected when PDELAY_REQ messages */
+ /* are sent. Collect the timestamp and throw it away for now */
+ if (ret == 0) {
+ if (txTimestamp(ptpClock, buf, length, rtOpts->time_mode, FALSE) != 0) {
+ ERROR("txTimestamp failed\n");
+ ret = ENOTIMESTAMP;
+ }
+ }
+
+ return ret;
}
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/dep/ptpd_dep.h
--- a/src/dep/ptpd_dep.h Tue May 14 17:07:59 2013 -0700
+++ b/src/dep/ptpd_dep.h Sun Oct 27 22:49:29 2013 -0700
@@ -1,9 +1,39 @@
+/*-
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
+ * Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
+ * Martin Burnicki, Gael Mace, Alexandre Van Kempen
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
/**
* @file ptpd_dep.h
- *
+ *
* @brief External definitions for inclusion elsewhere.
- *
- *
+ *
+ *
*/
#ifndef PTPD_DEP_H_
@@ -43,12 +73,12 @@
#define EMERGENCY(x, ...) message(LOG_EMERG, x, ##__VA_ARGS__)
#define ALERT(x, ...) message(LOG_ALERT, x, ##__VA_ARGS__)
#define CRITICAL(x, ...) message(LOG_CRIT, x, ##__VA_ARGS__)
-#define ERROR(x, ...) message(LOG_ERR, x, ##__VA_ARGS__)
+#define ERROR(x, ...) message(LOG_ERR, x, ##__VA_ARGS__)
#define PERROR(x, ...) message(LOG_ERR, x " (strerror: %m)\n", ##__VA_ARGS__)
#define WARNING(x, ...) message(LOG_WARNING, x, ##__VA_ARGS__)
-#define NOTIFY(x, ...) message(LOG_NOTICE, x, ##__VA_ARGS__)
+#define NOTIFY(x, ...) message(LOG_NOTICE, x, ##__VA_ARGS__)
#define NOTICE(x, ...) message(LOG_NOTICE, x, ##__VA_ARGS__)
-#define INFO(x, ...) message(LOG_INFO, x, ##__VA_ARGS__)
+#define INFO(x, ...) message(LOG_INFO, x, ##__VA_ARGS__)
@@ -90,7 +120,6 @@
#undef PTPD_DBG2
#define PTPD_DBG
#define PTPD_DBG2
-
#define DBGV(x, ...) message(LOG_DEBUGV, x, ##__VA_ARGS__)
#else
#define DBGV(x, ...)
@@ -116,6 +145,12 @@
#define DBG(x, ...)
#endif
+#ifdef PTPD_DUMP
+#define DUMP(text, addr, len) dump(text, addr, len)
+#else
+#define DUMP(text, addr, len)
+#endif
+
/** \}*/
/** \name Endian corrections*/
@@ -179,7 +214,7 @@
void msgUnpackManagement(Octet * buf,MsgManagement*);
UInteger8 msgUnloadManagement(Octet * buf,MsgManagement*,PtpClock*,RunTimeOpts*);
void msgUnpackManagementPayload(Octet *buf, MsgManagement *manage);
-void msgPackHeader(Octet * buf,PtpClock*);
+void msgPackHeader(Octet * buf,PtpClock*,unsigned int);
void msgPackAnnounce(Octet * buf,PtpClock*);
void msgPackSync(Octet * buf,Timestamp*,PtpClock*);
void msgPackFollowUp(Octet * buf,Timestamp*,PtpClock*);
@@ -211,13 +246,14 @@
Boolean netInit(NetPath*,RunTimeOpts*,PtpClock*);
Boolean netShutdown(NetPath*);
int netSelect(TimeInternal*,NetPath*);
-ssize_t netRecvEvent(Octet*,TimeInternal*,NetPath*);
-ssize_t netRecvGeneral(Octet*,TimeInternal*,NetPath*);
-ssize_t netSendEvent(Octet*,UInteger16,NetPath*, Integer32 );
-ssize_t netSendGeneral(Octet*,UInteger16,NetPath*, Integer32 );
-ssize_t netSendPeerGeneral(Octet*,UInteger16,NetPath*);
-ssize_t netSendPeerEvent(Octet*,UInteger16,NetPath*);
+ssize_t netRecvEvent(Octet*,TimeInternal*,PtpClock*,RunTimeOpts*,Boolean*);
+ssize_t netRecvGeneral(Octet*,TimeInternal*,PtpClock *, RunTimeOpts *);
+/* These functions all return 0 for success or an errno in the case of failure */
+int netSendEvent(Octet*,UInteger16,PtpClock*,RunTimeOpts*,Integer32);
+int netSendGeneral(Octet*,UInteger16,NetPath*,Integer32);
+int netSendPeerGeneral(Octet*,UInteger16,NetPath*);
+int netSendPeerEvent(Octet*,UInteger16,PtpClock*,RunTimeOpts*);
Boolean netRefreshIGMP(NetPath *, RunTimeOpts *, PtpClock *);
@@ -234,7 +270,7 @@
offset_from_master_filter*,RunTimeOpts*,PtpClock*,TimeInternal*);
void updateClock(RunTimeOpts*,PtpClock*);
-void servo_perform_clock_step(RunTimeOpts * rtOpts, PtpClock * ptpClock);
+void servo_perform_clock_step(RunTimeOpts * rtOpts, PtpClock * ptpClock, TimeInternal*);
/** \}*/
@@ -267,19 +303,110 @@
void message(int priority, const char *format, ...);
+void dump(const char *text, void *addr, int len);
void displayStats(RunTimeOpts *rtOpts, PtpClock *ptpClock);
Boolean nanoSleep(TimeInternal*);
-void getTime(TimeInternal*);
-void setTime(TimeInternal*);
double getRand(void);
-Boolean adjFreq(Integer32);
void recordSync(RunTimeOpts * rtOpts, UInteger16 sequenceId, TimeInternal * time);
+/** \}*/
+
+/** \name time.c (Unix API dependent)
+ * -Timing system API*/
+ /**\{*/
+Boolean initTime(RunTimeOpts*, PtpClock*);
+void shutdownTime(PtpClock*);
+void getTime(TimeInternal*, TimeMode, PtpClock*);
+void setTime(TimeInternal*, TimeMode, PtpClock*);
+void syncSystemWithNIC(RunTimeOpts *rtOpts, PtpClock *ptpClock);
+
+/**
+ * Adjusts the time, ideally by varying the clock rate.
+ *
+ * @param adj frequency adjustment: a time source which supports that ignores the offset
+ */
+void adjTime(LongDouble adj, TimeMode timeMode, PtpClock*);
+
+/**
+ * Adjusts the time by shifting the clock.
+ *
+ * @param offset this value must be substracted from clock (might be negative)
+ */
+void adjTimeOffset(TimeInternal *offset, TimeMode timeMode, PtpClock*);
+
+/**
+ * Adjusts the time by shifting the clock to insert a leap second
+ */
+void adjTimeInsertLeapSecond(TimeMode timeMode, RunTimeOpts*, PtpClock*);
+
+/**
+ * Adjusts the time by shifting the clock to delete a leap second
+ */
+void adjTimeDeleteLeapSecond(TimeMode timeMode, RunTimeOpts*, PtpClock*);
+
+/**
+ * Gets the time when the latest outgoing packet left the host.
+ *
+ * There is no way to identify the packet the time stamp belongs to,
+ * so this must be called after sending each packet until the time
+ * stamp for the packet is available. This can be some (hopefully
+ * small) time after the packet was passed to the IP stack.
+ *
+ * There is no mechanism either to determine packet loss and thus a
+ * time stamp which never becomes available.
+ *
+ * @todo Can such packet loss occur?
+ *
+ * Does not work with TIME_SYSTEM.
+ *
+ * @retval sendTimeStamp set to the time when the packet left the host
+ * @return TRUE if the time stamp was available
+ */
+Boolean getSendTime(TimeInternal *sendTimeStamp, TimeMode, NetPath*);
+
+/**
+ * Gets the time when the packet identified by the given attributes
+ * was received by the host.
+ *
+ * Because the arrival of packets is out of the control of PTPd, the
+ * time stamping must support unique identification of which time
+ * stamp belongs to which packet.
+ *
+ * Due to packet loss in the receive queue, there can be time stamps
+ * without IP packets. getReceiveTime() automatically discards stale
+ * time stamps, including the ones that where returned by
+ * getReceiveTime(). This implies that there is not guarantee that
+ * calling getReceiveTime() more than once for the same packet
+ * will always return a result.
+ *
+ * Due to hardware limitations only one time stamp might be stored
+ * until queried by the NIC driver; this can lead to packets without
+ * time stamp. This needs to be handled by the caller of
+ * getReceiveTime(), for example by ignoring the packet.
+ *
+ * Does not work with TIME_SYSTEM.
+ *
+ * @retval recvTimeStamp set to the time when the packet entered the host, if available
+ * @return TRUE if the time stamp was available
+ */
+Boolean getReceiveTime(TimeInternal *recvTimeStamp,
+ ClockIdentity clockIdentity,
+ UInteger16 sequenceId, TimeMode, NetPath*);
+
+long get_current_tickrate(void);
+
#if defined(__APPLE__)
void adjTime(Integer32);
#endif /* __APPLE__ */
+#if !defined(__APPLE__)
+void setTimexFlags(int flags, Boolean quiet);
+void unsetTimexFlags(int flags, Boolean quiet);
+int getTimexFlags(void);
+Boolean checkTimexFlags(int flags);
+#endif /* apple */
+
/** \}*/
/** \name timer.c (Unix API dependent)
@@ -297,6 +424,9 @@
void timerStart_random(UInteger16 index, float interval, IntervalTimer * itimer);
Boolean timerExpired(UInteger16,IntervalTimer*);
+
+/** gets the current system time */
+void timerNow(TimeInternal*);
/** \}*/
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/dep/servo.c
--- a/src/dep/servo.c Tue May 14 17:07:59 2013 -0700
+++ b/src/dep/servo.c Sun Oct 27 22:49:29 2013 -0700
@@ -1,4 +1,6 @@
/*-
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
* Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
* Martin Burnicki, Gael Mace, Alexandre Van Kempen
* Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
@@ -69,8 +71,8 @@
#if !defined(__APPLE__)
if (!rtOpts->noAdjust)
- adjFreq(0);
- ptpClock->observed_drift = 0;
+ adjTime(0, rtOpts->time_mode, ptpClock);
+ ptpClock->observed_drift = 0.0;
#endif /* apple */
@@ -85,10 +87,8 @@
ptpClock->ofm_filt.y = 0;
ptpClock->ofm_filt.nsec_prev = -1; /* AKB: -1 used for non-valid nsec time */
- ptpClock->ofm_filt.nsec_prev = 0;
-
ptpClock->owd_filt.s_exp = 0; /* clears one-way delay filter */
- rtOpts->offset_first_updated = FALSE;
+ ptpClock->offset_first_updated = FALSE;
ptpClock->char_last_msg='I';
@@ -105,6 +105,10 @@
void
updateDelay(one_way_delay_filter * owd_filt, RunTimeOpts * rtOpts, PtpClock * ptpClock, TimeInternal * correctionField)
{
+ /* updates paused, leap second pending - do nothing */
+ if(ptpClock->leapSecondInProgress)
+ return;
+
/* todo: do all intermediate calculations on temp vars */
TimeInternal prev_meanPathDelay = ptpClock->meanPathDelay;
@@ -121,31 +125,41 @@
/* calc 'slave_to_master_delay' */
subTime(&slave_to_master_delay, &ptpClock->delay_req_receive_time,
&ptpClock->delay_req_send_time);
-
+/*
if (slave_to_master_delay.nanoseconds < 0) {
- INFO("updateDelay aborted, delay %d is negative",
+ INFO("updateDelay aborted, delay %d is negative\n",
return;
}
+*/
- if (rtOpts->maxDelay) { /* If maxDelay is 0 then it's OFF */
- if (slave_to_master_delay.seconds && rtOpts->maxDelay) {
+ /* If maxDelay is 0 then it's OFF */
+ if ((rtOpts->maxDelay.seconds || rtOpts->maxDelay.nanoseconds) &&
+ ptpClock->offset_first_updated) {
+ if (slave_to_master_delay.seconds) {
INFO("updateDelay aborted, delay greater than 1"
- " second.");
+ " second.\n");
msgDump(ptpClock);
goto display;
}
-
- if (slave_to_master_delay.nanoseconds > rtOpts->maxDelay) {
- INFO("updateDelay aborted, delay %d greater than "
- "administratively set maximum %d\n",
- rtOpts->maxDelay);
+ /* Get scalar versions of the the slave to master delay and
+ * max delay */
+ int64_t s_to_m_delay = internalTime_to_scalar(&slave_to_master_delay);
+ int64_t max_delay = internalTime_to_scalar(&rtOpts->maxDelay);
+
+ if ((s_to_m_delay > max_delay) ||
+ (s_to_m_delay < -max_delay)) {
+ INFO("updateDelay aborted, s->m delay %d.%09d greater than "
+ "administratively set maximum %d.%09d\n",
+ rtOpts->maxDelay.seconds,
+ rtOpts->maxDelay.nanoseconds);
msgDump(ptpClock);
goto display;
- }
}
}
+ }
/*
@@ -153,7 +167,7 @@
* - update the global delaySM variable
* - calculate a new filtered MPD
*/
- if (rtOpts->offset_first_updated) {
+ if (ptpClock->offset_first_updated) {
Integer16 s;
/*
@@ -313,6 +327,12 @@
updateOffset(TimeInternal * send_time, TimeInternal * recv_time,
offset_from_master_filter * ofm_filt, RunTimeOpts * rtOpts, PtpClock * ptpClock, TimeInternal * correctionField)
{
+ DBGV("UTCOffset: %d, %d | leap 59: %d | leap61: %d\n",
+ ptpClock->currentUtcOffsetValid, ptpClock->currentUtcOffset,
+ ptpClock->leap59, ptpClock->leap61);
+ /* updates paused, leap second pending - do nothing */
+ if(ptpClock->leapSecondInProgress)
+ return;
DBGV("==> updateOffset\n");
@@ -322,19 +342,24 @@
/* calc 'master_to_slave_delay' */
subTime(&master_to_slave_delay, recv_time, send_time);
- if (rtOpts->maxDelay) { /* If maxDelay is 0 then it's OFF */
- if (master_to_slave_delay.seconds && rtOpts->maxDelay) {
- INFO("updateOffset aborted, delay greater than 1"
- " second.");
- msgDump(ptpClock);
- return;
- }
- if (master_to_slave_delay.nanoseconds > rtOpts->maxDelay) {
- INFO("updateOffset aborted, delay %d greater than "
- "administratively set maximum %d\n",
- rtOpts->maxDelay);
+
+ /* If maxDelay is 0 then it's OFF */
+ if ((rtOpts->maxDelay.seconds || rtOpts->maxDelay.nanoseconds) &&
+ ptpClock->offset_first_updated) {
+ /* Get scalar versions of the the slave to master delay and
+ * max delay */
+ int64_t m_to_s_delay = internalTime_to_scalar(&master_to_slave_delay);
+ int64_t max_delay = internalTime_to_scalar(&rtOpts->maxDelay);
+
+ if ((m_to_s_delay > max_delay) ||
+ (m_to_s_delay < -max_delay)) {
+ INFO("updateDelay aborted, m->s delay %d.%09d greater than "
+ "administratively set maximum %d.%09d\n",
+ rtOpts->maxDelay.seconds,
+ rtOpts->maxDelay.nanoseconds);
msgDump(ptpClock);
return;
}
@@ -344,6 +369,13 @@
ptpClock->char_last_msg='S';
ptpClock->last_packet_was_sync = TRUE;
+ DBGV("send_time = %d.%09d\n",
+ send_time->seconds, send_time->nanoseconds);
+ DBGV("recv_time = %d.%09d\n",
+ recv_time->seconds, recv_time->nanoseconds);
+ DBGV("master_to_slave_delay = %d.%09d\n",
+
/*
* The packet has passed basic checks, so we'll:
* - update the global delayMS variable
@@ -355,14 +387,21 @@
/* Take care about correctionField */
subTime(&ptpClock->delayMS,
&ptpClock->delayMS, correctionField);
+ DBGV("delayMS = %d.%09d\n",
+ ptpClock->delayMS.seconds, ptpClock->delayMS.nanoseconds);
+ DBGV("correctionField = %d.%09d\n",
+ correctionField->seconds, correctionField->nanoseconds);
+ DBGV("meanPathDelay = %d.%09d\n",
+ ptpClock->meanPathDelay.seconds, ptpClock->meanPathDelay.nanoseconds);
+ DBGV("updateOffset: delayMechanism = %d\n", ptpClock->delayMechanism);
/* update 'offsetFromMaster' */
if (ptpClock->delayMechanism == P2P) {
subTime(&ptpClock->offsetFromMaster,
&ptpClock->delayMS,
&ptpClock->peerMeanPathDelay);
- } else if (ptpClock->delayMechanism == E2E) {
+ } else {
/* (End to End mode) */
subTime(&ptpClock->offsetFromMaster,
&ptpClock->delayMS,
@@ -372,13 +411,16 @@
if (ptpClock->offsetFromMaster.seconds) {
/* cannot filter with secs, clear filter */
ofm_filt->nsec_prev = 0;
- rtOpts->offset_first_updated = TRUE;
+ ptpClock->offset_first_updated = TRUE;
return;
}
/* filter 'offsetFromMaster' */
- ofm_filt->y = ptpClock->offsetFromMaster.nanoseconds / 2 +
- ofm_filt->nsec_prev / 2;
+ /* The time is normalised so the nanoseconds will be less than 10^9. Improve
+ * accuracy of calculation by doing division after addition. Add 1 to get correct
+ * rounding.
+ */
+ ofm_filt->y = (ptpClock->offsetFromMaster.nanoseconds + ofm_filt->nsec_prev + 1) / 2;
ofm_filt->nsec_prev = ptpClock->offsetFromMaster.nanoseconds;
ptpClock->offsetFromMaster.nanoseconds = ofm_filt->y;
@@ -388,41 +430,44 @@
* Offset must have been computed at least one time before
* computing end to end delay
*/
- rtOpts->offset_first_updated = TRUE;
+ ptpClock->offset_first_updated = TRUE;
}
-void servo_perform_clock_step(RunTimeOpts * rtOpts, PtpClock * ptpClock)
+void servo_perform_clock_step(RunTimeOpts * rtOpts, PtpClock * ptpClock, TimeInternal *offset)
{
if(rtOpts->noAdjust){
WARNING(" Clock step blocked because of option -t\n");
return;
}
- TimeInternal timeTmp;
-
- getTime(&timeTmp);
- subTime(&timeTmp, &timeTmp, &ptpClock->offsetFromMaster);
+ WARNING("performing hard frequency reset, by setting frequency to zero\n");
+ /* In our driver, stepping the time also has the effect of setting the frequency */
+ /* adjustment to zero. */
+ adjTimeOffset(offset, rtOpts->time_mode, ptpClock);
+ ptpClock->observed_drift = 0.0;
- WARNING(" Performing hard frequency reset, by setting frequency to zero\n");
- adjFreq(0);
- ptpClock->observed_drift = 0;
-
- setTime(&timeTmp);
initClock(rtOpts, ptpClock);
- toState(PTP_FAULTY, rtOpts, ptpClock); /* make a full protocol reset */
}
-void warn_operator_fast_slewing(RunTimeOpts * rtOpts, PtpClock * ptpClock, Integer32 adj)
+void warn_operator_fast_slewing(RunTimeOpts * rtOpts, PtpClock * ptpClock, LongDouble adj)
{
- if(ptpClock->warned_operator_fast_slewing == 0){
- if ((adj >= ADJ_FREQ_MAX) || ((adj <= -ADJ_FREQ_MAX))){
+ LongDouble max_adj;
+ if ((rtOpts->time_mode == TIME_NIC) || (rtOpts->time_mode == TIME_BOTH))
+ max_adj = ADJ_FREQ_MAX_NIC;
+ else
+ max_adj = ADJ_FREQ_MAX_SYSTEM;
+
+ if ((adj >= max_adj) || (adj <= -max_adj)) {
+ if(ptpClock->warned_operator_fast_slewing == 0) {
ptpClock->warned_operator_fast_slewing = 1;
NOTICE("Servo: Going to slew the clock with the maximum frequency adjustment\n");
}
+ } else {
+ ptpClock->warned_operator_fast_slewing = 0;
}
}
@@ -436,9 +481,10 @@
/* rule of thumb: at tick rate 10000, slewing at the maximum speed took 0.5ms per second */
float estimated = (((abs(ptpClock->offsetFromMaster.seconds)) + 0.0) * 2.0 * 1000.0 / 3600.0);
-
- ALERT("Servo: %d seconds offset detected, will take %.1f hours to slew\n",
+ // we don't want to arrive early 1s in an expiration opening, so all consoles get a message when the time is 1s off.
+ ALERT("Servo: %d.%09d seconds offset detected, will take %.1f hours to slew\n",
ptpClock->offsetFromMaster.seconds,
+ ptpClock->offsetFromMaster.nanoseconds,
estimated
);
@@ -447,22 +493,25 @@
/*
- * this is a wrapper around adjFreq to abstract extra operations
+ * this is a wrapper around adjTime to abstract extra operations
*/
-void adjFreq_wrapper(RunTimeOpts * rtOpts, PtpClock * ptpClock, Integer32 adj)
+void adjTime_wrapper(RunTimeOpts * rtOpts, PtpClock * ptpClock, LongDouble adj)
{
if (rtOpts->noAdjust){
- DBGV("adjFreq2: noAdjust on, returning\n");
+ DBGV("adjTime_wrapper: noAdjust on, returning\n");
return;
}
+ // go back to only sending delayreq after first sync
+ // otherwise we lose the first sync always
+ // test all combinations
// call original adjtime
- DBG2(" adjFreq2: call adjfreq to %d us \n", adj / DBG_UNIT);
- adjFreq(adj);
+ DBG2(" adjTime_wrapper: call adjTime to %d \n", adj);
+ adjTime(adj, rtOpts->time_mode, ptpClock);
warn_operator_fast_slewing(rtOpts, ptpClock, adj);
@@ -475,8 +524,11 @@
void
updateClock(RunTimeOpts * rtOpts, PtpClock * ptpClock)
{
- Integer32 adj;
- //TimeInternal timeTmp;
+ LongDouble adj;
+
+ /* updates paused, leap second pending - do nothing */
+ if(ptpClock->leapSecondInProgress)
+ return;
DBGV("==> updateClock\n");
@@ -514,26 +566,31 @@
/*
* noAdjust = cannot do any change to clock
* noResetClock = if can change the clock, can we also step it?
+ * resetClockOnlyOnce = can only step clock on first update
*/
if (!rtOpts->noAdjust) {
/* */
- if (!rtOpts->noResetClock) {
+ if (!rtOpts->noResetClock &&
+ (!rtOpts->resetClockStartupOnly || !ptpClock->clock_first_updated)) {
- servo_perform_clock_step(rtOpts, ptpClock);
+ servo_perform_clock_step(rtOpts, ptpClock, &ptpClock->offsetFromMaster);
} else {
/*
* potential problem: "-1.1" is a) -1:0.1 or b) -1:-0.1?
* if this code is right it implies the second case
*/
#if !defined(__APPLE__)
- adj = ptpClock->offsetFromMaster.nanoseconds
- > 0 ? ADJ_FREQ_MAX : -ADJ_FREQ_MAX;
-
- // does this hurt when the clock gets close to zero again?
- ptpClock->observed_drift = adj;
+ LongDouble max_adj;
+ if ((rtOpts->time_mode == TIME_NIC) || (rtOpts->time_mode == TIME_BOTH))
+ max_adj = ADJ_FREQ_MAX_NIC;
+ else
+ max_adj = ADJ_FREQ_MAX_SYSTEM;
+
+ adj = (ptpClock->offsetFromMaster.nanoseconds > 0.0)?
+ max_adj : -max_adj;
warn_operator_slow_slewing(rtOpts, ptpClock);
- adjFreq_wrapper(rtOpts, ptpClock, -adj);
+ adjTime_wrapper(rtOpts, ptpClock, -adj);
/* its not clear how the APPLE case works for large jumps */
#endif /* __APPLE__ */
@@ -541,14 +598,18 @@
}
}
} else {
+ LongDouble max_adj;
/* these variables contain the actual ai and ap to be used below */
Integer32 ap = rtOpts->ap;
Integer32 ai = rtOpts->ai;
+ /* Clear the slow slewing warning so that it will be re-issued if
+ * another large offset occurs */
+ ptpClock->warned_operator_slow_slewing = 0;
+
-
/* the PI controller */
/* Offset from master is less than one second. Use the the PI controller
* to adjust the time
@@ -561,44 +622,48 @@
ai = 1;
+ /*
+ * optional new PI controller, with better accuracy with HW-timestamps:
+ */
/* the accumulator for the I component */
// original PI servo
ptpClock->observed_drift +=
- ptpClock->offsetFromMaster.nanoseconds / ai;
+ (LongDouble)ptpClock->offsetFromMaster.nanoseconds / (LongDouble)ai;
- // ADJ_FREQ_MAX: 512 000
+ if ((rtOpts->time_mode == TIME_NIC) || (rtOpts->time_mode == TIME_BOTH))
+ max_adj = ADJ_FREQ_MAX_NIC;
+ else
+ max_adj = ADJ_FREQ_MAX_SYSTEM;
+
+ /* clamp the accumulator to ADJ_FREQ_MAX for sanity */
+ if (ptpClock->observed_drift > max_adj)
+ ptpClock->observed_drift = max_adj;
+ else if (ptpClock->observed_drift < -max_adj)
+ ptpClock->observed_drift = -max_adj;
- /* clamp the accumulator to ADJ_FREQ_MAX for sanity */
- if (ptpClock->observed_drift > ADJ_FREQ_MAX)
- ptpClock->observed_drift = ADJ_FREQ_MAX;
- else if (ptpClock->observed_drift < -ADJ_FREQ_MAX)
- ptpClock->observed_drift = -ADJ_FREQ_MAX;
-
- adj = ptpClock->offsetFromMaster.nanoseconds / ap +
+ adj = (LongDouble)ptpClock->offsetFromMaster.nanoseconds / (LongDouble)ap +
ptpClock->observed_drift;
- DBG(" Observed_drift with AI component: %d\n",
- ptpClock->observed_drift );
+ DBG(" Observed_drift with AI component: %Lf\n",
+ ptpClock->observed_drift);
- DBG(" After PI: Adj: %d Drift: %d OFM %d\n",
+ DBG(" After PI: Adj: %Lf Drift: %Lf OFM %d\n",
adj, ptpClock->observed_drift , ptpClock->offsetFromMaster.nanoseconds);
-
- DBG2(" Observed_drift with AI component: %d\n",
- ptpClock->observed_drift / DBG_UNIT );
-
- DBG2(" After PI: Adj: %d Drift: %d OFM %d\n",
- adj, ptpClock->observed_drift / DBG_UNIT, ptpClock->offsetFromMaster.nanoseconds / DBG_UNIT);
-
#if defined(__APPLE__)
- adjTime(ptpClock->offsetFromMaster.nanoseconds);
+ adjTime((LongDouble)ptpClock->offsetFromMaster.nanoseconds);
#else
- adjFreq_wrapper(rtOpts, ptpClock, -adj);
+ adjTime_wrapper(rtOpts, ptpClock, -adj);
#endif /* __APPLE__ */
}
+ /* If we get here, the clock has been updated. Used to enable syncing of
+ * second servo in time both mode.
+ */
+ ptpClock->clock_first_updated = TRUE;
display:
@@ -625,6 +690,6 @@
DBGV("offset from master: %10ds %11dns\n",
ptpClock->offsetFromMaster.seconds,
ptpClock->offsetFromMaster.nanoseconds);
- DBGV("observed drift: %10d\n", ptpClock->observed_drift);
+ DBGV("observed drift: %Lf\n", ptpClock->observed_drift);
}
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/dep/sfxge_ioctl.h
--- /dev/null Fri Nov 14 16:18:09 2014
+++ b/src/dep/sfxge_ioctl.h Fri Nov 14 12:20:13 2014
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005-2006 Fen Systems Ltd.
+ * Copyright (c) 2006-2012 Solarflare Communications Inc
+ * 9501 Jeronimo Road, Suite 250,
+ * Irvine, CA 92618, USA
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SFXGE_IOCTL_H
+#define SFXGE_IOCTL_H
+
+#include <sys/sockio.h>
+#define __u8 uint8_t
+#define __u16 uint16_t
+#define __u32 uint32_t
+#define __u64 uint64_t
+#define __s8 int8_t
+#define __s16 int16_t
+#define __s32 int32_t
+#define __s64 int64_t
+
+#define SFXGE_IOC ('S' << 24 | 'F' << 16 | 'C' << 8)
+
+#define SFXGE_TS_INIT (SFXGE_IOC | 0x12)
+#define SFXGE_TS_READ (SFXGE_IOC | 0x13)
+#define SFXGE_TS_SETTIME (SFXGE_IOC | 0x14)
+#define SFXGE_TS_ADJTIME (SFXGE_IOC | 0x15)
+#define SFXGE_TS_SYNC (SFXGE_IOC | 0x16)
+
+#define SFXGE_DRV_VERSION1 1
+
+/* PTP support for NIC time disciplining ************************************/
+
+struct sfxge_timespec {
+ int64_t tv_sec;
+ int32_t tv_nsec;
+};
+
+/* Initialise timestamping, like SIOCHWTSTAMP *******************************/
+
+enum {
+ HWTSTAMP_TX_OFF,
+ HWTSTAMP_TX_ON,
+};
+
+enum {
+ HWTSTAMP_FILTER_NONE,
+ HWTSTAMP_FILTER_ALL,
+ HWTSTAMP_FILTER_SOME,
+ HWTSTAMP_FILTER_PTP_V1_L4_EVENT,
+ HWTSTAMP_FILTER_PTP_V1_L4_SYNC,
+ HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ,
+ HWTSTAMP_FILTER_PTP_V2_L4_EVENT,
+ HWTSTAMP_FILTER_PTP_V2_L4_SYNC,
+ HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ,
+ HWTSTAMP_FILTER_PTP_V2_L2_EVENT,
+ HWTSTAMP_FILTER_PTP_V2_L2_SYNC,
+ HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ,
+ HWTSTAMP_FILTER_PTP_V2_EVENT,
+ HWTSTAMP_FILTER_PTP_V2_SYNC,
+ HWTSTAMP_FILTER_PTP_V2_DELAY_REQ,
+};
+
+struct sfxge_hwtstamp_config {
+ uint32_t drv_version;
+ uint32_t flags;
+ uint32_t tx_type;
+ uint32_t rx_filter;
+};
+
+/* Read any transmit or receive timestamps since the last call **************/
+
+struct sfxge_ts_read {
+ uint32_t tx_valid;
+ struct sfxge_timespec tx_ts;
+ struct sfxge_timespec tx_ts_hw;
+ uint32_t rx_valid;
+ struct sfxge_timespec rx_ts;
+ struct sfxge_timespec rx_ts_hw;
+ uint8_t uuid [6];
+ uint8_t seqid [2];
+};
+
+struct sfxge_ts_settime {
+ struct sfxge_timespec ts; /* In and out */
+ uint32_t iswrite; /* 1 == write, 0 == read (only) */
+};
+
+struct sfxge_ts_adjtime {
+ int64_t adjustment; /* Parts per billion, In and out */
+ uint32_t iswrite; /* 1 == write, 0 == read (only) */
+};
+
+struct sfxge_ts_sync {
+ struct sfxge_timespec ts;
+};
+
+union sfxge_ioctl_data {
+ struct sfxge_hwtstamp_config ts_init;
+ struct sfxge_ts_read ts_read;
+ struct sfxge_ts_settime ts_settime;
+ struct sfxge_ts_adjtime ts_adjtime;
+ struct sfxge_ts_sync ts_sync;
+};
+
+struct sfxge_sock_ioctl {
+ uint32_t cmd;
+ uint16_t reserved;
+ union sfxge_ioctl_data u;
+};
+#endif /* SFXGE_IOCTL_H */
+
+
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/dep/startup.c
--- a/src/dep/startup.c Tue May 14 17:07:59 2013 -0700
+++ b/src/dep/startup.c Sun Oct 27 22:49:29 2013 -0700
@@ -1,4 +1,6 @@
/*-
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
* Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
* Martin Burnicki, Gael Mace, Alexandre Van Kempen
* Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
@@ -39,6 +41,8 @@
#include "../ptpd.h"
+PtpClock *ptpClock;
+
/*
* valgrind 3.5.0 currently reports no errors (last check: 20110512)
* valgrind 3.4.1 lacks an adjtimex handler
@@ -70,6 +74,10 @@
volatile sig_atomic_t sighup_received = 0;
volatile sig_atomic_t sigusr1_received = 0;
volatile sig_atomic_t sigusr2_received = 0;
+#ifdef DBG_SIGRTMIN_LEAP_SECOND
+volatile sig_atomic_t sigrtmin0_received = 0;
+volatile sig_atomic_t sigrtmin1_received = 0;
+#endif
/*
* Function to catch signals asynchronously.
@@ -79,31 +87,38 @@
*/
void catch_signals(int sig)
{
- switch (sig) {
- case SIGINT:
+ switch (sig) {
+ case SIGINT:
sigint_received = 1;
break;
- case SIGTERM:
+ case SIGTERM:
sigterm_received = 1;
- break;
- case SIGHUP:
- sighup_received = 1;
- break;
- case SIGUSR1:
- sigusr1_received = 1;
- break;
- case SIGUSR2:
- sigusr2_received = 1;
- break;
- default:
- /*
- * TODO: should all other signals be catched, and handled as SIGINT?
- *
- * Reason: currently, all other signals are just uncatched, and the OS kills us.
- * The difference is that we could then close the open files properly.
- */
- break;
- }
+ break;
+ case SIGHUP:
+ sighup_received = 1;
+ break;
+ case SIGUSR1:
+ sigusr1_received = 1;
+ break;
+ case SIGUSR2:
+ sigusr2_received = 1;
+ break;
+ default:
+#ifdef DBG_SIGRTMIN_LEAP_SECOND
+ if (sig == SIGRTMIN + 0)
+ sigrtmin0_received = 1;
+ else if (sig == SIGRTMIN + 1)
+ sigrtmin1_received = 1;
+ else
+#endif
+ /*
+ * TODO: should all other signals be catched, and handled as SIGINT?
+ *
+ * Reason: currently, all other signals are just uncatched, and the OS kills us.
+ * The difference is that we could then close the open files properly.
+ */
+ break;
+ }
}
@@ -119,12 +134,12 @@
exit(0);
}
-/**
+/**
* Signal handler for HUP which tells us to swap the log file.
*
* @param sig
*/
-void
+void
do_signal_sighup(RunTimeOpts * rtOpts)
{
if(rtOpts->do_record_quality_file)
@@ -135,7 +150,7 @@
if(!logToFile(rtOpts))
NOTIFY("SIGHUP logToFile failed\n");
- NOTIFY("I've been SIGHUP'd\n");
+ NOTIFY("I've been SIGHUP'd\n");
}
@@ -146,10 +161,10 @@
void
check_signals(RunTimeOpts * rtOpts, PtpClock * ptpClock)
{
- /*
- * note:
- * alarm signals are handled in a similar way in dep/timer.c
- */
+ /*
+ * note:
+ * alarm signals are handled in a similar way in dep/timer.c
+ */
if(sigint_received || sigterm_received){
do_signal_close(ptpClock);
@@ -158,42 +173,80 @@
if(sighup_received){
do_signal_sighup(rtOpts);
sighup_received=0;
+ }
+
+ if(sigusr1_received){
+ WARNING("SigUSR1 received, manually stepping clock to current known OFM\n");
+ /* If the time mode is time both then step the NIC clock and system clock.
+ * Do the time both clock first because this requires the clock to be stepped
+ * by the sum of the two servo's current offset and the offset is cleared by
+ * the clock step process */
+ if (rtOpts->time_mode == TIME_BOTH) {
+ TimeInternal offset;
+ extern PtpClock *G_timeBothClock;
+ extern RunTimeOpts *G_timeBothRtOpts;
+ addTime(&offset, &ptpClock->offsetFromMaster, &G_timeBothClock->offsetFromMaster);
+ servo_perform_clock_step(G_timeBothRtOpts, G_timeBothClock, &offset);
+ }
+ servo_perform_clock_step(rtOpts, ptpClock, &ptpClock->offsetFromMaster);
+ sigusr1_received = 0;
+ }
+
+
+#ifdef DBG_SIGUSR2_CHANGE_DOMAIN
+ if(sigusr2_received){
+ /* swap domain numbers */
+ static int prev_domain;
+ static int first_time = 1;
+ if(first_time){
+ first_time = 0;
+ prev_domain = ptpClock->domainNumber + 1;
+ }
+
+ int temp = ptpClock->domainNumber;
+ ptpClock->domainNumber = prev_domain;
+ prev_domain = temp;
+
+
+ // propagate new choice as the run-time option
+ rtOpts->domainNumber = ptpClock->domainNumber;
+
+ WARNING("SigUSR2 received. PTP_Domain is now %d (saved: %d)\n",
+ ptpClock->domainNumber,
+ prev_domain
+ );
+ sigusr2_received = 0;
+ }
+#endif
+
+
+#ifdef DBG_SIGRTMIN_LEAP_SECOND
+ if (sigrtmin0_received && (rtOpts->master_slave_mode != PTP_MODE_SLAVE)) {
+ ptpClock->leap61 = TRUE;
+ ptpClock->leap59 = FALSE;
+ timerStop(LEAP_SECOND_NOW_TIMER, ptpClock->itimer);
+ timerStart(LEAP_SECOND_NOW_TIMER,
+ secondsToMidnight() + (float)ptpClock->currentUtcOffset,
+ ptpClock->itimer);
+
+ INFO("Signalling leap second 61 at end of current day\n");
+ sigrtmin0_received = 0;
}
- if(sigusr1_received){
- WARNING("SigUSR1 received, manually stepping clock to current known OFM\n");
- servo_perform_clock_step(rtOpts, ptpClock);
- sigusr1_received = 0;
- }
+ if (sigrtmin1_received && (rtOpts->master_slave_mode != PTP_MODE_SLAVE)) {
+ ptpClock->leap59 = TRUE;
+ ptpClock->leap61 = FALSE;
+
+ timerStop(LEAP_SECOND_NOW_TIMER, ptpClock->itimer);
+ timerStart(LEAP_SECOND_NOW_TIMER,
+ secondsToMidnight() + (float)ptpClock->currentUtcOffset - 1.0,
+ ptpClock->itimer);
-
-#ifdef DBG_SIGUSR2_CHANGE_DOMAIN
- if(sigusr2_received){
- /* swap domain numbers */
- static int prev_domain;
- static int first_time = 1;
- if(first_time){
- first_time = 0;
- prev_domain = ptpClock->domainNumber + 1;
- }
-
- int temp = ptpClock->domainNumber;
- ptpClock->domainNumber = prev_domain;
- prev_domain = temp;
-
-
- // propagate new choice as the run-time option
- rtOpts->domainNumber = ptpClock->domainNumber;
-
- WARNING("SigUSR2 received. PTP_Domain is now %d (saved: %d)\n",
- ptpClock->domainNumber,
- prev_domain
- );
- sigusr2_received = 0;
+ INFO("Signalling leap second 59 at end of current day\n");
+ sigrtmin1_received = 0;
}
#endif
-
#ifdef DBG_SIGUSR2_CHANGE_DEBUG
#ifdef RUNTIME_DEBUG
if(sigusr2_received){
@@ -242,8 +295,8 @@
#include <sys/stat.h>
#include <libgen.h>
-#define LOCKFILE "/var/run/kernel_clock"
-#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
+#define LOCKFILE "/system/volatile/ptpd.pid"
+#define LOCKMODE (S_IRUSR|S_IWUSR)
int global_lock_fd;
@@ -261,6 +314,31 @@
return(fcntl(fd, F_SETLK, &fl));
}
+int
+get_owner_lockfile(int fd)
+{
+ struct flock fl;
+
+ fl.l_type = F_WRLCK;
+ fl.l_start = 0;
+ fl.l_whence = SEEK_SET;
+ fl.l_len = 0;
+ if (fcntl(fd, F_GETLK, &fl) == 0) {
+ return(fl.l_pid);
+ }
+ return 0;
+}
+
+int
+unlockfile(int fd) {
+ struct flock fl;
+
+ fl.l_type = F_UNLCK;
+ fl.l_start = 0;
+ fl.l_whence = SEEK_SET;
+ fl.l_len = 0;
+ return(fcntl(fd, F_SETLK, &fl));
+}
/*
* a discussion where the lock file should reside: http://rute.2038bug.com/node38.html.gz#SECTION003859000000000000000
@@ -268,33 +346,37 @@
int
daemon_already_running(void)
{
- char buf[16];
+ char buf[16];
+ int lpid;
INFO(" Info: Going to check lock %s\n", LOCKFILE);
- global_lock_fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE);
- if (global_lock_fd < 0) {
- syslog(LOG_ERR, "can't open %s: %s", LOCKFILE, strerror(errno));
- PERROR("can't open %s: %s", LOCKFILE, strerror(errno));
- exit(1);
- }
- if (lockfile(global_lock_fd) < 0) {
- if (errno == EACCES || errno == EAGAIN) {
- close(global_lock_fd);
- return(1);
- }
- syslog(LOG_ERR, "can't lock %s: %s", LOCKFILE, strerror(errno));
- PERROR("can't lock %s: %s", LOCKFILE, strerror(errno));
- return(1);
- }
- ftruncate(global_lock_fd, 0);
- sprintf(buf, "%ld\n", (long)getpid());
- write(global_lock_fd, buf, strlen(buf)+1);
- return(0);
+ global_lock_fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE);
+ if (global_lock_fd < 0) {
+ syslog(LOG_ERR, "can't open %s: %s", LOCKFILE, strerror(errno));
+ PERROR("can't open %s: %s", LOCKFILE, strerror(errno));
+ //ptpdShutdown(FALSE);
+ exit(1);
+ }
+ if (lockfile(global_lock_fd) < 0) {
+ if (errno == EACCES || errno == EAGAIN) {
+ lpid = get_owner_lockfile(global_lock_fd);
+ syslog(LOG_ERR, "%s locked by %d", LOCKFILE, lpid);
+ PERROR("%s locked by %d", LOCKFILE, lpid);
+ close(global_lock_fd);
+ return(1);
+ }
+ syslog(LOG_ERR, "can't lock %s: %s", LOCKFILE, strerror(errno));
+ PERROR("can't lock %s: %s", LOCKFILE, strerror(errno));
+ return(1);
+ }
+ ftruncate(global_lock_fd, 0);
+ sprintf(buf, "%ld\n", (long)getpid());
+ write(global_lock_fd, buf, strlen(buf)+1);
+ return(0);
}
-
int query_shell(char *command, char *answer, int answer_size);
@@ -310,14 +392,17 @@
char command[BUF_SIZE];
char answer[BUF_SIZE];
int matches;
+ int mypid;
- snprintf(command, BUF_SIZE - 1, "pgrep -x %s | wc -l", name);
+ mypid = getpid();
+ snprintf(command, BUF_SIZE - 1, "pgrep -x %s | grep -v %d | wc -l", name, mypid);
if( query_shell(command, answer, BUF_SIZE) < 0){
return -1;
};
sscanf(answer, "%d", &matches);
+
return (matches);
}
@@ -333,32 +418,32 @@
*/
int query_shell(char *command, char *answer, int answer_size)
{
- int status;
- FILE *fp;
+ int status;
+ FILE *fp;
// clear previous answer
answer[0] = '\0';
fp = popen(command, "r");
- if (fp == NULL){
+ if (fp == NULL){
PERROR("can't call %s", command);
- return -1;
- };
+ return -1;
+ };
// get first line of popen
fgets(answer, answer_size - 1, fp);
DBG2("Query_shell: _%s_ -> _%s_\n", command, answer);
- status = pclose(fp);
- if (status == -1) {
- PERROR("can't call pclose() ");
- return -1;
- }
+ status = pclose(fp);
+ if (status == -1) {
+ PERROR("can't call pclose() ");
+ return -1;
+ }
- /* from Man page:
- Use macros described under wait() to inspect `status' in order
- to determine success/failure of command executed by popen() */
+ /* from Man page:
+ Use macros described under wait() to inspect `status' in order
+ to determine success/failure of command executed by popen() */
return 0;
}
@@ -375,38 +460,38 @@
int
check_parallel_daemons(char *name, int expected, int strict, RunTimeOpts * rtOpts)
{
- int matches = pgrep_matches(name);
+ int matches = pgrep_matches(name);
- if(matches == expected){
- if(!expected){
- INFO( " Info: No %s daemons detected in parallel (as expected)\n",
- name );
- } else {
- INFO( " Info: %d %s daemons detected in parallel (as expected)\n",
- matches, name);
- }
- return 1;
+ if(matches == expected){
+ if(!expected){
+ INFO( " Info: No %s daemons detected in parallel (as expected)\n",
+ name );
} else {
- if(strict){
- if(expected == 0){
- ERROR( " Error: %d %s daemon(s) detected in parallel, but we were not expecting any. Exiting.\n",
- matches, name);
- } else {
- ERROR( " Error: %d %s daemon(s) detected in parallel, but we were expecting %d. Exiting.\n",
- matches, name, expected);
- }
- return 0;
- } else {
- if(!expected){
- WARNING(" Warning: %d %s daemon(s) detected in parallel, but we were expected none. Continuing anyway.\n",
- matches, name);
- } else {
- WARNING(" Warning: %d %s daemon(s) detected in parallel, but we were expecting %d. Continuing anyway.\n",
- matches, name, expected);
- }
- return 1;
- }
+ INFO( " Info: %d %s daemons detected in parallel (as expected)\n",
+ matches, name);
}
+ return 1;
+ } else {
+ if(strict){
+ if(expected == 0){
+ ERROR( " Error: %d %s daemon(s) detected in parallel, but we were not expecting any. Exiting.\n",
+ matches, name);
+ } else {
+ ERROR( " Error: %d %s daemon(s) detected in parallel, but we were expecting %d. Exiting.\n",
+ matches, name, expected);
+ }
+ return 0;
+ } else {
+ if(!expected){
+ WARNING(" Warning: %d %s daemon(s) detected in parallel, but we were expected none. Continuing anyway.\n",
+ matches, name);
+ } else {
+ WARNING(" Warning: %d %s daemon(s) detected in parallel, but we were expecting %d. Continuing anyway.\n",
+ matches, name, expected);
+ }
+ return 1;
+ }
+ }
}
@@ -417,28 +502,28 @@
-/**
+/**
* Log output to a file
*
*
* @return True if success, False if failure
*/
-int
+int
logToFile(RunTimeOpts * rtOpts)
{
if(rtOpts->logFd != -1)
close(rtOpts->logFd);
-
- /* We new append the file instead of replacing it. Also use mask 644 instead of 444 */
+
+ /* We new append the file instead of replacing it. Also use mask 644 instead of 444 */
if((rtOpts->logFd = open(rtOpts->file, O_CREAT | O_APPEND | O_RDWR, 0644 )) != -1) {
dup2(rtOpts->logFd, STDOUT_FILENO);
dup2(rtOpts->logFd, STDERR_FILENO);
- }
+ }
return rtOpts->logFd != -1;
}
-/**
+/**
* Record quality data for later correlation
*
*
@@ -451,63 +536,82 @@
fclose(rtOpts->recordFP);
if ((rtOpts->recordFP = fopen(rtOpts->recordFile, "w")) == NULL)
- PERROR("could not open sync recording file");
- else
+ PERROR("could not open sync recording file");
+ else
setlinebuf(rtOpts->recordFP);
return (rtOpts->recordFP != NULL);
}
-void
+void
ptpdShutdown(PtpClock * ptpClock)
{
- netShutdown(&ptpClock->netPath);
+ shutdownTime(ptpClock);
+ netShutdown(&ptpClock->netPath);
-
+
free(ptpClock->foreign);
free(ptpClock);
- /* properly clean lockfile (eventough new deaemons can adquire the lock after we die) */
- close(global_lock_fd);
- unlink(LOCKFILE);
+#if 0
+ /* Don't call free if we are in the context of the signal handler */
+ if (!inSigHandler) {
+ free(ptpClock->foreign);
+ free(ptpClock);
+ }
+#endif
+
+ /* properly clean lockfile (even though new deaemons can acquire the lock after we die) */
+ close(global_lock_fd);
+ unlink(LOCKFILE);
}
void dump_command_line_parameters(int argc, char **argv)
{
//
int i = 0;
- char sbuf[1000];
- char *st = sbuf;
- int len = 0;
-
- *st = '\0';
- for(i=0; i < argc; i++){
- len += snprintf(sbuf + len,
- sizeof(sbuf) - len,
- "%s ", argv[i]);
+ char sbuf[1000];
+ char *st = sbuf;
+ int len = 0;
+
+ *st = '\0';
+ for(i=0; i < argc; i++){
+ if (len >= sizeof(sbuf)) {
+ ERROR("command line too long, %d\n", len);
+ return;
+ } else {
+ len += snprintf(sbuf + len,
+ sizeof(sbuf) - len,
+ "%s ", argv[i]);
+ }
+ }
+
+ INFO("\n");
+ INFO("Starting ptpd daemon with parameters: %s\n", sbuf);
+}
+
+
+void
+display_short_help(char *msg, Boolean error)
+{
+ char *ermsg;
+ if (error) {
+ ermsg = "ERROR";
+ } else {
+ ermsg = "";
}
- INFO("\n");
- INFO("Starting ptpd2 daemon with parameters: %s\n", sbuf);
-}
-
-
-void
-display_short_help(char *error)
-{
printf(
- //"\n"
- "ptpd2:\n"
- " -gGW Protocol mode (slave/master with ntp/master without ntp)\n"
+ "ptpd:\n"
+ " -gW Protocol mode (slave/master)\n"
" -b <dev> Interface to use\n"
"\n"
" -cC -DVfS Console / verbose console; Dump stats / Interval / Output file / no Syslog\n"
" -uU Unicast/Hybrid mode\n"
"\n"
"\n"
- " -hHB Summary / Complete help file / run-time debug level\n"
+ " -?HB Summary / Complete help file / run-time debug level\n"
"\n"
- "ERROR: %s\n\n",
- error
+ "%s %s\n\n", ermsg, msg
);
}
@@ -517,166 +621,188 @@
ptpdStartup(int argc, char **argv, Integer16 * ret, RunTimeOpts * rtOpts)
{
PtpClock * ptpClock;
- int c, noclose = 0;
- int mode_selected = 0; // 1: slave / 2: master, with ntp / 3: master without ntp
+ int c, noclose = 0;
+ int mode_selected = 0; // 1: slave / 2: master, with ntp / 3: master without ntp
- int ntp_daemons_expected = 0;
- int ntp_daemons_strict = 1;
- int ptp_daemons_expected = 0;
- int ptp_daemons_strict = 1;
+ int ntp_daemons_expected = 0;
+ int ntp_daemons_strict = 1;
+ int ptp_daemons_expected = 0;
+ int ptp_daemons_strict = 1;
- dump_command_line_parameters(argc, argv);
+ Boolean time_mode_specified = FALSE;
+ TimeMode default_time_mode;
+ Boolean use_hardware_assist = FALSE;
+ Boolean is_error;
- const char *getopt_string = "HgGWb:cCf:ST:dDPR:xO:tM:a:w:u:Uehzl:o:i:I:n:N:y:m:v:r:s:p:q:Y:BjLV:";
+ dump_command_line_parameters(argc, argv);
+/*
+#ifdef PTPD_FULL_OPTIONS
+ const char *getopt_string = "HcSdDEPf:R:X:B:txjO:M:a:w:b:l:T:u:o:i:n:y:m:v:r:s:p:q:Y:LZgGWK";
+#else
+ const char *getopt_string = "HcSdDEPf:R:X:B:txjM:a:w:b:l:T:o:i:n:y:m:v:r:s:p:q:Y:LZgGWK";
+#endif
+*/
- /* parse command line arguments */
- while ((c = getopt(argc, argv, getopt_string)) != -1) {
- switch (c) {
- case '?':
- printf("\n");
- display_short_help("Please input correct parameters (use -H for complete help file)");
- *ret = 1;
- return 0;
- break;
-
- case 'H':
- printf(
- "\nUsage: ptpv2d [OPTION]\n\n"
- "Ptpv2d runs on UDP/IP , E2E mode by default\n"
+ const char *getopt_string = "?HgGWb:cCf:ST:dDPR:xO:tM:a:w:u:ehzl:o:i:n:N:y:m:v:r:s:p:q:Y:BjLV:XZK";
+
+ /* parse command line arguments */
+ while ((c = getopt(argc, argv, getopt_string)) != -1) {
+ switch (c) {
+ case '?':
+ if (optopt == '?') {
+ is_error = FALSE;
+ *ret = 0;
+ } else {
+ is_error = TRUE;
+ *ret = 1;
+ }
+ printf("\n");
+ display_short_help("Please input correct parameters (use -H for complete help file)", is_error);
+ return 0;
+ break;
+
+ case 'H':
+ printf(
+ "\nUsage: ptpd [OPTION]\n\n"
+ "ptpd runs on UDP/IP , E2E mode by default\n"
+ "\n"
+#define GETOPT_START_OF_OPTIONS
+ "-H show detailed help page\n"
"\n"
-#define GETOPT_START_OF_OPTIONS
- "-H show detailed help page\n"
+ "Mode selection (one option is always required):\n"
+ "-g run as slave only\n"
+ "-W run as a Master _without_ NTP (reverts to client when inactive)\n"
+ "-b NAME bind PTP to network interface NAME\n"
"\n"
- "Mode selection (one option is always required):\n"
- "-g run as slave only\n"
- "-G run as a Master _with_ NTP (implies -t -L)\n"
- "-W run as a Master _without_ NTP (reverts to client when inactive)\n"
- "-b NAME bind PTP to network interface NAME\n"
+ "Options:\n"
+ "-c run in command line (non-daemon) mode (implies -D)\n"
+ "-C verbose non-daemon mode: implies -c -S -D -V 0, disables -f\n"
+ "-f FILE send output to FILE (implies -D)\n"
+ "-S DON'T send messages to syslog\n"
"\n"
- "Options:\n"
- "-c run in command line (non-daemon) mode (implies -D)\n"
- "-C verbose non-daemon mode: implies -c -S -D -V 0, disables -f\n"
- "-f FILE send output to FILE (implies -D)\n"
- "-S DON'T send messages to syslog\n"
+ "-T NUMBER set multicast time to live\n"
+ "-d display stats (per received packet, see also -V)\n"
+ "-D display stats in .csv format (per received packet, see also -V)\n"
+ "-P display each received packet in detail\n"
+ "-R FILE record data about sync packets in a seperate file\n"
"\n"
- "-T NUMBER set multicast time to live\n"
- "-d display stats (per received packet, see also -V)\n"
- "-D display stats in .csv format (per received packet, see also -V)\n"
- "-P display each received packet in detail\n"
- "-R FILE record data about sync packets in a seperate file\n"
+ "-x do not reset the clock if off by more than one second\n"
+ "-O NUMBER do not reset the clock if offset is more than NUMBER nanoseconds\n"
+
+ "-t do not make any changes to the system clock\n"
+ "-M NUMBER do not accept delay values of more than NUMBER nanoseconds\n"
+ "-a 10,1000 specify clock servo Proportional and Integral attenuations\n"
+ "-w NUMBER specify one way delay filter stiffness\n"
"\n"
- "-x do not reset the clock if off by more than one second\n"
- "-O NUMBER do not reset the clock if offset is more than NUMBER nanoseconds\n"
+ "-K use PTP hardware in network interface. Exit if\n"
+ " PTP hardware is not usable or available.\n"
+ "-u ADDRESS Unicast mode: send all messages in unicast to ADDRESS\n"
+ "-U Hybrid mode: send DELAY messages in unicast\n"
+ " This causes all delayReq messages to be sent in unicast to the\n"
+ " IP address of the Master (taken from the last announce received).\n"
+ " For masters, it replyes the delayResp to the IP address of the client\n"
+ " (from the corresponding delayReq message).\n"
+ " All other messages are send in multicast\n"
+ "\n"
+ "-e run in ethernet mode (level2) \n"
+ "-h run in End to End mode \n"
+ "-z run in Peer-delay mode\n"
+ "-l NUMBER,NUMBER specify inbound, outbound latency in nsec.\n"
+ " (Use this to compensate the time it takes to send and recv\n"
+ " messages via sockets)\n"
+ "\n"
+ "-o NUMBER specify current UTC offset\n"
+ "-i NUMBER specify PTP domain number (between 0-3)\n"
+ "-I NUMBER specify Mcast group (between 0-3, emulates PTPv1 group selection)\n"
- "-t do not make any changes to the system clock\n"
- "-M NUMBER do not accept delay values of more than NUMBER nanoseconds\n"
- "-a 10,1000 specify clock servo Proportional and Integral attenuations\n"
- "-w NUMBER specify one way delay filter stiffness\n"
"\n"
- "-u ADDRESS Unicast mode: send all messages in unicast to ADDRESS\n"
- "-U Hybrid mode: send DELAY messages in unicast\n"
- " This causes all delayReq messages to be sent in unicast to the\n"
- " IP address of the Master (taken from the last announce received).\n"
- " For masters, it replyes the delayResp to the IP address of the client\n"
- " (from the corresponding delayReq message).\n"
- " All other messages are send in multicast\n"
+ "-n NUMBER specify announce interval in seconds\n"
+ "-N NUMBER specify announce receipt TO (number of lost announces to timeout)\n"
+
+ "-y NUMBER specify sync interval in seconds\n"
+ "-m NUMBER specify max number of foreign master records\n"
"\n"
- "-e run in ethernet mode (level2) \n"
- "-h run in End to End mode \n"
- "-z run in Peer-delay mode\n"
- "-l NUMBER,NUMBER specify inbound, outbound latency in nsec.\n"
- " (Use this to compensate the time it takes to send and recv\n"
- " messages via sockets)\n"
+ "-v NUMBER Master mode: specify system clock Allen variance\n"
+ "-r NUMBER Master mode: specify system clock accuracy\n"
+ "-s NUMBER Master mode: specify system clock class\n"
+ "-p NUMBER Master mode: specify priority1 attribute\n"
+ "-q NUMBER Master mode: specify priority2 attribute\n"
+ "\n"
+ "\n"
+ "-Y 0[,0] Initial and Master_Overide delayreq intervals\n"
+ " desc: the first 2^ number is the rate the slave sends delayReq\n"
+ " When the first answer is received, the master value is used (unless the\n"
+ " second number was also given)\n"
+ "\n"
+ "-B Enable debug messages (if compiled-in). Multiple invocations to more debug\n"
+ "\n"
+ "Compatibility Options (to restore previous default behaviour):\n"
+ "-j Do not refresh the IGMP Multicast menbership at each protol reset\n"
+ "-L Allow multiple instances (ignore lock and other daemons)\n"
+ "-V 0 Seconds between log messages (0: all messages)\n"
"\n"
- "-o NUMBER specify current UTC offset\n"
- "-i NUMBER specify PTP domain number (between 0-3)\n"
- "-I NUMBER specify Mcast group (between 0-3, emulates PTPv1 group selection)\n"
-
"\n"
- "-n NUMBER specify announce interval in 2^NUMBER sec\n"
- "-N NUMBER specify announce receipt TO (number of lost announces to timeout)\n"
-
- "-y NUMBER specify sync interval in 2^NUMBER sec\n"
- "-m NUMBER specify max number of foreign master records\n"
- "\n"
- "-v NUMBER Master mode: specify system clock Allen variance\n"
- "-r NUMBER Master mode: specify system clock accuracy\n"
- "-s NUMBER Master mode: specify system clock class\n"
- "-p NUMBER Master mode: specify priority1 attribute\n"
- "-q NUMBER Master mode: specify priority2 attribute\n"
- "\n"
- "\n"
- "-Y 0[,0] Initial and Master_Overide delayreq intervals\n"
- " desc: the first 2^ number is the rate the slave sends delayReq\n"
- " When the first answer is received, the master value is used (unless the\n"
- " second number was also given)\n"
- "\n"
- "-B Enable debug messages (if compiled-in). Multiple invocations to more debug\n"
- "\n"
- "Compatibility Options (to restore previous default behaviour):\n"
- "-j Do not refresh the IGMP Multicast menbership at each protol reset\n"
- "-L Allow multiple instances (ignore lock and other daemons)\n"
- "-V 0 Seconds between log messages (0: all messages)\n"
- "\n"
- "\n"
+ "-Z ignore delayReq interval given by Master\n"
#define GETOPT_END_OF_OPTIONS
"\n"
- "Possible internal states:\n"
- " init: INITIALIZING\n"
- " flt: FAULTY\n"
- " lstn_init: LISTENING (first time)\n"
- " lstn_reset: LISTENING (non first time)\n"
- " pass: INACTIVE Master\n"
- " uncl: UNCALIBRATED\n"
- " slv: SLAVE\n"
- " pmst: PRE Master\n"
- " mst: ACTIVE Master\n"
- " dsbl: DISABLED\n"
- " ?: UNKNOWN state\n"
+ "Possible internal states:\n"
+ " init: INITIALIZING\n"
+ " flt: FAULTY\n"
+ " lstn_init: LISTENING (first time)\n"
+ " lstn_reset: LISTENING (non first time)\n"
+ " pass: INACTIVE Master\n"
+ " uncl: UNCALIBRATED\n"
+ " slv: SLAVE\n"
+ " pmst: PRE Master\n"
+ " mst: ACTIVE Master\n"
+ " dsbl: DISABLED\n"
+ " ?: UNKNOWN state\n"
- "\n"
+ "\n"
- "Signals synchronous behaviour:\n"
- " SIGHUP Re-open statistics log (specified with -f)\n"
- " SIGUSR1 Manually step clock to current OFM value (overides -x, but honors -t)\n"
- " SIGUSR2 swap domain between current and current + 1 (useful for testing)\n"
- " SIGUSR2 cycle run-time debug level (requires RUNTIME_DEBUG)\n"
- "\n"
- " SIGINT|TERM close file, remove lock file, and clean exit\n"
- " SIGKILL|STOP force an unclean exit\n"
-
- "\n"
- "BMC Algorithm defaults:\n"
- " Software: P1(128) > Class(13|248) > Accuracy(\"unk\"/0xFE) > Variance(61536) > P2(128)\n"
+ "Signals synchronous behaviour:\n"
+ " SIGHUP Re-open statistics log (specified with -f)\n"
+ " SIGUSR1 Manually step clock to current OFM value (overides -x, but honors -t)\n"
+ " SIGUSR2 swap domain between current and current + 1 (useful for testing)\n"
+ " SIGUSR2 cycle run-time debug level (requires RUNTIME_DEBUG)\n"
+#ifdef DBG_SIGRTMIN_LEAP_SECOND
+ " SIGRTMIN Master: signal a leap second 61 at the end of the current UTC day\n"
+ " SIGRTMIN+1 Master- signal a leap second 59 at the end of the current UTC day\n"
+#endif
+ "\n"
+ " SIGINT|TERM close file, remove lock file, and clean exit\n"
+ " SIGKILL|STOP force an unclean exit\n"
-
- /* "-k NUMBER,NUMBER send a management message of key, record, then exit\n" implemented later.. */
- "\n"
- );
- *ret = 1;
- return 0;
- break;
+ "\n"
+ "BMC Algorithm defaults:\n"
+ " Software: P1(248) > Class(13|248) > Accuracy(\"unk\"/0xFE) > Variance(61536) > P2(0)\n\nptpd version %s\n",
- case 'c':
- rtOpts->nonDaemon = 1;
- break;
+ PTP_VERSION_STRING
+ );
+ *ret = 0;
+ return 0;
+ break;
+
+ case 'c':
+ rtOpts->nonDaemon = 1;
+ break;
- case 'C':
- rtOpts->nonDaemon = 2;
+ case 'C':
+ rtOpts->nonDaemon = 2;
- rtOpts->useSysLog = FALSE;
- rtOpts->syslog_startup_messages_also_to_stdout = TRUE;
- rtOpts->displayStats = TRUE;
- rtOpts->csvStats = TRUE;
- rtOpts->log_seconds_between_message = 0;
- rtOpts->do_log_to_file = FALSE;
- break;
-
- case 'S':
- rtOpts->useSysLog = FALSE;
- break;
+ rtOpts->useSysLog = FALSE;
+ rtOpts->syslog_startup_messages_also_to_stdout = TRUE;
+ rtOpts->displayStats = TRUE;
+ rtOpts->csvStats = TRUE;
+ rtOpts->log_seconds_between_message = 0;
+ rtOpts->do_log_to_file = FALSE;
+ break;
+
+ case 'S':
+ rtOpts->useSysLog = FALSE;
+ break;
case 'T':
rtOpts->ttl = atoi(optarg);
break;
@@ -697,74 +823,101 @@
#endif
break;
- case 'd':
- rtOpts->displayStats = TRUE;
- break;
- case 'D':
- rtOpts->displayStats = TRUE;
- rtOpts->csvStats = TRUE;
- break;
- case 'P':
- rtOpts->displayPackets = TRUE;
- break;
+ case 'd':
+ rtOpts->displayStats = TRUE;
+ break;
+ case 'D':
+ rtOpts->displayStats = TRUE;
+ rtOpts->verboseStats = TRUE;
+ rtOpts->csvStats = TRUE;
+ break;
+ case 'P':
+ rtOpts->displayPackets = TRUE;
+ break;
- case 'R':
- /* this option handling is now after the arguments processing (to show the help in case of -?) */
- strncpy(rtOpts->recordFile, optarg, PATH_MAX);
- rtOpts->do_record_quality_file = TRUE;
- break;
+ case 'R':
+ /* this option handling is now after the arguments processing (to show the help in case of -?) */
+ strncpy(rtOpts->recordFile, optarg, PATH_MAX);
+ rtOpts->do_record_quality_file = TRUE;
+ break;
+ case 'X':
+ if(!strcasecmp(optarg, "nic"))
+ {
+ rtOpts->time_mode = TIME_NIC;
+ }
+ else if(!strcasecmp(optarg, "system"))
+ {
+ rtOpts->time_mode = TIME_SYSTEM;
+ }
+ else if(!strcasecmp(optarg, "both"))
+ {
+ rtOpts->time_mode = TIME_BOTH;
+ }
+ else if(!strcasecmp(optarg, "linux_hw"))
+ {
+ rtOpts->time_mode = TIME_SYSTEM_LINUX_HW;
+ }
+ else
+ {
+ ERROR("unsupported -z clock '%s'.\n", optarg);
+ *ret = 1;
+ }
+ /*
+ * Set a flag to control whether to indicate that we don't need
+ * to set the default time mode.
+ */
+ time_mode_specified = TRUE;
+ break;
+ case 'x':
+ rtOpts->noResetClock = TRUE;
+ break;
+ case 'O':
+ rtOpts->maxReset = atoi(optarg);
+ if (rtOpts->maxReset > 1000000000) {
+ ERROR("Use -x to prevent jumps of more"
+ " than one second.");
+ *ret = 1;
+ return (0);
+ }
+ break;
+ case 'M':
+ {
+ long double max_delay = strtod(optarg, NULL);
+ rtOpts->maxDelay.seconds = (Integer32)floor(max_delay);
+ rtOpts->maxDelay.nanoseconds =
+ (Integer32)round((max_delay - rtOpts->maxDelay.seconds) * 1000000000.0);
+ break;
+ }
+ break;
+ case 't':
+ rtOpts->noAdjust = TRUE;
+ break;
+ case 'a':
+ rtOpts->ap = strtol(optarg, &optarg, 0);
+ if (optarg[0])
+ rtOpts->ai = strtol(optarg + 1, 0, 0);
+ break;
+ case 'w':
+ rtOpts->s = strtol(optarg, &optarg, 0);
+ break;
+ case 'b':
+ memset(rtOpts->ifaceName, 0, IFACE_NAME_LENGTH);
+ strncpy(rtOpts->ifaceName, optarg, IFACE_NAME_LENGTH);
+ break;
+ case 'u':
+ rtOpts->do_unicast_mode = 1;
+ strncpy(rtOpts->unicastAddress, optarg,
+ MAXHOSTNAMELEN);
- case 'x':
- rtOpts->noResetClock = TRUE;
- break;
- case 'O':
- rtOpts->maxReset = atoi(optarg);
- if (rtOpts->maxReset > 1000000000) {
- ERROR("Use -x to prevent jumps of more"
- " than one second.");
- *ret = 1;
- return (0);
- }
- break;
- case 'M':
- rtOpts->maxDelay = atoi(optarg);
- if (rtOpts->maxDelay > 1000000000) {
- ERROR("Use -x to prevent jumps of more"
- " than one second.");
- *ret = 1;
- return (0);
- }
- break;
- case 't':
- rtOpts->noAdjust = TRUE;
- break;
- case 'a':
- rtOpts->ap = strtol(optarg, &optarg, 0);
- if (optarg[0])
- rtOpts->ai = strtol(optarg + 1, 0, 0);
- break;
- case 'w':
- rtOpts->s = strtol(optarg, &optarg, 0);
- break;
- case 'b':
- memset(rtOpts->ifaceName, 0, IFACE_NAME_LENGTH);
- strncpy(rtOpts->ifaceName, optarg, IFACE_NAME_LENGTH);
- break;
+ /*
+ * FIXME: some code still relies on checking if this variable is filled. Upgrade this to do_unicast_mode
+ *
+ * E.g.: netSendEvent(Octet * buf, UInteger16 length, NetPath * netPath, Integer32 alt_dst)
+ * if(netPath->unicastAddr || alt_dst ) ...
+ */
+ break;
- case 'u':
- rtOpts->do_unicast_mode = 1;
- strncpy(rtOpts->unicastAddress, optarg,
- MAXHOSTNAMELEN);
-
- /*
- * FIXME: some code still relies on checking if this variable is filled. Upgrade this to do_unicast_mode
- *
- * E.g.: netSendEvent(Octet * buf, UInteger16 length, NetPath * netPath, Integer32 alt_dst)
- * if(netPath->unicastAddr || alt_dst ){
- */
- break;
-
case 'U':
#ifdef PTP_EXPERIMENTAL
rtOpts->do_hybrid_mode = 1;
@@ -773,174 +926,244 @@
#endif
break;
-
- case 'l':
- rtOpts->inboundLatency.nanoseconds =
- strtol(optarg, &optarg, 0);
- if (optarg[0])
- rtOpts->outboundLatency.nanoseconds =
- strtol(optarg + 1, 0, 0);
- break;
- case 'o':
- rtOpts->currentUtcOffset = strtol(optarg, &optarg, 0);
- break;
- case 'i':
- rtOpts->domainNumber = strtol(optarg, &optarg, 0);
- break;
- case 'I':
+ case 'l':
+ rtOpts->inboundLatency.nanoseconds =
+ strtol(optarg, &optarg, 0);
+ if (optarg[0])
+ rtOpts->outboundLatency.nanoseconds =
+ strtol(optarg + 1, 0, 0);
+ break;
+ case 'o':
+ rtOpts->currentUtcOffset = strtol(optarg, &optarg, 0);
+ rtOpts->currentUtcOffsetValid = TRUE;
+ break;
+ case 'i':
+ rtOpts->domainNumber = strtol(optarg, &optarg, 0);
+ if (rtOpts->domainNumber > 3) {
+ ERROR(" Please provide an integer between 0 to 3 with -i\n");
+ *ret = 1;
+ return (0);
+ }
+ break;
+
+ case 'I':
#ifdef PTP_EXPERIMENTAL
- rtOpts->mcast_group_Number = strtol(optarg, &optarg, 0);
+ rtOpts->mcast_group_Number = strtol(optarg, &optarg, 0);
+ if (rtOpts->mcast_group_Number > 3) {
+ ERROR(" Please provide an integer between 0 to 3 with -I\n");
+ *ret = 1;
+ return (0);
+ }
#else
- INFO("Multicast group selection not enabled. Please compile with PTP_EXPERIMENTAL\n");
+ INFO("Multicast group selection not enabled. Please compile with PTP_EXPERIMENTAL\n");
#endif
- break;
+ break;
+
-
- case 'y':
- rtOpts->syncInterval = strtol(optarg, 0, 0);
- break;
- case 'n':
- rtOpts->announceInterval = strtol(optarg, 0, 0);
- break;
+ case 'y':
+ rtOpts->syncInterval = strtod(optarg, 0);
+ break;
+ case 'n':
+ rtOpts->announceInterval = strtod(optarg, 0);
+ break;
case 'N':
rtOpts->announceReceiptTimeout = strtol(optarg, 0, 0);
break;
- case 'm':
- rtOpts->max_foreign_records = strtol(optarg, 0, 0);
- if (rtOpts->max_foreign_records < 1)
- rtOpts->max_foreign_records = 1;
- break;
- case 'v':
- rtOpts->clockQuality.offsetScaledLogVariance =
- strtol(optarg, 0, 0);
- break;
- case 'r':
- rtOpts->clockQuality.clockAccuracy =
- strtol(optarg, 0, 0);
- break;
- case 's':
- rtOpts->clockQuality.clockClass = strtol(optarg, 0, 0);
- break;
- case 'p':
- rtOpts->priority1 = strtol(optarg, 0, 0);
- break;
- case 'q':
- rtOpts->priority2 = strtol(optarg, 0, 0);
- break;
- case 'e':
- rtOpts->ethernet_mode = TRUE;
- ERROR("Not implemented yet !");
- return 0;
- break;
- case 'h':
- rtOpts->delayMechanism = E2E;
- break;
+ case 'm':
+ rtOpts->max_foreign_records = strtol(optarg, 0, 0);
+ if (rtOpts->max_foreign_records < 1)
+ rtOpts->max_foreign_records = 1;
+ break;
+ case 'v':
+ rtOpts->clockQuality.offsetScaledLogVariance =
+ strtol(optarg, 0, 0);
+ break;
+ case 'r':
+ rtOpts->clockQuality.clockAccuracy =
+ strtol(optarg, 0, 0);
+ break;
+ case 's':
+ rtOpts->clockQuality.clockClass = strtol(optarg, 0, 0);
+ break;
+ case 'p':
+ rtOpts->priority1 = strtol(optarg, 0, 0);
+ break;
+ case 'q':
+ rtOpts->priority2 = strtol(optarg, 0, 0);
+ break;
+ case 'e':
+ rtOpts->ethernet_mode = TRUE;
+ ERROR("Not implemented yet !");
+ *ret = 3;
+ return 0;
+ break;
+ case 'h':
+ rtOpts->delayMechanism = E2E;
+ break;
- case 'V':
- rtOpts->log_seconds_between_message = strtol(optarg, &optarg, 0);
- break;
+ case 'V':
+ rtOpts->log_seconds_between_message = strtol(optarg, &optarg, 0);
+ break;
- case 'z':
- rtOpts->delayMechanism = P2P;
- break;
+ case 'Z':
+ rtOpts->ignore_delayreq_interval_master = TRUE;
+ break;
- /* mode selection */
- /* slave only */
- case 'g':
- mode_selected = 1;
- rtOpts->slaveOnly = TRUE;
- rtOpts->initial_delayreq = DEFAULT_DELAYREQ_INTERVAL; // allow us to use faster rate at init
+ case 'z':
+ rtOpts->delayMechanism = P2P;
+ break;
- /* we dont expect any parallel deamons, and we are strict about it */
- ntp_daemons_expected = 0;
- ntp_daemons_strict = 1;
- ptp_daemons_expected = 0;
- ptp_daemons_strict = 1;
- break;
-
- /* Master + NTP */
- case 'G':
- mode_selected = 2;
- rtOpts->clockQuality.clockClass = DEFAULT_CLOCK_CLASS__APPLICATION_SPECIFIC_TIME_SOURCE;
- rtOpts->slaveOnly = FALSE;
- rtOpts->noAdjust = TRUE;
+ /* mode selection */
+ /* slave only */
+ case 'g':
+ switch (mode_selected) {
+ case 2 :
+ ERROR(" -G and -g cannot be specified together\n");
+ *ret = 3;
+ return 0;
+ case 3:
+ ERROR(" -W and -g cannot be specified together\n");
+ *ret = 3;
+ return 0;
+ default:
+ break;
+ }
+ mode_selected = 1;
+ rtOpts->slaveOnly = TRUE;
+ rtOpts->master_slave_mode = PTP_MODE_SLAVE;
+ rtOpts->initial_delayreq = DEFAULT_DELAYREQ_INTERVAL; // allow us to use faster rate at init
- /* we expect one ntpd daemon in parallel, but we are not strick about it */
- ntp_daemons_expected = 1;
- ntp_daemons_strict = 0;
- ptp_daemons_expected = 0;
- ptp_daemons_strict = 0;
- break;
+ /* we dont expect any parallel deamons, and we are strict about it */
+ ntp_daemons_expected = 0;
+ ntp_daemons_strict = 1;
+ ptp_daemons_expected = 0;
+ ptp_daemons_strict = 1;
- /*
- * master without NTP (Original Master behaviour):
- * it falls back to slave mode when its inactive master;
- * once it starts being active, it will drift for itself, so in actual terms it always requires NTP to work properly
- */
- case 'W':
- mode_selected = 3;
- rtOpts->slaveOnly = FALSE;
- rtOpts->noAdjust = FALSE;
+ default_time_mode = DEFAULT_SLAVE_TIME_MODE;
+ break;
- /* we don't expect ntpd, but we can run with other ptpv1 deamons */
- ntp_daemons_expected = 0;
- ntp_daemons_strict = 1;
- ptp_daemons_expected = 0;
- ptp_daemons_strict = 0;
- break;
+ /* Master + NTP */
+ case 'G':
+ switch (mode_selected) {
+ case 1 :
+ ERROR(" -G and -g cannot be specified together\n");
+ *ret = 3;
+ return 0;
+ case 3:
+ ERROR(" -G and -W cannot be specified together\n");
+ *ret = 3;
+ return 0;
+ default:
+ break;
+ }
+ mode_selected = 2;
+ rtOpts->master_slave_mode = PTP_MODE_MASTER_WITH_NTP;
+ rtOpts->clockQuality.clockClass = DEFAULT_CLOCK_CLASS__APPLICATION_SPECIFIC_TIME_SOURCE;
+ rtOpts->slaveOnly = FALSE;
+ rtOpts->noAdjust = TRUE;
-
- case 'Y':
- rtOpts->initial_delayreq = strtol(optarg, &optarg, 0);
- rtOpts->subsequent_delayreq = rtOpts->initial_delayreq;
- rtOpts->ignore_delayreq_interval_master = FALSE;
+ /* we expect one ntpd daemon in parallel, but we are not strick about it */
+ ntp_daemons_expected = 1;
+ ntp_daemons_strict = 0;
+ ptp_daemons_expected = 0;
+ ptp_daemons_strict = 0;
- /* Use this to override the master-given DelayReq value */
- if (optarg[0]){
- rtOpts->subsequent_delayreq = strtol(optarg + 1, &optarg, 0);
- rtOpts->ignore_delayreq_interval_master = TRUE;
- }
- break;
+ default_time_mode = DEFAULT_MASTER_TIME_MODE;
+ break;
- case 'L':
- /* enable running multiple ptpd2 daemons */
- rtOpts->ignore_daemon_lock = TRUE;
- break;
- case 'j':
- rtOpts->do_IGMP_refresh = FALSE;
- break;
+ /*
+ * master without NTP (Original Master behaviour):
+ * it falls back to slave mode when its inactive master;
+ * once it starts being active, it will drift for itself, so in actual terms it always requires NTP to work properly
+ */
+ case 'W':
+ switch (mode_selected) {
+ case 1 :
+ ERROR(" -W and -g cannot be specified together\n");
+ *ret = 3;
+ return 0;
+ case 2:
+ ERROR(" -G and -W cannot be specified together\n");
+ *ret = 3;
+ return 0;
+ default:
+ break;
+ }
+ rtOpts->master_slave_mode = PTP_MODE_MASTER_NO_NTP;
+ mode_selected = 3;
+ rtOpts->slaveOnly = FALSE;
+ rtOpts->noAdjust = FALSE;
+
+ /* we don't expect ntpd, but we can run with other ptpv1 deamons */
+ ntp_daemons_expected = 0;
+ ntp_daemons_strict = 1;
+ ptp_daemons_expected = 0;
+ ptp_daemons_strict = 0;
+ default_time_mode = DEFAULT_MASTER_TIME_MODE;
+ break;
+
+ /* Use the NIC hardware; i.e. TIME_BOTH mode */
+ case 'K':
+/*
+#ifdef __sparc__
+ ERROR("HW assist (-K) is not supported on sparc\n");
+ *ret = 3;
+ return 0;
+#endif
+*/
+ use_hardware_assist = TRUE;
+ break;
+
-
+ case 'Y':
+ rtOpts->initial_delayreq = strtod(optarg, 0);
+ rtOpts->subsequent_delayreq = rtOpts->initial_delayreq;
+ rtOpts->ignore_delayreq_interval_master = FALSE;
- default:
- ERROR("Unknown parameter %c \n", c);
- *ret = 1;
- return 0;
- }
+ /* Use this to override the master-given DelayReq value */
+ if (optarg[0]){
+ rtOpts->subsequent_delayreq = strtol(optarg + 1, &optarg, 0);
+ rtOpts->ignore_delayreq_interval_master = TRUE;
}
+ break;
+ case 'L':
+ /* enable running multiple ptpd2 daemons */
+ rtOpts->ignore_daemon_lock = TRUE;
+ break;
+ case 'j':
+ rtOpts->do_IGMP_refresh = FALSE;
+ break;
- /*
- * we try to catch as many error conditions as possible, but before we call daemon().
- * the exception is the lock file, as we get a new pid when we call daemon(),
- * so this is checked twice: once to read, second to read/write
- */
- if(geteuid() != 0)
- {
- display_short_help("Please run this daemon as root");
- *ret = 1;
- return 0;
- }
+ default:
+ ERROR("Unknown parameter %c \n", c);
+ *ret = 1;
+ return 0;
+ }
+ }
- if(!mode_selected){
- display_short_help("Please select program mode");
- *ret = 1;
- return 0;
- }
+
+ /*
+ * we try to catch as many error conditions as possible, but before we call daemon().
+ * the exception is the lock file, as we get a new pid when we call daemon(),
+ * so this is checked twice: once to read, second to read/write
+ */
+ if(geteuid() != 0)
+ {
+ display_short_help("Please run this daemon as root", TRUE);
+ *ret = 1;
+ return 0;
+ }
+
+ if(rtOpts->master_slave_mode == PTP_MODE_NULL) {
+ display_short_help("Please select program mode", TRUE);
+ *ret = 1;
+ return 0;
+ }
#ifdef PTP_EXPERIMENTAL
if(rtOpts->do_unicast_mode && rtOpts->do_hybrid_mode){
@@ -950,42 +1173,47 @@
}
#endif
+ /* If the time mode has not been specified then use the default time mode */
+ if (time_mode_specified == FALSE) {
+ if (use_hardware_assist) {
+ default_time_mode = TIME_BOTH;
+ }
+ rtOpts->time_mode = default_time_mode;
+ }
+ ptpClock = (PtpClock *) calloc(1, sizeof(PtpClock));
+ if (!ptpClock) {
+ PERROR("failed to allocate memory for protocol engine data");
+ *ret = 2;
+ return 0;
+ } else {
+ DBG("allocated %d bytes for protocol engine data\n",
+ (int)sizeof(PtpClock));
+ ptpClock->foreign = (ForeignMasterRecord *)
+ calloc(rtOpts->max_foreign_records,
+ sizeof(ForeignMasterRecord));
+ if (!ptpClock->foreign) {
+ PERROR("failed to allocate memory for foreign "
+ "master data");
+ *ret = 2;
+ free(ptpClock);
+ return 0;
+ } else {
+ DBG("allocated %d bytes for foreign master data\n",
+ (int)(rtOpts->max_foreign_records *
+ sizeof(ForeignMasterRecord)));
+ }
+ }
+ /* Init to 0 net buffer */
+ memset(ptpClock->msgIbuf, 0, PACKET_SIZE);
+ memset(ptpClock->msgObuf, 0, PACKET_SIZE);
+ /* Initialise sockets to invalid */
+ ptpClock->netPath.eventSock = -1;
+ ptpClock->netPath.generalSock = -1;
-
- ptpClock = (PtpClock *) calloc(1, sizeof(PtpClock));
- if (!ptpClock) {
- PERROR("failed to allocate memory for protocol engine data");
- *ret = 2;
- return 0;
- } else {
- DBG("allocated %d bytes for protocol engine data\n",
- (int)sizeof(PtpClock));
- ptpClock->foreign = (ForeignMasterRecord *)
- calloc(rtOpts->max_foreign_records,
- sizeof(ForeignMasterRecord));
- if (!ptpClock->foreign) {
- PERROR("failed to allocate memory for foreign "
- "master data");
- *ret = 2;
- free(ptpClock);
- return 0;
- } else {
- DBG("allocated %d bytes for foreign master data\n",
- (int)(rtOpts->max_foreign_records *
- sizeof(ForeignMasterRecord)));
- }
- }
-
- /* Init to 0 net buffer */
- memset(ptpClock->msgIbuf, 0, PACKET_SIZE);
- memset(ptpClock->msgObuf, 0, PACKET_SIZE);
-
-
-
/*
* This section discusses some of the complexities of doing proper Locking and Background Daemon programs.
*
@@ -997,7 +1225,7 @@
*
* The correct way is to lock the shared resource that needs write protection: the kernel clock itself.
* (http://mywiki.wooledge.org/ProcessManagement#How_do_I_make_sure_only_one_copy_of_my_script_can_run_at_a_time.3F)
- * Thus, ptpd2 will lock the file /var/run/kernel_clock in daemon_already_running().
+ * Thus, ptpd2 will lock the file LOCKFILE in daemon_already_running().
* Unfortunately this is not followed by the other discipliners, so on top of locking we also try to find
* them by name (we spawn pgrep in check_parallel_daemons()).
* Another alternative would be to clone NTP's way of doing this: to bind to port 123, the NTP port. Although this
@@ -1024,10 +1252,13 @@
if(rtOpts->ignore_daemon_lock == 0){
/* check and create Lock */
if(daemon_already_running()){
- ERROR(" Error: Multiple ptpd2 instances detected (-L to ignore lock file %s)\n", LOCKFILE);
+ ERROR(" Error: Multiple ptpd instances detected (-L to ignore lock file %s)\n", LOCKFILE);
*ret = 3;
+ free(ptpClock);
return 0;
}
+ /* unlock the file so that child process can lock it */
+ (void) unlockfile(global_lock_fd);
} else {
/* if we ignore the daemon lock, we also are not strict for parallel daemons (but we always syslog what is happening) */
ptp_daemons_strict=0;
@@ -1035,86 +1266,92 @@
}
- if(check_parallel_daemons("ptpd", ptp_daemons_expected, ptp_daemons_strict, rtOpts) &&
- check_parallel_daemons("ntpd", ntp_daemons_expected, ntp_daemons_strict, rtOpts))
- {
- /* ok */
- } else {
- *ret = 3;
- return 0;
- }
+ if(check_parallel_daemons("ptpd", ptp_daemons_expected, ptp_daemons_strict, rtOpts) &&
+ check_parallel_daemons("ntpd", ntp_daemons_expected, ntp_daemons_strict, rtOpts))
+ {
+ /* ok */
+ } else {
+ *ret = 3;
+ free(ptpClock);
+ return 0;
+ }
+ /* Manage open files: stats and quality file */
+ if(rtOpts->do_record_quality_file){
+ if (recordToFile(rtOpts))
+ noclose = 1;
+ else
+ PERROR("could not open quality file");
+ }
+ if(rtOpts->do_log_to_file){
+ if(logToFile(rtOpts))
+ noclose = 1;
+ else
+ PERROR("could not open output file");
+ rtOpts->displayStats = TRUE;
+ rtOpts->verboseStats = TRUE;
+ rtOpts->csvStats = TRUE;
+ }
- /* Manage open files: stats and quality file */
- if(rtOpts->do_record_quality_file){
- if (recordToFile(rtOpts))
- noclose = 1;
- else
- PERROR("could not open quality file");
- }
- if(rtOpts->do_log_to_file){
- if(logToFile(rtOpts))
- noclose = 1;
- else
- PERROR("could not open output file");
- rtOpts->displayStats = TRUE;
- rtOpts->csvStats = TRUE;
- }
-
-
-
- /* DAEMON */
+ /* DAEMON */
#ifdef PTPD_NO_DAEMON
- if(rtOpts->nonDaemon == 0){
- rtOpts->nonDaemon= 1;
- }
+ if(rtOpts->nonDaemon == 0){
+ rtOpts->nonDaemon= 1;
+ }
#endif
-
- if(rtOpts->nonDaemon == 0){
- /* fork to daemon */
- if (daemon(0, noclose) == -1) {
- PERROR("failed to start as daemon");
- *ret = 3;
- return 0;
- }
+
+ if(rtOpts->nonDaemon == 0){
+ /* fork to daemon */
+ if (daemon(0, noclose) == -1) {
+ PERROR("failed to start as daemon");
+ *ret = 3;
+ free(ptpClock);
+ return 0;
+ }
INFO(" Info: Now running as a daemon\n");
- }
+ }
/* if syslog is on, send all messages to syslog only */
rtOpts->syslog_startup_messages_also_to_stdout = FALSE;
- /* Second lock check, to replace the contents with our own new PID. It seems that F_WRLCK is not inherited to the child, so we lock again */
- if(rtOpts->ignore_daemon_lock == 0){
- /* check and create Lock */
- if(daemon_already_running()){
+ /* Second lock check, to replace the contents with our own new PID. It seems that F_WRLCK is not inherited to the child, so we lock again */
+ if(rtOpts->ignore_daemon_lock == 0){
+ /* check and create Lock */
+ if(daemon_already_running()){
ERROR("Multiple instances of this daemon detected (Use option -L to ignore lock file %s)\n", LOCKFILE);
- *ret = 3;
- return 0;
- }
- }
+ *ret = 3;
+ free(ptpClock);
+ return 0;
+ }
+ }
-
- /* use new synchronous signal handlers */
- signal(SIGINT, catch_signals);
- signal(SIGTERM, catch_signals);
- signal(SIGHUP, catch_signals);
- signal(SIGUSR1, catch_signals);
- signal(SIGUSR2, catch_signals);
+ /* use new syncronous signal handlers */
+ signal(SIGINT, catch_signals);
+ signal(SIGTERM, catch_signals);
+ signal(SIGHUP, catch_signals);
- *ret = 0;
+ signal(SIGUSR1, catch_signals);
+ signal(SIGUSR2, catch_signals);
+
+#ifdef DBG_SIGRTMIN_LEAP_SECOND
+ signal(SIGRTMIN, catch_signals);
+ signal(SIGRTMIN+1, catch_signals);
+#endif
+
+ *ret = 0;
INFO(" Info: Startup finished sucessfully\n");
- return ptpClock;
+ return ptpClock;
}
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/dep/sys.c
--- a/src/dep/sys.c Tue May 14 17:07:59 2013 -0700
+++ b/src/dep/sys.c Sun Oct 27 22:49:29 2013 -0700
@@ -1,4 +1,6 @@
/*-
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
* Copyright (c) 2011 George V. Neville-Neil, Steven Kreuzer,
* Martin Burnicki, Gael Mace, Alexandre Van Kempen,
* National Instruments.
@@ -39,6 +41,8 @@
*
*/
+#include <inttypes.h>
+
#include "../ptpd.h"
#if defined(linux)
@@ -55,6 +59,9 @@
/* only C99 has the round function built-in */
double round (double __x);
+/* TODO Yuk */
+extern PtpClock *G_ptpClock;
+extern PtpClock* G_timeBothClock;
/*
returns a static char * for the representation of time, for debug purposes
@@ -111,9 +118,11 @@
/* always print either a space, or the leading "-". This makes the stat files columns-aligned */
len += snprintf(&s[len], max_len - len, "%c",
isTimeInternalNegative(p)? '-':' ');
+ if (len > max_len) len = max_len;
len += snprintf(&s[len], max_len - len, "%d.%09d",
abs(p->seconds), abs(p->nanoseconds));
+ if (len > max_len) len = max_len;
return len;
}
@@ -170,11 +179,14 @@
int len = 0;
int i;
- if (info)
+ if (info) {
len += snprintf(&s[len], max_len - len, "%s", info);
+ if (len > max_len) len = max_len;
+ }
for (i = 0; ;) {
len += snprintf(&s[len], max_len - len, "%02x", (unsigned char) id[i]);
+ if (len > max_len) len = max_len;
if (++i >= CLOCK_IDENTITY_LENGTH)
break;
@@ -191,19 +203,23 @@
int len = 0;
int i;
- if (info)
+ if (info) {
len += snprintf(&s[len], max_len - len, "%s", info);
+ if (len > max_len) len = max_len;
+ }
for (i = 0; ;) {
/* skip bytes 3 and 4 */
if(!((i==3) || (i==4))){
len += snprintf(&s[len], max_len - len, "%02x", (unsigned char) id[i]);
+ if (len > max_len) len = max_len;
if (++i >= CLOCK_IDENTITY_LENGTH)
break;
/* print a separator after each byte except the last one */
len += snprintf(&s[len], max_len - len, "%s", ":");
+ if (len > max_len) len = max_len;
} else {
i++;
@@ -226,7 +242,7 @@
static struct ether_addr prev_addr;
static char buf[BUF_SIZE];
-#if defined(linux) || defined(__NetBSD__)
+#if defined(linux) || defined(__NetBSD__) || defined(__sun)
if (memcmp(addr->ether_addr_octet, &prev_addr,
sizeof(struct ether_addr )) != 0) {
valid = 0;
@@ -270,7 +286,7 @@
for (i = 0, j = 0; i< CLOCK_IDENTITY_LENGTH ; i++ ){
/* skip bytes 3 and 4 */
if(!((i==3) || (i==4))){
-#if defined(linux) || defined(__NetBSD__)
+#if defined(linux) || defined(__NetBSD__) || defined(__sun)
e.ether_addr_octet[j] = (uint8_t) id[i];
#else // e.g. defined(__FreeBSD__)
e.octet[j] = (uint8_t) id[i];
@@ -282,6 +298,7 @@
/* convert and print hostname */
ether_ntohost_cache(buf, &e);
len += snprintf(&s[len], max_len - len, "(%s)", buf);
+ if (len > max_len) len = max_len;
return len;
}
@@ -292,18 +309,23 @@
{
int len = 0;
- if (info)
+ if (info) {
len += snprintf(&s[len], max_len - len, "%s", info);
+ if (len > max_len) len = max_len;
+ }
#ifdef PRINT_MAC_ADDRESSES
len += snprint_ClockIdentity_mac(&s[len], max_len - len, id->clockIdentity, NULL);
#else
len += snprint_ClockIdentity(&s[len], max_len - len, id->clockIdentity, NULL);
#endif
+ if (len > max_len) len = max_len;
len += snprint_ClockIdentity_ntohost(&s[len], max_len - len, id->clockIdentity, NULL);
+ if (len > max_len) len = max_len;
len += snprintf(&s[len], max_len - len, "/%02x", (unsigned) id->portNumber);
+ if (len > max_len) len = max_len;
return len;
}
@@ -356,7 +378,6 @@
struct timeval now;
extern char *translatePortState(PtpClock *ptpClock);
- extern PtpClock *G_ptpClock;
fprintf(stderr, " (ptpd %-9s ",
priority == LOG_EMERG ? "emergency)" :
@@ -387,7 +408,51 @@
va_end(ap);
}
-void
+/*
+* Dumps a data buffer
+* This either prints the message to syslog, or with timestamp+state to stderr
+* (which has possibly been redirected to a file, using logtofile()/dup2()
+*/
+void
+dump(const char *text, void *addr, int len)
+{
+ uint8_t *address = (uint8_t *)addr;
+ extern RunTimeOpts rtOpts;
+ if(rtOpts.useSysLog) {
+ int i;
+ static Boolean logOpened;
+ if(!logOpened) {
+ openlog("ptpd2", 0, LOG_DAEMON);
+ logOpened = TRUE;
+ }
+
+ syslog(LOG_DEBUG, "%s: length %d, data...\n", text, len);
+ for (i = 0; i < len; i++) {
+ syslog(LOG_DEBUG, "0x%02x ", address[i]);
+ if ((i % 8) == 7) {
+ syslog(LOG_DEBUG, "\n");
+ }
+ }
+ if ((i % 8) != 0) {
+ syslog(LOG_DEBUG, "\n");
+ }
+
+ } else {
+ int i;
+ fprintf(stderr, "%s: length %d, data...\n", text, len);
+ for (i = 0; i < len; i++) {
+ fprintf(stderr, "0x%02x ", address[i]);
+ if ((i % 8) == 7) {
+ fprintf(stderr, "\n");
+ }
+ }
+ if ((i % 8) != 0) {
+ fprintf(stderr, "\n");
+ }
+ }
+}
+
+void
displayStats(RunTimeOpts * rtOpts, PtpClock * ptpClock)
{
static int start = 1;
@@ -402,6 +467,14 @@
return;
}
+ /* In non-verbose mode, don't print a separate line for the other ptp clock
+ * instance used for time both if we've in slave state. Conversely, if in
+ * listening or master state we do want stats, even if this is non-verbose mode
+ */
+ if (!rtOpts->verboseStats && (ptpClock != G_ptpClock) &&
+ (G_ptpClock->portState == PTP_SLAVE)) {
+ return;
+ }
if (start && rtOpts->csvStats) {
@@ -413,7 +486,7 @@
}
memset(sbuf, ' ', sizeof(sbuf));
- getTime(&now);
+ getTime(&now, TIME_SYSTEM, ptpClock);
/*
@@ -440,110 +513,167 @@
strftime(time_str, MAXTIMESTR, "%Y-%m-%d %X", localtime(&time_s));
len += snprintf(sbuf + len, sizeof(sbuf) - len, "%s%s.%06d, %s",
rtOpts->csvStats ? "" : "state: ",
- time_str, (int)now.nanoseconds/1000,
+ time_str, (int)now.nanoseconds/1000,
translatePortState(ptpClock));
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+
+ if (ptpClock->name != NULL) {
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "%s ", ptpClock->name);
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+ }
+
+ if (rtOpts->verboseStats) {
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "%s, ",
+ translatePortState(ptpClock));
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+ }
if (ptpClock->portState == PTP_SLAVE) {
- len += snprint_PortIdentity(sbuf + len, sizeof(sbuf) - len,
- &ptpClock->parentPortIdentity, " ");
+ if (!rtOpts->verboseStats) {
+ int64_t offset = ((int64_t)ptpClock->offsetFromMaster.seconds * 1000000000)
+ + (int64_t)ptpClock->offsetFromMaster.nanoseconds;
- /*
- * if grandmaster ID differs from parent port ID then
- * also print GM ID
- */
- if (memcmp(ptpClock->grandmasterIdentity,
- ptpClock->parentPortIdentity.clockIdentity,
- CLOCK_IDENTITY_LENGTH)) {
- len += snprint_ClockIdentity(sbuf + len,
- sizeof(sbuf) - len,
- ptpClock->grandmasterIdentity,
- " GM:");
- }
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "offset: %"PRIi64" ns", offset);
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
- len += snprintf(sbuf + len, sizeof(sbuf) - len, ", ");
+ if (rtOpts->time_mode == TIME_BOTH) {
+ int64_t offset = ((int64_t)G_timeBothClock->offsetFromMaster.seconds * 1000000000)
+ + (int64_t)G_timeBothClock->offsetFromMaster.nanoseconds;
- if (!rtOpts->csvStats)
- len += snprintf(sbuf + len,
- sizeof(sbuf) - len, "owd: ");
-
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, ", %s offset: %"PRIi64" ns",
+ (G_timeBothClock->name != NULL)? G_timeBothClock->name: "",
+ offset);
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+ }
+ } else {
+ if (!rtOpts->csvStats) {
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "cid: ");
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+ }
+ len += snprint_PortIdentity(sbuf + len, sizeof(sbuf) - len,
+ &ptpClock->parentPortIdentity, " ");
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+
+ /*
+ * if grandmaster ID differs from parent port ID then
+ * also print GM ID
+ */
+ if (memcmp(ptpClock->grandmasterIdentity,
+ ptpClock->parentPortIdentity.clockIdentity,
+ CLOCK_IDENTITY_LENGTH)) {
+ len += snprint_ClockIdentity(sbuf + len,
+ sizeof(sbuf) - len,
+ ptpClock->grandmasterIdentity,
+ " GM:");
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+ }
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, ", ");
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+
+ if (!rtOpts->csvStats) {
+ len += snprintf(sbuf + len,
+ sizeof(sbuf) - len, "owd: ");
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+ }
+
if(rtOpts->delayMechanism == E2E) {
len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
- &ptpClock->meanPathDelay);
+ &ptpClock->meanPathDelay);
} else {
len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
&ptpClock->peerMeanPathDelay);
}
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
len += snprintf(sbuf + len, sizeof(sbuf) - len, ", ");
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
- if (!rtOpts->csvStats)
- len += snprintf(sbuf + len, sizeof(sbuf) - len,
+ if (!rtOpts->csvStats) {
+ len += snprintf(sbuf + len, sizeof(sbuf) - len,
"ofm: ");
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+ }
len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
- &ptpClock->offsetFromMaster);
+ &ptpClock->offsetFromMaster);
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
/* print MS and SM with sign */
len += snprintf(sbuf + len, sizeof(sbuf) - len, ", ");
- if (!rtOpts->csvStats)
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+ if (!rtOpts->csvStats) {
len += snprintf(sbuf + len, sizeof(sbuf) - len,
"stm: ");
-
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+ }
+
len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
&(ptpClock->delaySM));
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
len += snprintf(sbuf + len, sizeof(sbuf) - len, ", ");
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
- if (!rtOpts->csvStats)
+ if (!rtOpts->csvStats) {
len += snprintf(sbuf + len, sizeof(sbuf) - len,
"mts: ");
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+ }
len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
&(ptpClock->delayMS));
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+
+
+ len += sprintf(sbuf + len, ", %s%Lf",
+ rtOpts->csvStats ? "" : "drift: ",
+ ptpClock->observed_drift);
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
- len += sprintf(sbuf + len, ", %s%d",
- rtOpts->csvStats ? "" : "drift: ",
- ptpClock->observed_drift);
-
-
/* Last column has the type of last packet processed by the servo */
len += snprintf(sbuf + len, sizeof(sbuf) - len, ", ");
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
- if (!rtOpts->csvStats)
- len += snprintf(sbuf + len, sizeof(sbuf) - len,
- "last_msg: ");
+ if (!rtOpts->csvStats) {
+ len += snprintf(sbuf + len, sizeof(sbuf) - len,
+ "last_msg: ");
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+ }
- len += snprintf(sbuf + len, sizeof(sbuf) - len,
- "%c ", ptpClock->char_last_msg);
+ len += snprintf(sbuf + len, sizeof(sbuf) - len,
+ "%c ", ptpClock->char_last_msg);
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+ }
+ }
+ else {
+ if ((ptpClock->portState == PTP_MASTER) || (ptpClock->portState == PTP_PASSIVE)) {
- }
- else {
- if ((ptpClock->portState == PTP_MASTER) || (ptpClock->portState == PTP_PASSIVE)) {
+ len += snprint_PortIdentity(sbuf + len, sizeof(sbuf) - len,
+ &ptpClock->parentPortIdentity, " ");
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+ }
- len += snprint_PortIdentity(sbuf + len, sizeof(sbuf) - len,
- &ptpClock->parentPortIdentity, " ");
-
- //len += snprintf(sbuf + len, sizeof(sbuf) - len, ")");
- }
-
- /* show the current reset number on the log */
- if (ptpClock->portState == PTP_LISTENING) {
- len += snprintf(sbuf + len,
- sizeof(sbuf) - len,
- " %d ", ptpClock->reset_count);
- }
- }
+ /* show the current reset number on the log */
+ if (ptpClock->portState == PTP_LISTENING) {
+ len += snprintf(sbuf + len,
+ sizeof(sbuf) - len,
+ " num_resets: %d ", ptpClock->reset_count);
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
+ }
+ }
-
- /* add final \n in normal status lines */
- len += snprintf(sbuf + len, sizeof(sbuf) - len, "\n");
+
+ /* add final \n in normal status lines */
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "\n");
+ if (len > sizeof(sbuf)) len = sizeof(sbuf);
#if 0 /* NOTE: Do we want this? */
if (rtOpts->nonDaemon) {
@@ -551,8 +681,8 @@
len += snprintf(sbuf + len, sizeof(sbuf) - len, "\n");
}
#endif
- write(1, sbuf, rtOpts->csvStats ? len : SCREEN_MAXSZ + 1);
-
+ write(1, sbuf, len);
+
}
@@ -581,6 +711,7 @@
return TRUE;
}
+#if 0 /* Defined in dep/time.c - fetch time from hw */
void
getTime(TimeInternal * time)
{
@@ -600,21 +731,7 @@
time->nanoseconds = tp.tv_nsec;
#endif /* linux || __APPLE__ */
}
-
-void
-setTime(TimeInternal * time)
-{
- struct timeval tv;
-
- tv.tv_sec = time->seconds;
- tv.tv_usec = time->nanoseconds / 1000;
- WARNING("Going to step the system clock to %ds %dns\n",
- time->seconds, time->nanoseconds);
- settimeofday(&tv, 0);
- WARNING("Finished stepping the system clock to %ds %dns\n",
- time->seconds, time->nanoseconds);
-}
-
+#endif
/* returns a double beween 0.0 and 1.0 */
double
@@ -627,44 +744,123 @@
-/*
- * TODO: this function should have been coded in a way to manipulate both the frequency and the tick,
- * to avoid having to call setTime() when the clock is very far away.
- * This would result in situations we would force the kernel clock to run the clock twice as slow,
- * in order to avoid stepping time backwards
- */
#if !defined(__APPLE__)
-Boolean
-adjFreq(Integer32 adj)
+void
+setTimexFlags(int flags, Boolean quiet)
{
- struct timex t;
+ struct timex tmx;
+ int ret;
- memset(&t, 0, sizeof(t));
- if (adj > ADJ_FREQ_MAX){
- adj = ADJ_FREQ_MAX;
- } else if (adj < -ADJ_FREQ_MAX){
- adj = -ADJ_FREQ_MAX;
+ memset(&tmx, 0, sizeof(tmx));
+
+ tmx.modes = MOD_STATUS;
+
+ tmx.status = getTimexFlags();
+ if(tmx.status == -1)
+ return;
+ /* unset all read-only flags */
+ tmx.status &= ~STA_RONLY;
+ tmx.status |= flags;
+
+ ret = adjtimex(&tmx);
+
+ if (ret < 0)
+ PERROR("Could not set adjtimex flags: %s", strerror(errno));
+
+ if(!quiet && ret > 2) {
+ switch (ret) {
+ case TIME_OOP:
+ WARNING("Adjtimex: leap second already in progress\n");
+ break;
+ case TIME_WAIT:
+ WARNING("Adjtimex: leap second already occurred\n");
+ break;
+#if !defined(TIME_BAD)
+ case TIME_ERROR:
+#else
+ case TIME_BAD:
+#endif /* TIME_BAD */
+ default:
+ DBGV("unsetTimexFlags: adjtimex() returned TIME_BAD\n");
+ break;
+ }
+ }
+}
+
+void
+unsetTimexFlags(int flags, Boolean quiet)
+{
+ struct timex tmx;
+ int ret;
+
+ memset(&tmx, 0, sizeof(tmx));
+
+ tmx.modes = MOD_STATUS;
+ tmx.status = getTimexFlags();
+ if(tmx.status == -1)
+ return;
+
+ /* unset all read-only flags */
+ tmx.status &= ~STA_RONLY;
+ tmx.status &= ~flags;
+
+ ret = adjtimex(&tmx);
+
+ if (ret < 0)
+ PERROR("Could not unset adjtimex flags: %s", strerror(errno));
+
+ if(!quiet && ret > 2) {
+ switch (ret) {
+ case TIME_OOP:
+ WARNING("Adjtimex: leap second already in progress\n");
+ break;
+ case TIME_WAIT:
+ WARNING("Adjtimex: leap second already occurred\n");
+ break;
+#if !defined(TIME_BAD)
+ case TIME_ERROR:
+#else
+ case TIME_BAD:
+#endif /* TIME_BAD */
+ default:
+ DBGV("unsetTimexFlags: adjtimex() returned TIME_BAD\n");
+ break;
+ }
+ }
+}
+
+int getTimexFlags(void)
+{
+ struct timex tmx;
+ int ret;
+
+ memset(&tmx, 0, sizeof(tmx));
+
+ tmx.modes = 0;
+ ret = adjtimex(&tmx);
+ if (ret < 0) {
+ PERROR("Could not read adjtimex flags: %s", strerror(errno));
+ return(-1);
+
}
- t.modes = MOD_FREQUENCY;
- t.freq = adj * ((1 << 16) / 1000);
+ return( tmx.status );
+}
- /* do calculation in double precision, instead of Integer32 */
- int t1 = t.freq;
- int t2;
-
- float f = (adj + 0.0) * (((1 << 16) + 0.0) / 1000.0); /* could be float f = adj * 65.536 */
- t2 = t1; // just to avoid compiler warning
- t2 = (int)round(f);
- t.freq = t2;
+Boolean
+checkTimexFlags(int flags) {
- DBG2(" adj is %d; t freq is %d (float: %f Integer32: %d)\n", adj, t.freq, f, t1);
-
- return !adjtimex(&t);
+ int tflags = getTimexFlags();
+ if (tflags == -1)
+ return FALSE;
+ return ((tflags & flags) == flags);
}
#else
+/* @ioctl_timestamping TODO This won't work - not sure whether we can easily support
+* apple and ioctl timestamping */
+
void
adjTime(Integer32 nanoseconds)
{
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/dep/time.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dep/time.c Sun Oct 27 22:49:29 2013 -0700
@@ -0,0 +1,1208 @@
+/*-
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "../ptpd.h"
+#include <stdarg.h>
+#include <math.h>
+
+#ifdef __sun
+#include <unistd.h>
+#include <stropts.h>
+#endif
+
+
+#ifdef linux
+
+#include <asm/types.h>
+#include <linux/errqueue.h>
+
+/* SO_EE_ORIGIN_TIMESTAMPING is defined in linux/errqueue.h in recent kernels.
+ * Define it for compilation with older kernels.
+ */
+#ifndef SO_EE_ORIGIN_TIMESTAMPING
+#define SO_EE_ORIGIN_TIMESTAMPING 4
+#endif
+
+/* SO_TIMESTAMPING is defined in asm/socket.h in recent kernels.
+ * Define it for compilation with older kernels.
+ */
+#ifndef SO_TIMESTAMPING
+#define SO_TIMESTAMPING 37
+#define SCM_TIMESTAMPING SO_TIMESTAMPING
+#endif
+
+/* SIOCSHWTSTAMP is defined in linux/sockios.h in recent kernels.
+ * Define it for compilation with older kernels.
+ */
+#ifndef SIOCSHWTSTAMP
+#define SIOCSHWTSTAMP 0x89b0
+#endif
+
+#endif /*linux*/
+
+/* SO_TIMESTAMPING gets an integer bit field comprised of these values */
+enum {
+ SOF_TIMESTAMPING_TX_HARDWARE = (1<<0),
+ SOF_TIMESTAMPING_TX_SOFTWARE = (1<<1),
+ SOF_TIMESTAMPING_RX_HARDWARE = (1<<2),
+ SOF_TIMESTAMPING_RX_SOFTWARE = (1<<3),
+ SOF_TIMESTAMPING_SOFTWARE = (1<<4),
+ SOF_TIMESTAMPING_SYS_HARDWARE = (1<<5),
+ SOF_TIMESTAMPING_RAW_HARDWARE = (1<<6),
+ SOF_TIMESTAMPING_MASK =
+ (SOF_TIMESTAMPING_RAW_HARDWARE - 1) |
+ SOF_TIMESTAMPING_RAW_HARDWARE
+};
+
+#ifdef __sun
+#include "sfxge_ioctl.h"
+#else
+#include "efx_ioctl.h"
+#endif
+
+/** global state for controlling system time when TIME_BOTH is selected */
+static PtpClock timeBothClock;
+PtpClock *G_timeBothClock = NULL;
+
+/** run time optsions for controlling system time when TIME_BOTH is selected */
+static RunTimeOpts timeBothRtOpts;
+RunTimeOpts *G_timeBothRtOpts = NULL;
+
+/**
+ * Most recent send time stamp from NIC, 0/0 if none available right now.
+ * Reset by getSendTime().
+ */
+static TimeInternal lastSendTime;
+
+#ifndef RECV_ARRAY_SIZE
+/**
+ * Must be large enough to buffer all time stamps received from the NIC
+ * but not yet requested by the protocol processor. Because new information
+ * can only be added when the protocol asks for old one, this should not
+ * get very full.
+ */
+# define RECV_ARRAY_SIZE 10
+#endif
+
+/**
+ * An array of the latest RECV_ARRAY_SIZE packet receive information.
+ * Once it overflows the oldest ones are overwritten in a round-robin
+ * fashion.
+ */
+static struct {
+ TimeInternal recvTimeStamp;
+ UInteger16 sequenceId;
+ ClockIdentity clockId;
+} lastRecvTimes[RECV_ARRAY_SIZE];
+
+/**
+ * Oldest valid and next free entry in lastRecvTimes.
+ * Valid ones are [oldest, free[ if oldest <= free,
+ * otherwise [oldest, RECV_ARRAY_SIZE[ and [0, free[.
+ */
+static int oldestRecv, nextFreeRecv;
+
+int ptp_ioctl(NetPath *netPath, void *drv_ioctl)
+{
+#ifndef __sun
+ netPath.eventSockIFR.ifr_data = drv_ioctl;
+ return(ioctl(netPath->eventSock, SIOCEFX, &netPath->eventSockIFR));
+#else /* __sun */
+ struct strioctl ioc;
+ struct sfxge_sock_ioctl *req = (struct sfxge_sock_ioctl *)drv_ioctl;
+ int len;
+
+ switch(req->cmd) {
+ case SFXGE_TS_INIT:
+ len = sizeof (struct sfxge_hwtstamp_config);
+ DBGV("SFXGE_TS_INIT issued\n");
+ break;
+ case SFXGE_TS_READ:
+ len = sizeof (struct sfxge_ts_read);
+ DBGV("SFXGE_TS_READ issued\n");
+ break;
+ case SFXGE_TS_ADJTIME:
+ len = sizeof (struct sfxge_ts_adjtime);
+ DBGV("SFXGE_TS_ADJTIME issued\n");
+ break;
+ case SFXGE_TS_SETTIME:
+ len = sizeof (struct sfxge_ts_settime);
+ DBGV("SFXGE_TS_SETTIME issued\n");
+ break;
+ case SFXGE_TS_SYNC:
+ len = sizeof (struct sfxge_ts_sync);
+ DBGV("SFXGE_TS_SYNC issued\n");
+ break;
+ default:
+ printf("Unknown ioctl number %x ... exiting\n", req->cmd);
+ exit (1);
+ }
+ (void) memset(&ioc, 0, sizeof (ioc));
+ ioc.ic_cmd = req->cmd;
+ ioc.ic_timout = 0;
+ ioc.ic_len = len;
+ ioc.ic_dp = (char *)&req->u;
+ return (ioctl(netPath->devFd, I_STR, (char *)&ioc));
+#endif /* __sun */
+}
+/**
+ * if TIME_BOTH: measure NIC<->system time offsets and adapt system time
+ *
+ * This function is called whenever init.c gets control; to prevent to
+ * frequent changes it ignores invocations less than one second away from
+ * the previous one.
+ */
+void syncSystemWithNIC(RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ TimeInternal delay;
+ static TimeInternal zero;
+#ifdef __sun
+ struct sfxge_sock_ioctl req;
+#else
+ struct efx_sock_ioctl req;
+#endif
+
+ if(rtOpts->time_mode != TIME_BOTH)
+ return;
+
+ /* Limit the number of times per second we synchronise with the NIC */
+ static TimeInternal lastsync;
+ TimeInternal now, t;
+ LongDouble offset;
+ timerNow(&now);
+ subTime(&t, &now, &lastsync);
+ offset = (LongDouble)t.seconds + ((LongDouble)t.nanoseconds / 1000000000);
+ if ((offset > 0) && (offset < rtOpts->system_time_update_interval))
+ return;
+
+ lastsync = now;
+
+ memset(&req, 0, sizeof(req));
+#if defined(__sun)
+ req.cmd = SFXGE_TS_SYNC;
+#else
+ req.cmd = EFX_TS_SYNC;
+#endif
+ if (ptp_ioctl(&ptpClock->netPath, &req) < 0) {
+ ptpClock->statistics.ts_sync_failures++;
+ ERROR("failed to correlate SFC NIC and system clock %d times (if %s, error %s)\n",
+ ptpClock->statistics.ts_sync_failures,
+ ptpClock->netPath.eventSockIFR.lifr_name, strerror(errno));
+ return;
+ }
+
+ /* For master modes, set the name according to the port state */
+ switch (rtOpts->master_slave_mode) {
+ case PTP_MODE_MASTER_WITH_NTP:
+ if (ptpClock->portState == PTP_MASTER) {
+ ptpClock->name = "[nic->slave]";
+ } else if (ptpClock->portState == PTP_PASSIVE) {
+ ptpClock->name = "[master||nic]";
+ }
+ break;
+
+ case PTP_MODE_MASTER_NO_NTP:
+ if (ptpClock->portState == PTP_MASTER) {
+ ptpClock->name = "[nic->slave]";
+ } else if (ptpClock->portState == PTP_SLAVE) {
+ ptpClock->name = "[master->nic]";
+ }
+ break;
+
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ /* Update the timeBothClock with leap seconds in progress from main servo
+ * to make sure we don't try to update the system clock during the leap
+ * second.
+ */
+ timeBothClock.leapSecondInProgress = ptpClock->leapSecondInProgress;
+
+ // TODO Possibly the delay req time needs to be inverted or doubled
+ // We don't really support a measurement of the one way delay at present.
+
+ DBGV("sync value %lld.%09d\n", req.u.ts_sync.ts.tv_sec, req.u.ts_sync.ts.tv_nsec);
+ DBGV("system to NIC delay %ld.%09d\n", delay.seconds, delay.nanoseconds);
+
+ timeBothClock.delay_req_receive_time = delay;
+ timeBothClock.delay_req_send_time = zero;
+ /* Note- we don't use the correction factor- zero */
+ updateDelay(&timeBothClock.owd_filt, &timeBothRtOpts, &timeBothClock, &zero);
+
+ /* Note that the NIC to system delay and system to NIC delay are the same */
+ DBGV("NIC to system delay %ld.%09d\n", delay.seconds, delay.nanoseconds);
+ /* Note that we don't use the correction factor - zero */
+ updateOffset(&delay, &zero, &timeBothClock.ofm_filt, &timeBothRtOpts, &timeBothClock, &zero);
+
+ updateClock(&timeBothRtOpts, &timeBothClock);
+ DBGV("system time updated\n");
+}
+
+static Boolean selectNICTimeMode(TimeMode timeMode, PtpClock *ptpClock)
+{
+ NetPath *netPath = &ptpClock->netPath;
+
+ DBGV("time stamp rx/tx packets\n");
+
+#ifdef linux
+ /* Linux SW mode is an anomaly and we treat it differently to the other modes */
+ if (timeMode == TIME_SYSTEM_LINUX_SW) {
+ /* same as before, but without requiring support by the NIC */
+ int so_timestamping_flags = SOF_TIMESTAMPING_TX_SOFTWARE
+ | SOF_TIMESTAMPING_RX_SOFTWARE
+ | SOF_TIMESTAMPING_SOFTWARE;
+
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_TIMESTAMPING,
+ &so_timestamping_flags, sizeof(so_timestamping_flags)) < 0) {
+ ERROR("SO_TIMESTAMPING: setsockopt error: %s", strerror(errno));
+ return FALSE;
+ }
+
+ NOTIFY("SO_TIMESTAMPING (software) enabled\n");
+ ptpClock->tsMethod = TS_METHOD_SO_TIMESTAMPING;
+ return TRUE;
+ }
+#endif
+
+ /* For the modes linux_hw, nic and both, try to enable SO_TIMESTAMPING. If
+ * that fails, attempt IOCTL based timestamping. Otherwise, fail.
+ */
+
+#ifndef __sun /* Do not attempt to compile for the time being if on __sun */
+
+ /* Attempt SO_TIMSTAMPING */
+ NOTIFY("trying SO_TIMESTAMPING...\n");
+
+ {
+ struct hwtstamp_config hwconfig;
+
+ int so_timestamping_flags = SOF_TIMESTAMPING_TX_HARDWARE
+ | SOF_TIMESTAMPING_RX_HARDWARE
+ | SOF_TIMESTAMPING_SYS_HARDWARE
+ | SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ netPath->eventSockIFR.ifr_data = (void *)&hwconfig;
+
+ memset(&hwconfig, 0, sizeof(&hwconfig));
+ hwconfig.tx_type = HWTSTAMP_TX_ON;
+ hwconfig.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
+
+ if (ioctl(netPath->eventSock, SIOCSHWTSTAMP, &netPath->eventSockIFR) < 0) {
+ if (errno == ERANGE) {
+ /* hardware time stamping not supported */
+ NOTIFY("SO_TIMESTAMPING: SIOCSHWTSTAMP mode of operation not supported\n");
+ } else {
+ NOTIFY("SO_TIMESTAMPING: SIOCSHWTSTAMP %s\n", strerror(errno));
+ }
+ } else {
+ /* Now enable the SO_TIMESTAMPING option on the event socket */
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_TIMESTAMPING,
+ &so_timestamping_flags, sizeof(so_timestamping_flags)) < 0) {
+ /* Enabling SO_TIMESTAMPING on the socket shouldn't fail if the driver
+ * operation succeeded.
+ */
+ NOTIFY("SO_TIMESTAMPING: setsockopt error: %s", strerror(errno));
+ /* TODO should we carry on? */
+ } else {
+ NOTIFY("SO_TIMESTAMPING enabled\n");
+ ptpClock->tsMethod = TS_METHOD_SO_TIMESTAMPING;
+ return TRUE;
+ }
+ }
+ }
+
+#endif /* ifndef __sun */
+
+ /* Attempt IOCTL timestamping */
+ NOTIFY("trying IOCTL timestamping...\n");
+
+ {
+#ifdef __sun
+ struct sfxge_sock_ioctl req;
+#else
+ struct efx_sock_ioctl req;
+#endif
+ req.u.ts_init.flags = 0;
+ req.u.ts_init.tx_type = HWTSTAMP_TX_ON;
+ req.u.ts_init.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
+
+#ifdef __sun
+ req.cmd = SFXGE_TS_INIT;
+#else
+ req.cmd = EFX_TS_INIT;
+#endif
+
+ if(ptp_ioctl(netPath, &req) < 0) {
+ NOTIFY("could not activate SFC hardware rx/tx time stamping on %s, %s\n",
+ netPath->eventSockIFR.lifr_name, strerror(errno));
+ } else {
+ NOTIFY("SFC IOCTL timestamping enabled\n");
+ ptpClock->tsMethod = TS_METHOD_DRIVER_IOCTL;
+ return TRUE;
+ }
+ }
+
+ ERROR("failed to enable IOCTL hardware timestamping!\n");
+ return FALSE;
+}
+
+Boolean initTime(RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ Boolean rc;
+
+ switch(rtOpts->time_mode) {
+ case TIME_SYSTEM:
+ ptpClock->tsMethod = TS_METHOD_SYSTEM;
+ return TRUE;
+ break;
+ case TIME_BOTH:
+ /* Prepare clock servo for controlling system time.
+ * TODO We can adjust the secondary servo rate now so this is no longer really true...
+ * "For the system->nic secondary servo, we reset the S, AP and AI parameters
+ * because the servo rate is fixed at 1 second, where as the filter parameters
+ * for the primary may by adjusted to compensate for message frequency and
+ * accuracy of measurements."
+ * Generally if noAdjust is set on the primary, set it on the secondary. The exception is
+ * when a master with NTP. In this case, the secondary is used to sync the NIC clock from
+ * the system clock so enable adjustment.
+ * The four scenarios are as follows:
+ * Master + NTP: system --> nic --> slave (-G option)
+ * Slave + NTP: system --> nic X master (-G option)
+ * Master no NTP: system <-- nic --> slave (-W option)
+ * Slave no NTP: system <-- nic <-- master (-g option)
+ */
+ timeBothClock = *ptpClock;
+ timeBothRtOpts = *rtOpts;
+ timeBothRtOpts.s = DEFAULT_DELAY_S;
+ timeBothRtOpts.ap = DEFAULT_AP;
+ timeBothRtOpts.ai = DEFAULT_AI;
+ timeBothClock.portState = PTP_SLAVE;
+
+ switch (rtOpts->master_slave_mode) {
+ case PTP_MODE_MASTER_WITH_NTP:
+ ptpClock->name = "[nic->slave]";
+ timeBothClock.name = "[system->nic]";
+ timeBothRtOpts.time_mode = TIME_NIC;
+ timeBothClock.nic_instead_of_system = TRUE;
+ timeBothRtOpts.noAdjust = DEFAULT_NO_ADJUST_CLOCK;
+ break;
+
+ case PTP_MODE_MASTER_NO_NTP:
+ ptpClock->name = "[nic->slave]";
+ timeBothClock.name = "[nic->system]";
+ timeBothRtOpts.time_mode = TIME_SYSTEM;
+ timeBothClock.nic_instead_of_system = FALSE;
+ break;
+
+ case PTP_MODE_SLAVE:
+ ptpClock->name = "[master->nic]";
+ timeBothClock.name = "[nic->system]";
+ timeBothRtOpts.time_mode = TIME_SYSTEM;
+ timeBothClock.nic_instead_of_system = FALSE;
+ break;
+
+ default:
+ ERROR("Invalid value in switch %d\n", rtOpts->master_slave_mode);
+ return FALSE;
+ }
+
+ initClock(&timeBothRtOpts, &timeBothClock);
+ G_timeBothClock = &timeBothClock;
+ G_timeBothRtOpts = &timeBothRtOpts;
+
+ rc = selectNICTimeMode(rtOpts->time_mode, ptpClock);
+ if (rc && ((rtOpts->master_slave_mode != PTP_MODE_SLAVE) || rtOpts->noResetClock)) {
+ /* If we are in one or the master modes, set the NIC time once using the
+ * current system time. After this, the NIC time will free-run but at least
+ * we won't be shortly after the epoch. Note that we use timerNow rather
+ * than getTime to get the system time because getTime will return the NIC
+ * time in this mode.
+ */
+ TimeInternal time;
+ timerNow(&time);
+ setTime(&time, rtOpts->time_mode, ptpClock);
+ NOTIFY("NIC time set to system time: %d.%d\n", time.seconds, time.nanoseconds);
+ }
+ return rc;
+ break;
+ case TIME_NIC:
+ rc = selectNICTimeMode(rtOpts->time_mode, ptpClock);
+ if (rc && ((rtOpts->master_slave_mode != PTP_MODE_SLAVE) || rtOpts->noResetClock)) {
+ /* If we are in one or the master modes, set the NIC time once using the
+ * current system time. After this, the NIC time will free-run but at least
+ * we won't be shortly after the epoch. Note that we use timerNow rather
+ * than getTime to get the system time because getTime will return the NIC
+ * time in this mode.
+ */
+ TimeInternal time;
+ timerNow(&time);
+ setTime(&time, rtOpts->time_mode, ptpClock);
+ NOTIFY("NIC time set to system time: %d.%d\n", time.seconds, time.nanoseconds);
+ }
+ return rc;
+ break;
+ case TIME_SYSTEM_LINUX_HW:
+ case TIME_SYSTEM_LINUX_SW:
+ return selectNICTimeMode(rtOpts->time_mode, ptpClock);
+ break;
+ default:
+ ERROR("unsupported selection of time source\n");
+ return FALSE;
+ break;
+ }
+}
+
+void shutdownTime(PtpClock *ptpClock)
+{
+ NetPath *netPath = &ptpClock->netPath;
+
+ switch (ptpClock->tsMethod) {
+
+#ifndef __sun /* Do not compile for the time being if __sun */
+ case TS_METHOD_SO_TIMESTAMPING:
+ {
+ int so_timestamping_flags = 0;
+
+ struct hwtstamp_config hwconfig;
+ netPath->eventSockIFR.ifr_data = (void *)&hwconfig;
+ memset(&hwconfig, 0, sizeof(&hwconfig));
+
+ hwconfig.tx_type = HWTSTAMP_TX_OFF;
+ hwconfig.rx_filter = HWTSTAMP_FILTER_NONE;
+ (void)ioctl(netPath->eventSock, SIOCSHWTSTAMP, &netPath->eventSockIFR);
+
+ (void)setsockopt(netPath->eventSock, SOL_SOCKET, SO_TIMESTAMPING,
+ &so_timestamping_flags, sizeof(so_timestamping_flags));
+ }
+ break;
+#endif /* ifndef __sun */
+
+ case TS_METHOD_DRIVER_IOCTL:
+ {
+#ifdef __sun
+ struct sfxge_sock_ioctl req;
+ req.cmd = SFXGE_TS_INIT;
+#else
+ struct efx_sock_ioctl req;
+ req.cmd = EFX_TS_INIT;
+#endif
+ req.u.ts_init.flags = 0;
+ req.u.ts_init.tx_type = HWTSTAMP_TX_OFF;
+ req.u.ts_init.rx_filter = HWTSTAMP_FILTER_NONE;
+ (void)ptp_ioctl(netPath, &req);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void getTime(TimeInternal *time, TimeMode timeMode, PtpClock *ptpClock)
+{
+ switch(timeMode)
+ {
+ case TIME_SYSTEM_LINUX_HW:
+ case TIME_SYSTEM_LINUX_SW:
+ case TIME_SYSTEM: {
+
+#if defined(__APPLE__) /* TODO Does OSX really not support clock_gettime()? */
+ struct timeval tv;
+
+ gettimeofday(&tv, 0);
+ time->seconds = tv.tv_sec;
+ time->nanoseconds = tv.tv_usec*1000;
+#else
+ struct timespec tv;
+ int err = clock_gettime(CLOCK_REALTIME, &tv);
+ if (err != 0) {
+ ERROR("error getting time %d\n", err);
+ return;
+ }
+ time->seconds = tv.tv_sec;
+ time->nanoseconds = tv.tv_nsec;
+#endif
+ break;
+ }
+ case TIME_BOTH:
+ case TIME_NIC: {
+ NetPath *netPath = &ptpClock->netPath;
+#ifdef __sun
+ struct sfxge_sock_ioctl req;
+#else
+ struct efx_sock_ioctl req;
+#endif
+
+ memset(&req, 0, sizeof(req));
+
+#ifdef __sun
+ req.cmd = SFXGE_TS_SETTIME;
+#else
+ req.cmd = EFX_TS_SETTIME;
+#endif
+ req.u.ts_settime.iswrite = 0;
+ if (ptp_ioctl(netPath, &req) < 0) {
+ ERROR("could not read SFC hardware time on %s: %s\n",
+ netPath->eventSockIFR.lifr_name, strerror(errno));
+ return;
+ }
+ time->seconds = req.u.ts_settime.ts.tv_sec;
+ time->nanoseconds = req.u.ts_settime.ts.tv_nsec;
+ break;
+ }
+ default:
+ ERROR("unsupported selection of time source\n");
+ break;
+ }
+}
+
+void setTime(TimeInternal *time, TimeMode timeMode, PtpClock *ptpClock)
+{
+ switch(timeMode)
+ {
+ case TIME_SYSTEM_LINUX_HW:
+ case TIME_SYSTEM_LINUX_SW:
+ case TIME_SYSTEM: {
+ WARNING("going to step the system clock to %ds %dns\n",
+ time->seconds, time->nanoseconds);
+#if defined(__APPLE__) /* TODO Does OSX really not support clock_settime()? */
+ struct timeval tv;
+ tv.tv_sec = time->seconds;
+ tv.tv_usec = time->nanoseconds/1000;
+ settimeofday(&tv, 0);
+#else
+ struct timespec tv;
+ tv.tv_sec = time->seconds;
+ tv.tv_nsec = time->nanoseconds;
+ int err = clock_settime(CLOCK_REALTIME, &tv);
+ if (err != 0) {
+ ERROR("error setting time %d\n", err);
+ return;
+ }
+#endif
+ WARNING("finished stepping the system clock to %ds %dns\n",
+ time->seconds, time->nanoseconds);
+ break;
+ }
+ case TIME_BOTH:
+ case TIME_NIC: {
+ NetPath *netPath = &ptpClock->netPath;
+#ifdef __sun
+ struct sfxge_sock_ioctl req;
+#else
+ struct efx_sock_ioctl req;
+#endif
+
+ TimeInternal currentTime, offset;
+
+ memset(&req, 0, sizeof(req));
+
+ NOTIFY("resetting NIC clock to %ds %dns\n", time->seconds, time->nanoseconds);
+ getTime(¤tTime, timeMode, ptpClock);
+ subTime(&offset, time, ¤tTime);
+
+ req.u.ts_settime.iswrite = 1;
+#ifdef __sun
+ req.cmd = SFXGE_TS_SETTIME;
+#else
+ req.cmd = EFX_TS_SETTIME;
+#endif
+
+ NOTIFY("adding NIC offset %ld.%09d (%ld/%p)\n",
+ sizeof(req), &req);
+ if (ptp_ioctl(netPath, &req) < 0) {
+ ERROR("could not set SFC hardware time on %s: %s\n",
+ netPath->eventSockIFR.lifr_name, strerror(errno));
+ }
+ else
+ {
+ DBGV("new NIC time %ld.%09d\n",
+ }
+ break;
+ }
+ default:
+ ERROR("unsupported selection of time source\n");
+ break;
+ }
+}
+
+
+void adjTime(LongDouble adj, TimeMode timeMode, PtpClock *ptpClock)
+{
+ switch(timeMode)
+ {
+ case TIME_SYSTEM_LINUX_HW:
+ case TIME_SYSTEM_LINUX_SW:
+ case TIME_SYSTEM: {
+ struct timex t;
+ static Boolean maxAdjValid;
+ static LongDouble maxAdj;
+ static LongDouble minTick, maxTick;
+ static LongDouble userHZ;
+ static LongDouble tickRes; /* USER_HZ * 1000 [ppb] */
+ LongDouble tickAdj;
+ LongDouble freqAdj;
+ long maxError;
+ int res;
+
+ if (!maxAdjValid) {
+ userHZ = (LongDouble)sysconf(_SC_CLK_TCK);
+ t.modes = 0;
+#ifdef __sun
+#warning Solaris - struct timex: t.constant is arbitrarily assigned to 15
+ t.constant = 15; /* need to be >0 but <30 */
+#endif
+ adjtimex(&t);
+ maxAdj = (LongDouble)t.tolerance / (((1<<16)+0.0)/1000.0);
+ tickRes = userHZ * 1000.0;
+ /* limits from the adjtimex command man page; could be determined via binary search */
+ minTick = (900000.0 - 1000000.0) / userHZ;
+ maxTick = (1100000.0 - 1000000.0) / userHZ;
+ maxAdjValid = TRUE;
+ }
+
+ /*
+ * The Linux man page for the adjtimex() system call does not
+ * describe limits for frequency. The more recent man page for
+ * the adjtimex command on RH5 does and says that
+ * -tolerance <= frequency <= tolerance
+ * which was confirmed by trying out values just outside that interval.
+ *
+ * Note that this contradicts the comments for struct timex which say
+ * that freq and tolerance have different units (scaled ppm vs ppm).
+ *
+ * We follow the actual implementation on Linux 2.6.22 and do the
+ * range check after scaling.
+ */
+
+ /* LDE 17082011 Set max error to stop adjtimex reported bad time */
+ t.modes = MOD_FREQUENCY|MOD_CLKB|MOD_MAXERROR;
+
+ /*
+ * 1 t.tick = 1 e-6 s * USER_HZ 1/s = 1 USER_HZ * 1000 ppb
+ *
+ * Large values of adj can be turned into t.tick adjustments:
+ * tickAdj t.tick = adj ppb / ( USER_HZ * 1000 ppb )
+ *
+ * Round this so that the error is as small is possible,
+ * because we need to fit that into t.freq.
+ */
+ freqAdj = adj;
+ tickAdj = 0;
+ if(freqAdj > maxAdj)
+ {
+ tickAdj = round((adj - maxAdj) / tickRes);
+ if(tickAdj > maxTick)
+ tickAdj = maxTick;
+ freqAdj = adj - tickAdj * tickRes;
+ }
+ else if(freqAdj < -maxAdj)
+ {
+ tickAdj = -round((-adj - maxAdj) / tickRes);
+ if(tickAdj < minTick)
+ tickAdj = minTick;
+ freqAdj = adj - tickAdj * tickRes;
+ }
+ if(freqAdj > maxAdj)
+ freqAdj = maxAdj;
+ else if(freqAdj < -maxAdj)
+ freqAdj = -maxAdj;
+
+ t.freq = (long)round(freqAdj * (((1 << 16) + 0.0) / 1000.0));
+#ifdef __sun
+#warning Solaris - struct timex: no field named tick
+#warning Solaris - struct timex: t.constant is arbitrarily assigned to 15
+ t.constant = 15; /* need to be >0 but <30 */
+#else
+ t.tick = (long)round(tickAdj + (1000000.0 / userHZ));
+#endif
+ ptpClock->frequency_adjustment = tickAdj * tickRes + freqAdj;
+
+ /* LDE 17082011 Set the max error to the current offset from master */
+ /* Saturate the max error at 2000 seconds. This is close the max value that */
+ /* fit in the max error field. */
+ if (ptpClock->offsetFromMaster.seconds > 2000) {
+ maxError = 0xffffffff;
+ } else {
+ maxError = ptpClock->offsetFromMaster.seconds * 1000000;
+ if (ptpClock->offsetFromMaster.nanoseconds > 0) {
+ maxError += ptpClock->offsetFromMaster.nanoseconds/1000;
+ } else {
+ maxError -= ptpClock->offsetFromMaster.nanoseconds/1000;
+ }
+ }
+ t.maxerror = maxError;
+
+#ifndef __sun
+ DBG("requested adj %Lf ppb => adjust system frequency by %ld scaled ppm (%Lf ppb) + %ld us/tick "
+ "(%Lf ppb) = adj %Lf ppb (freq limit %Lf/%Lf ppm, tick limit %Lf/%Lf us*USER_HZ)\n",
+ adj,
+ t.freq, freqAdj,
+ (long)(t.tick - 1000000.0 / userHZ), tickAdj * tickRes,
+ ptpClock->frequency_adjustment,
+ -maxAdj, maxAdj,
+ minTick, maxTick);
+#endif
+ res = adjtimex(&t);
+ switch (res) {
+ case -1:
+ ERROR("adjtimex(freq = %d) failed: %s\n",
+ t.freq, strerror(errno));
+ break;
+ case TIME_OK:
+ DBG(" -> TIME_OK\n");
+ break;
+ case TIME_INS:
+ ERROR("adjtimex -> insert leap second?!\n");
+ break;
+ case TIME_DEL:
+ ERROR("adjtimex -> delete leap second?!\n");
+ break;
+ case TIME_OOP:
+ ERROR("adjtimex -> leap second in progress?!\n");
+ break;
+ case TIME_WAIT:
+ ERROR("adjtimex -> leap second has occurred?!\n");
+ break;
+ case TIME_BAD:
+ ERROR("adjtimex -> time bad\n");
+ /* Clear the unsynchronised flag - we are synchronised */
+ t.modes = MOD_STATUS;
+ t.status &= ~STA_UNSYNC;
+ (void)adjtimex(&t);
+ INFO("clearing system time unsynchronised flag\n");
+ break;
+ default:
+ ERROR("adjtimex -> unknown result %d\n", res);
+ break;
+ }
+ break;
+ }
+ case TIME_BOTH:
+ case TIME_NIC: {
+ /* adjust NIC frequency */
+ NetPath *netPath = &ptpClock->netPath;
+#ifdef __sun
+ struct sfxge_sock_ioctl req;
+#else
+ struct efx_sock_ioctl req;
+#endif
+
+ memset(&req, 0, sizeof(req));
+ if (ptpClock->nic_instead_of_system) {
+ req.u.ts_adjtime.adjustment = (long long)round(-adj);
+ ptpClock->frequency_adjustment = -adj;
+ } else {
+ req.u.ts_adjtime.adjustment = (long long)round(adj);
+ ptpClock->frequency_adjustment = adj;
+ }
+ req.u.ts_adjtime.iswrite = 1;
+#ifdef __sun
+ req.cmd = SFXGE_TS_ADJTIME;
+#else
+ req.cmd = EFX_TS_ADJTIME;
+#endif
+ DBGV("adjust NIC frequency by %lld (%Lf) ppb\n",
+ req.u.ts_adjtime.adjustment, ptpClock->frequency_adjustment);
+ if (ptp_ioctl(netPath, &req) < 0) {
+ ERROR("could not modify SFC hardware frequency on %s: %s\n",
+ netPath->eventSockIFR.lifr_name, strerror(errno));
+ }
+ break;
+ }
+ default:
+ ERROR("unsupported selection of time source\n");
+ break;
+ }
+}
+
+void adjTimeOffset(TimeInternal *offset, TimeMode timeMode, PtpClock *ptpClock)
+{
+ switch(timeMode)
+ {
+ case TIME_BOTH:
+ case TIME_NIC: {
+ NetPath *netPath = &ptpClock->netPath;
+#ifdef __sun
+ struct sfxge_sock_ioctl req;
+#else
+ struct efx_sock_ioctl req;
+#endif
+
+ memset(&req, 0, sizeof(req));
+ req.u.ts_settime.iswrite = 1;
+ req.u.ts_settime.ts.tv_sec = offset->seconds;
+ req.u.ts_settime.ts.tv_nsec = offset->nanoseconds;
+#ifdef __sun
+ req.cmd = SFXGE_TS_SETTIME;
+#else
+ req.cmd = EFX_TS_SETTIME;
+#endif
+
+ /* invert the sign: if offset is positive, we need to substract it and vice versa;
+ * when in nic_instead_of_system the logic is already inverted
+ */
+ // TODO check that time is normalised at this point / use arith fns for all time ops
+ if (!ptpClock->nic_instead_of_system) {
+ req.u.ts_settime.ts.tv_sec *= -1;
+ req.u.ts_settime.ts.tv_nsec *= -1;
+ }
+
+ DBGV("adjust NIC time by offset %ld.%09d\n",
+ (UInteger32)req.u.ts_settime.ts.tv_sec, req.u.ts_settime.ts.tv_nsec);
+ if (ptp_ioctl(netPath, &req) < 0) {
+ ERROR("could not modify SFC hardware time on %s: %s\n",
+ ptpClock->netPath.eventSockIFR.lifr_name,
+ strerror(errno));
+ }
+ break;
+ }
+
+ default: {
+ TimeInternal timeTmp;
+
+ DBG2("Adjusting system time by offset %ld.%09d\n", offset->seconds, offset->nanoseconds);
+
+ getTime(&timeTmp, timeMode, ptpClock);
+ DBGV("**** Time was %ld.%09d\n", timeTmp.seconds, timeTmp.nanoseconds);
+ subTime(&timeTmp, &timeTmp, offset);
+ /* We don't allow the time to be set to before 1/1/1971 because this almost */
+ /* certainly means something has gone very wrong */
+ if (timeTmp.seconds < UTC_TIME_VALID_MINIMUM) {
+ DBG("**** Refusing to set system time to %ld.%09d\n", timeTmp.seconds, timeTmp.nanoseconds);
+ } else {
+ setTime(&timeTmp, timeMode, ptpClock);
+ DBG("System time adjusted to %ld.%09d\n", timeTmp.seconds, timeTmp.nanoseconds);
+ }
+ }
+ }
+}
+
+void adjTimeInsertLeapSecond(TimeMode timeMode, RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ if (rtOpts->noResetClock || rtOpts->resetClockStartupOnly)
+ return;
+
+ switch(timeMode) {
+ case TIME_BOTH:
+ case TIME_NIC:
+ case TIME_SYSTEM_LINUX_HW:
+ {
+ NetPath *netPath = &ptpClock->netPath;
+ LongDouble adj;
+#ifdef __sun
+ struct sfxge_sock_ioctl req;
+#else
+ struct efx_sock_ioctl req;
+#endif
+ memset(&req, 0, sizeof(req));
+#if defined(__sun)
+ req.cmd = SFXGE_TS_SETTIME;
+#else
+ req.cmd = EFX_TS_SETTIME;
+#endif
+
+ req.u.ts_settime.iswrite = 1;
+ req.u.ts_settime.ts.tv_sec = -1;
+ req.u.ts_settime.ts.tv_nsec = 0;
+
+ DBGV("insert leap second in NIC time\n");
+ if (ptp_ioctl(netPath, &req) < 0) {
+ ERROR("could not modify SFC hardware time on %s: %s\n",
+ ptpClock->netPath.eventSockIFR.lifr_name,
+ strerror(errno));
+ }
+
+ /* Applying an offset resets the frequency adjustment to 0 (doh!)
+ * If we are running in time mode both or nic, we need to set the
+ * frequency adjustment back to the correct value immediately!
+ */
+ if ((timeMode == TIME_BOTH) || (timeMode == TIME_NIC)) {
+ adj = (LongDouble)ptpClock->offsetFromMaster.nanoseconds / rtOpts->ap
+ + ptpClock->observed_drift;
+
+ adjTime(-adj, timeMode, ptpClock);
+ }
+ break;
+ }
+
+ default:
+ INFO("No need to insert leap second for mode - handled by kernel\n");
+ }
+}
+
+void adjTimeDeleteLeapSecond(TimeMode timeMode, RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ if (rtOpts->noResetClock || rtOpts->resetClockStartupOnly)
+ return;
+
+ switch(timeMode) {
+ case TIME_BOTH:
+ case TIME_NIC:
+ case TIME_SYSTEM_LINUX_HW:
+ {
+ NetPath *netPath = &ptpClock->netPath;
+ LongDouble adj;
+
+#ifdef __sun
+ struct sfxge_sock_ioctl req;
+
+ memset(&req, 0, sizeof(req));
+ req.cmd = SFXGE_TS_SETTIME;
+#else
+ struct efx_sock_ioctl req;
+
+ memset(&req, 0, sizeof(req));
+ req.cmd = EFX_TS_SETTIME;
+#endif
+
+ req.u.ts_settime.iswrite = 1;
+ req.u.ts_settime.ts.tv_sec = 1;
+ req.u.ts_settime.ts.tv_nsec = 0;
+
+ DBGV("delete leap second from NIC time\n");
+ if (ptp_ioctl(netPath, &req) < 0) {
+ ERROR("could not modify SFC hardware time on %s: %s\n",
+ ptpClock->netPath.eventSockIFR.lifr_name,
+ strerror(errno));
+ }
+
+ /* Applying an offset resets the frequency adjustment to 0 (doh!)
+ * If we are running in time mode both or nic, we need to set the
+ * frequency adjustment back to the correct value immediately!
+ */
+ if ((timeMode == TIME_BOTH) || (timeMode == TIME_NIC)) {
+ adj = (LongDouble)ptpClock->offsetFromMaster.nanoseconds / rtOpts->ap
+ + ptpClock->observed_drift;
+
+ adjTime(-adj, timeMode, ptpClock);
+ }
+ break;
+ }
+
+ default:
+ INFO("No need to insert leap second for mode - handled by kernel\n");
+ }
+}
+
+
+static void getTimeStamps(TimeMode timeMode, NetPath *netPath)
+{
+#ifdef __sun
+ struct sfxge_sock_ioctl req;
+#else
+ struct efx_sock_ioctl req;
+#endif
+
+ Boolean rawTime = (timeMode == TIME_BOTH) || (timeMode == TIME_NIC);
+
+ /* Repeatedly call the IOCTL until we have all the timestamps */
+ do {
+ memset(&req, 0, sizeof(req));
+#ifdef __sun
+ req.cmd = SFXGE_TS_READ;
+#else
+ req.cmd = EFX_TS_READ;
+#endif
+ if(ptp_ioctl(netPath, &req) < 0) {
+ ERROR("could not read SFC hardware time stamps on %s: %s\n",
+ netPath->eventSockIFR.lifr_name, strerror(errno));
+ return;
+ }
+
+ DBGV("rx %s, tx %s\n",
+ req.u.ts_read.rx_valid ? "valid" : "invalid",
+ req.u.ts_read.tx_valid ? "valid" : "invalid");
+
+ if(req.u.ts_read.rx_valid)
+ {
+ int newIndex;
+
+ if(nextFreeRecv == RECV_ARRAY_SIZE)
+ {
+ newIndex = 0;
+ nextFreeRecv = 1;
+ oldestRecv = 2;
+ }
+ else
+ {
+ newIndex = nextFreeRecv;
+ nextFreeRecv++;
+ if(oldestRecv && nextFreeRecv == oldestRecv)
+ ++oldestRecv;
+ }
+
+ if(oldestRecv >= RECV_ARRAY_SIZE)
+ oldestRecv = 0;
+
+ DBGV("new entry %d, oldest %d, next free %d\n", newIndex, oldestRecv, nextFreeRecv);
+
+ if (rawTime) {
+ lastRecvTimes[newIndex].recvTimeStamp.seconds = req.u.ts_read.rx_ts_hw.tv_sec;
+ lastRecvTimes[newIndex].recvTimeStamp.nanoseconds = req.u.ts_read.rx_ts_hw.tv_nsec;
+ } else {
+ lastRecvTimes[newIndex].recvTimeStamp.seconds = req.u.ts_read.rx_ts.tv_sec;
+ lastRecvTimes[newIndex].recvTimeStamp.nanoseconds = req.u.ts_read.rx_ts.tv_nsec;
+ }
+ lastRecvTimes[newIndex].sequenceId = ((uint16_t)req.u.ts_read.seqid[0] << 8)
+ | (uint16_t)req.u.ts_read.seqid[1];
+
+ /* Section 18.3.7 IEEE1588. Version 1 UUIDs are mapped into octets 2-7 of the clock
+ * ID. We don't compare the first two octets.
+ */
+ lastRecvTimes[newIndex].clockId[0] = 0;
+ lastRecvTimes[newIndex].clockId[1] = 0;
+ memcpy(lastRecvTimes[newIndex].clockId + sizeof(lastRecvTimes[newIndex].clockId) - sizeof(req.u.ts_read.uuid),
+ sizeof(req.u.ts_read.uuid));
+
+ DBGV("rx %d: time %d.%09u, sequence %u, uuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ newIndex,
+ lastRecvTimes[newIndex].recvTimeStamp.seconds,
+ lastRecvTimes[newIndex].recvTimeStamp.nanoseconds,
+ lastRecvTimes[newIndex].sequenceId,
+ }
+
+ if(req.u.ts_read.tx_valid)
+ {
+ if (rawTime) {
+ } else {
+ }
+
+ DBGV("tx time %d.%09d (%d.%09d)\n",
+ }
+ } while (req.u.ts_read.tx_valid || req.u.ts_read.rx_valid);
+}
+
+Boolean getSendTime(TimeInternal *sendTimeStamp,
+ TimeMode timeMode, NetPath *netPath)
+{
+ /* check for new time stamps */
+ getTimeStamps(timeMode, netPath);
+
+ {
+ *sendTimeStamp = lastSendTime;
+ lastSendTime.seconds = 0;
+ lastSendTime.nanoseconds = 0;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * helper function for getReceiveTime() which searches for time stamp
+ * in lastRecvTimes[leftIndex, rightIndex[
+ */
+static Boolean getReceiveTimeFromArray(TimeInternal *recvTimeStamp,
+ ClockIdentity clockId,
+ UInteger16 sequenceId,
+ int leftIndex, int rightIndex)
+{
+ int i;
+
+ DBGV("left idx %d, right idx %d\n", leftIndex, rightIndex);
+
+ //DBGV("sequence id %d, clock id %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ // sequenceId,
+ // clockId[0], clockId[1], clockId[2], clockId[3],
+ // clockId[4], clockId[5], clockId[6], clockId[7]);
+
+ for(i = leftIndex; i < rightIndex; i++)
+ {
+ //DBGV("rx index %d: time %d.%09d, sequence %u, clock id %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ // i,
+ // lastRecvTimes[i].recvTimeStamp.seconds,
+ // lastRecvTimes[i].recvTimeStamp.nanoseconds,
+ // lastRecvTimes[i].sequenceId,
+ // lastRecvTimes[i].clockId[0], lastRecvTimes[i].clockId[1],
+ // lastRecvTimes[i].clockId[2], lastRecvTimes[i].clockId[3],
+ // lastRecvTimes[i].clockId[4], lastRecvTimes[i].clockId[5],
+ // lastRecvTimes[i].clockId[6], lastRecvTimes[i].clockId[7]);
+
+ if(!memcmp(lastRecvTimes[i].clockId + 2, clockId + 2, 6) &&
+ lastRecvTimes[i].sequenceId == sequenceId)
+ {
+ DBGV("found rx index %d: time %d.%09d, sequence %u, clock id %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ i,
+ lastRecvTimes[i].recvTimeStamp.seconds,
+ lastRecvTimes[i].recvTimeStamp.nanoseconds,
+ lastRecvTimes[i].sequenceId,
+ lastRecvTimes[i].clockId[0], lastRecvTimes[i].clockId[1],
+ lastRecvTimes[i].clockId[2], lastRecvTimes[i].clockId[3],
+ lastRecvTimes[i].clockId[4], lastRecvTimes[i].clockId[5],
+ lastRecvTimes[i].clockId[6], lastRecvTimes[i].clockId[7]);
+
+ *recvTimeStamp = lastRecvTimes[i].recvTimeStamp;
+ // invalidate entry to prevent accidental reuse (happened when slaves were
+ // restarted quickly while the master still had their old sequence IDs in the array)
+ memset(&lastRecvTimes[i], 0, sizeof(lastRecvTimes[i]));
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+Boolean getReceiveTime(TimeInternal *recvTimeStamp,
+ ClockIdentity clockId,
+ UInteger16 sequenceId,
+ TimeMode timeMode,
+ NetPath *netPath)
+{
+ /* check for new time stamps */
+ getTimeStamps(timeMode, netPath);
+
+ if(oldestRecv <= nextFreeRecv)
+ return getReceiveTimeFromArray(recvTimeStamp, clockId, sequenceId, oldestRecv, nextFreeRecv);
+ else
+ {
+ if(getReceiveTimeFromArray(recvTimeStamp, clockId, sequenceId, oldestRecv, RECV_ARRAY_SIZE))
+ return TRUE;
+ else
+ return getReceiveTimeFromArray(recvTimeStamp, clockId, sequenceId, 0, nextFreeRecv);
+ }
+}
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/dep/timer.c
--- a/src/dep/timer.c Tue May 14 17:07:59 2013 -0700
+++ b/src/dep/timer.c Sun Oct 27 22:49:29 2013 -0700
@@ -1,5 +1,7 @@
/*-
- * Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
+ * Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
* Martin Burnicki, Gael Mace, Alexandre Van Kempen
* Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
*
@@ -39,6 +41,9 @@
#include "../ptpd.h"
#define US_TIMER_INTERVAL (62500)
+#ifdef __sun
+volatile hrtime_t timerInited;
+#endif
volatile unsigned int elapsed;
/*
@@ -54,15 +59,38 @@
{
elapsed++;
/* be sure to NOT call DBG in asynchronous handlers! */
+
+#ifdef __sun
+ /*
+ * Solaris removes the handler once signal is delivered. We
+ * need to set the handler again.
+ */
+ signal(SIGALRM, catch_alarm);
+#endif
}
-void
+/** Strings to help timer related debug messages. */
+char *PTP_timer_dbg_string[] =
+{"PDELAYREQ_INTERVAL_TIMER",
+ "DELAYREQ_INTERVAL_TIMER",
+ "SYNC_INTERVAL_TIMER",
+ "ANNOUNCE_RECEIPT_TIMER",
+ "ANNOUNCE_INTERVAL_TIMER",
+
+ /* non-spec timers */
+ "OPERATOR_MESSAGES_TIMER",
+ "LEAP_SECOND_PENDING_TIMER",
+ "LEAP_SECOND_NOW_TIMER"
+};
+
+void
initTimer(void)
{
- struct itimerval itimer;
DBG("initTimer\n");
+#if !defined(__sun)
+ struct itimerval itimer;
signal(SIGALRM, SIG_IGN);
elapsed = 0;
@@ -71,6 +99,10 @@
signal(SIGALRM, catch_alarm);
setitimer(ITIMER_REAL, &itimer, 0);
+#else
+ timerInited = gethrtime();
+#endif
+
}
void
@@ -83,8 +115,18 @@
* latch how many ticks we got since we were last called
* remember that catch_alarm() is totally asynchronous to this timerUpdate()
*/
+#ifdef __sun
+ /*
+ * We want to have the number of 'ticks' (each equal to
+ * US_TIMER_INTERVAL microseconds) since last call assigned to
+ * 'delta'.
+ */
+ delta = (gethrtime() - timerInited)/(US_TIMER_INTERVAL * 1000);
+ timerInited = gethrtime();
+#else
delta = elapsed;
elapsed = 0;
+#endif
if (delta <= 0)
return;
@@ -100,7 +142,8 @@
<= 0) {
itimer[i].left = itimer[i].interval;
itimer[i].expire = TRUE;
- DBG2("TimerUpdate: Timer %u has now expired. (Re-armed again with interval %d, left %d)\n", i, itimer[i].interval, itimer[i].left );
+ DBG2("TimerUpdate: Timer %u(%s) has now expired. (Re-armed again with interval %d, left %d)\n",
+ i, PTP_timer_dbg_string[i], itimer[i].interval, itimer[i].left );
}
}
@@ -113,7 +156,8 @@
return;
itimer[index].interval = 0;
- DBG2("timerStop: Stopping timer %d. (New interval: %d; New left: %d)\n", index, itimer[index].left , itimer[index].interval);
+ DBG2("timerStop: Stopping timer %d(%s). (New interval: %d; New left: %d)\n",\
+ index, PTP_timer_dbg_string[index], itimer[index].left , itimer[index].interval);
}
void
@@ -156,12 +200,13 @@
DBG("Timer would be issued immediatly. Please raise dep/timer.c:US_TIMER_INTERVAL to hold %.2fs\n",
interval
);
-
+
}
}
itimer[index].interval = itimer[index].left;
- DBG2("timerStart: Set timer %d to %f. New interval: %d; new left: %d\n", index, interval, itimer[index].left , itimer[index].interval);
+ DBG2("timerStart: Set timer %d(%s) to %f. New interval: %d; new left: %d\n",\
+ index, PTP_timer_dbg_string[index], interval, itimer[index].left , itimer[index].interval);
}
@@ -173,21 +218,22 @@
* PTPv1 algorithm was:
* ptpClock->R = getRand(&ptpClock->random_seed) % (PTP_DELAY_REQ_INTERVAL - 2) + 2;
* R is the number of Syncs to be received, before sending a new request
- *
- */
+ *
+ */
void timerStart_random(UInteger16 index, float interval, IntervalTimer * itimer)
{
float new_value;
new_value = getRand() * interval * 2.0;
- DBG2(" timerStart_random: requested %.2f, got %.2f\n", interval, new_value);
-
+ DBG2(" timerStart_random: index %d(%s) requested %.2f, got %.2f\n",
+ index, PTP_timer_dbg_string[index], interval, new_value);
+
timerStart(index, new_value, itimer);
}
-Boolean
+Boolean
timerExpired(UInteger16 index, IntervalTimer * itimer)
{
timerUpdate(itimer);
@@ -201,7 +247,28 @@
itimer[index].expire = FALSE;
- DBG2("timerExpired: Timer %d expired, taking actions. current interval: %d; current left: %d\n", index, itimer[index].left , itimer[index].interval);
+ DBG2("timerExpired: Timer %d(%s) expired, taking actions. current interval: %d; current left: %d\n",\
+ index, PTP_timer_dbg_string[index], itimer[index].left , itimer[index].interval);
return TRUE;
}
+
+void timerNow(TimeInternal *time)
+{
+#if defined(__APPLE__)
+ struct timeval tv;
+
+ gettimeofday(&tv, 0);
+ time->seconds = tv.tv_sec;
+ time->nanoseconds = tv.tv_usec*1000;
+#else
+ struct timespec tv;
+ int err = clock_gettime(CLOCK_REALTIME, &tv);
+ if (err != 0) {
+ ERROR("error getting time %d\n", err);
+ return;
+ }
+ time->seconds = tv.tv_sec;
+ time->nanoseconds = tv.tv_nsec;
+#endif
+}
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/display.c
--- a/src/display.c Tue May 14 17:07:59 2013 -0700
+++ b/src/display.c Sun Oct 27 22:49:29 2013 -0700
@@ -1,4 +1,6 @@
/*-
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
* Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
* Martin Burnicki, Gael Mace, Alexandre Van Kempen
* Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
@@ -105,7 +107,7 @@
netPath_display(NetPath * net)
{
struct in_addr addr;
-
+ DBGV(">>>Network Info:\n");
DBGV("eventSock : %d \n", net->eventSock);
DBGV("generalSock : %d \n", net->generalSock);
addr.s_addr = net->multicastAddr;
@@ -114,6 +116,7 @@
DBGV("peerMulticastAddress : %s \n", inet_ntoa(addr));
addr.s_addr = net->unicastAddr;
DBGV("unicastAddress : %s \n", inet_ntoa(addr));
+ DBGV("<<<\n");
}
/**\brief Display a IntervalTimer Structure*/
@@ -201,27 +204,27 @@
void
msgHeader_display(MsgHeader * header)
{
- DBGV("Message header : \n");
+ DBGV(">>>Message header : \n");
DBGV("\n");
- DBGV("transportSpecific : %d\n", header->transportSpecific);
- DBGV("messageType : %d\n", header->messageType);
- DBGV("versionPTP : %d\n", header->versionPTP);
- DBGV("messageLength : %d\n", header->messageLength);
- DBGV("domainNumber : %d\n", header->domainNumber);
- DBGV("FlagField %02hhx:%02hhx\n", header->flagField[0], header->flagField[1]);
+ DBGV("\ttransportSpecific : %d\n", header->transportSpecific);
+ DBGV("\tmessageType : %d\n", header->messageType);
+ DBGV("\tversionPTP : %d\n", header->versionPTP);
+ DBGV("\tmessageLength : %d\n", header->messageLength);
+ DBGV("\tdomainNumber : %d\n", header->domainNumber);
+ DBGV("\tFlagField %02hhx:%02hhx\n", header->flagField[0], header->flagField[1]);
integer64_display(&header->correctionfield);
portIdentity_display(&header->sourcePortIdentity);
- DBGV("sequenceId : %d\n", header->sequenceId);
- DBGV("controlField : %d\n", header->controlField);
- DBGV("logMessageInterval : %d\n", header->logMessageInterval);
- DBGV("\n");
+ DBGV("\tsequenceId : %d\n", header->sequenceId);
+ DBGV("\tcontrolField : %d\n", header->controlField);
+ DBGV("\tlogMessageInterval : %d\n", header->logMessageInterval);
+ DBGV("<<<\n");
}
/**\brief Display Announce message*/
void
msgAnnounce_display(MsgAnnounce * announce)
{
- DBGV("Announce Message : \n");
+ DBGV(">>>Announce Message : \n");
DBGV("\n");
DBGV("originTimestamp : \n");
DBGV("secondField : \n");
@@ -235,7 +238,7 @@
clockIdentity_display(announce->grandmasterIdentity);
DBGV("stepsRemoved : %d \n", announce->stepsRemoved);
DBGV("timeSource : %d \n", announce->timeSource);
- DBGV("\n");
+ DBGV("<<<\n");
}
/**\brief Display Follow_UP message*/
@@ -292,8 +295,8 @@
DBGV("---Run time Options Display-- \n");
DBGV("\n");
- DBGV("announceInterval : %d \n", rtOpts->announceInterval);
- DBGV("syncInterval : %d \n", rtOpts->syncInterval);
+ DBGV("announceInterval : %Lf \n", rtOpts->announceInterval);
+ DBGV("syncInterval : %Lf \n", rtOpts->syncInterval);
clockQuality_display(&(rtOpts->clockQuality));
DBGV("priority1 : %d \n", rtOpts->priority1);
DBGV("priority2 : %d \n", rtOpts->priority2);
@@ -314,7 +317,7 @@
timeInternal_display(&(rtOpts->outboundLatency));
DBGV("max_foreign_records : %d \n", rtOpts->max_foreign_records);
DBGV("ethernet mode : %d \n", rtOpts->ethernet_mode);
- DBGV("\n");
+ DBGV("--- ---\n");
}
@@ -333,7 +336,7 @@
DBGV("priority2 : %d \n", ptpClock->priority2);
DBGV("domainNumber : %d \n", ptpClock->domainNumber);
DBGV("slaveOnly : %d \n", ptpClock->slaveOnly);
- DBGV("\n");
+ DBGV("-------\n");
}
@@ -350,7 +353,7 @@
timeInternal_display(&ptpClock->offsetFromMaster);
DBGV("Mean path delay : \n");
timeInternal_display(&ptpClock->meanPathDelay);
- DBGV("\n");
+ DBGV("---------\n");
}
@@ -371,7 +374,7 @@
clockQuality_display(&ptpClock->grandmasterClockQuality);
DBGV("grandmasterpriority1 : %d \n", ptpClock->grandmasterPriority1);
DBGV("grandmasterpriority2 : %d \n", ptpClock->grandmasterPriority2);
- DBGV("\n");
+ DBGV("---------\n");
}
/**\brief Display Global data set of a PtpClock*/
@@ -390,7 +393,7 @@
DBGV("frequencyTraceable : %d \n", ptpClock->frequencyTraceable);
DBGV("ptpTimescale : %d \n", ptpClock->ptpTimescale);
DBGV("timeSource : %d \n", ptpClock->timeSource);
- DBGV("\n");
+ DBGV("------\n");
}
/**\brief Display Port data set of a PtpClock*/
@@ -403,6 +406,7 @@
portIdentity_display(&ptpClock->portIdentity);
DBGV("port state : %d \n", ptpClock->portState);
+ DBGV("minDelayReqInterval : %Lf \n", ptpClock->minDelayReqInterval);
DBGV("logMinDelayReqInterval : %d \n", ptpClock->logMinDelayReqInterval);
DBGV("peerMeanPathDelay : \n");
timeInternal_display(&ptpClock->peerMeanPathDelay);
@@ -412,7 +416,7 @@
DBGV("delayMechanism : %d \n", ptpClock->delayMechanism);
DBGV("logMinPdelayReqInterval : %d \n", ptpClock->logMinPdelayReqInterval);
DBGV("versionNumber : %d \n", ptpClock->versionNumber);
- DBGV("\n");
+ DBGV("--------\n");
}
/**\brief Display ForeignMaster data set of a PtpClock*/
@@ -444,7 +448,7 @@
DBGV("No Foreign masters recorded \n");
}
- DBGV("\n");
+ DBGV("---- End of Ptp Clock Foreign Data Set ---\n");
}
@@ -506,7 +510,7 @@
DBGV("y : %d \n", ptpClock->owd_filt.y);
DBGV("s_exp : %d \n", ptpClock->owd_filt.s_exp);
DBGV("\n");
- DBGV("observed_drift : %d \n", ptpClock->observed_drift);
+ DBGV("observed_drift : %Lf \n", ptpClock->observed_drift);
DBGV("message activity %d \n", ptpClock->message_activity);
DBGV("\n");
@@ -519,7 +523,7 @@
netPath_display(&ptpClock->netPath);
DBGV("mCommunication technology %d \n", ptpClock->port_communication_technology);
clockUUID_display(ptpClock->port_uuid_field);
- DBGV("\n");
+ DBGV("---------\n");
}
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/protocol.c
--- a/src/protocol.c Tue May 14 17:07:59 2013 -0700
+++ b/src/protocol.c Sun Oct 27 22:49:29 2013 -0700
@@ -1,4 +1,6 @@
/*-
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
* Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
* Martin Burnicki, Gael Mace, Alexandre Van Kempen
* Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
@@ -30,10 +32,10 @@
/**
* @file protocol.c
* @date Wed Jun 23 09:40:39 2010
- *
+ *
* @brief The code that handles the IEEE-1588 protocol and state machine
- *
- *
+ *
+ *
*/
#include "ptpd.h"
@@ -73,7 +75,7 @@
checked for 'port_state'. the actions and events may or may not change
'port_state' by calling toState(), but once they are done we loop around
again and perform the actions required for the new 'port_state'. */
-void
+void
protocol(RunTimeOpts *rtOpts, PtpClock *ptpClock)
{
DBG("event POWERUP\n");
@@ -105,75 +107,83 @@
/* perform actions required when leaving 'port_state' and entering 'state' */
-void
+void
toState(UInteger8 state, RunTimeOpts *rtOpts, PtpClock *ptpClock)
{
ptpClock->message_activity = TRUE;
-
+
/* leaving state tasks */
switch (ptpClock->portState)
{
case PTP_MASTER:
- timerStop(SYNC_INTERVAL_TIMER, ptpClock->itimer);
+ timerStop(SYNC_INTERVAL_TIMER, ptpClock->itimer);
timerStop(ANNOUNCE_INTERVAL_TIMER, ptpClock->itimer);
- timerStop(PDELAYREQ_INTERVAL_TIMER, ptpClock->itimer);
+ timerStop(PDELAYREQ_INTERVAL_TIMER, ptpClock->itimer);
break;
-
+
case PTP_SLAVE:
timerStop(ANNOUNCE_RECEIPT_TIMER, ptpClock->itimer);
-
+
if (ptpClock->delayMechanism == E2E)
timerStop(DELAYREQ_INTERVAL_TIMER, ptpClock->itimer);
else if (ptpClock->delayMechanism == P2P)
timerStop(PDELAYREQ_INTERVAL_TIMER, ptpClock->itimer);
-
- initClock(rtOpts, ptpClock);
+
+ /* bug 25796 - Don't reset the servo when entering or leaving the slave state.
+ * Instead we assume let the servo continue to work. If the time on a the next
+ * master is significantly different, this will cause a servo reset and a time
+ * correction. Otherwise we will converge as normal.
+ * initClock(rtOpts, ptpClock);
+ */
break;
-
+
case PTP_PASSIVE:
timerStop(PDELAYREQ_INTERVAL_TIMER, ptpClock->itimer);
timerStop(ANNOUNCE_RECEIPT_TIMER, ptpClock->itimer);
break;
-
+
case PTP_LISTENING:
timerStop(ANNOUNCE_RECEIPT_TIMER, ptpClock->itimer);
break;
-
+
default:
break;
}
-
+
/* entering state tasks */
/*
* No need of PRE_MASTER state because of only ordinary clock
* implementation.
*/
-
+
switch (state)
{
case PTP_INITIALIZING:
DBG("state PTP_INITIALIZING\n");
+ INFO("state -> initialising\n");
ptpClock->portState = PTP_INITIALIZING;
break;
-
+
case PTP_FAULTY:
DBG("state PTP_FAULTY\n");
+ INFO("state -> faulty\n");
ptpClock->portState = PTP_FAULTY;
break;
-
+
case PTP_DISABLED:
DBG("state PTP_DISABLED\n");
+ INFO("state -> disabled\n");
ptpClock->portState = PTP_DISABLED;
break;
-
+
case PTP_LISTENING:
/* in Listening mode, make sure we don't send anything. Instead we just expect/wait for announces (started below) */
timerStop(SYNC_INTERVAL_TIMER, ptpClock->itimer);
timerStop(ANNOUNCE_INTERVAL_TIMER, ptpClock->itimer);
timerStop(PDELAYREQ_INTERVAL_TIMER, ptpClock->itimer);
timerStop(DELAYREQ_INTERVAL_TIMER, ptpClock->itimer);
-
+
/*
* Count how many _unique_ timeouts happen to us.
* If we were already in Listen mode, then do not count this as a seperate reset, but stil do a new IGMP refresh
@@ -183,51 +193,52 @@
}
/* Revert to the original DelayReq interval, and ignore the one for the last master */
- ptpClock->logMinDelayReqInterval = rtOpts->initial_delayreq;
+ ptpClock->minDelayReqInterval = rtOpts->initial_delayreq;
+ ptpClock->logMinDelayReqInterval = log2IntegerSaturateAtZero(rtOpts->initial_delayreq);
/* force a IGMP refresh per reset */
if (rtOpts->do_IGMP_refresh) {
netRefreshIGMP(&ptpClock->netPath, rtOpts, ptpClock);
}
-
+
DBG("state PTP_LISTENING\n");
- INFO(" now in state PTP_LISTENING\n");
- timerStart(ANNOUNCE_RECEIPT_TIMER,
+ INFO("state -> listening\n");
+ timerStart(ANNOUNCE_RECEIPT_TIMER,
(ptpClock->announceReceiptTimeout) *
- (pow(2,ptpClock->logAnnounceInterval)),
+ rtOpts->announceInterval,
ptpClock->itimer);
ptpClock->portState = PTP_LISTENING;
break;
case PTP_MASTER:
DBG("state PTP_MASTER\n");
- INFO(" now in state PTP_MASTER\n");
-
+ INFO("state -> master\n");
+
timerStart(SYNC_INTERVAL_TIMER,
- pow(2,ptpClock->logSyncInterval), ptpClock->itimer);
- DBG("SYNC INTERVAL TIMER : %f \n",
- pow(2,ptpClock->logSyncInterval));
+ rtOpts->syncInterval, ptpClock->itimer);
+ DBG("SYNC INTERVAL TIMER : %Lf \n",
+ rtOpts->syncInterval);
timerStart(ANNOUNCE_INTERVAL_TIMER,
- pow(2,ptpClock->logAnnounceInterval),
- ptpClock->itimer);
- timerStart(PDELAYREQ_INTERVAL_TIMER,
- pow(2,ptpClock->logMinPdelayReqInterval),
+ rtOpts->announceInterval,
+ ptpClock->itimer);
+ timerStart(PDELAYREQ_INTERVAL_TIMER,
+ pow(2,ptpClock->logMinPdelayReqInterval),
ptpClock->itimer);
ptpClock->portState = PTP_MASTER;
break;
case PTP_PASSIVE:
DBG("state PTP_PASSIVE\n");
- INFO(" now in state PTP_PASSIVE\n");
+ INFO("state -> passive\n");
-
- timerStart(PDELAYREQ_INTERVAL_TIMER,
- pow(2,ptpClock->logMinPdelayReqInterval),
+
+ timerStart(PDELAYREQ_INTERVAL_TIMER,
+ pow(2,ptpClock->logMinPdelayReqInterval),
ptpClock->itimer);
- timerStart(ANNOUNCE_RECEIPT_TIMER,
+ timerStart(ANNOUNCE_RECEIPT_TIMER,
(ptpClock->announceReceiptTimeout) *
- (pow(2,ptpClock->logAnnounceInterval)),
+ rtOpts->announceInterval,
ptpClock->itimer);
ptpClock->portState = PTP_PASSIVE;
p1(ptpClock, rtOpts);
@@ -235,15 +246,21 @@
case PTP_UNCALIBRATED:
DBG("state PTP_UNCALIBRATED\n");
+ INFO("state -> uncalibrated\n");
ptpClock->portState = PTP_UNCALIBRATED;
break;
case PTP_SLAVE:
DBG("state PTP_SLAVE\n");
- INFO(" now in state PTP_SLAVE\n");
-
- initClock(rtOpts, ptpClock);
-
+ INFO("state -> slave\n");
+
+ /* bug 25796 - Don't reset the servo when entering or leaving the slave state.
+ * Instead we assume let the servo continue to work. If the time on a the next
+ * master is significantly different, this will cause a servo reset and a time
+ * correction. Otherwise we will converge as normal.
+ * initClock(rtOpts, ptpClock);
+ */
+
ptpClock->waitingForFollow = FALSE;
ptpClock->waitingForDelayResp = FALSE;
@@ -252,16 +269,16 @@
clearTime(&ptpClock->pdelay_req_receive_time);
clearTime(&ptpClock->pdelay_resp_send_time);
clearTime(&ptpClock->pdelay_resp_receive_time);
-
+
timerStart(OPERATOR_MESSAGES_TIMER,
OPERATOR_MESSAGES_INTERVAL,
ptpClock->itimer);
timerStart(ANNOUNCE_RECEIPT_TIMER,
(ptpClock->announceReceiptTimeout) *
- (pow(2,ptpClock->logAnnounceInterval)),
+ rtOpts->announceInterval,
ptpClock->itimer);
-
+
/*
* Previously, this state transition would start the delayreq timer immediately.
* However, if this was faster than the first received sync, then the servo would drop the delayResp
@@ -271,6 +288,24 @@
ptpClock->waiting_for_first_delayresp = TRUE;
ptpClock->portState = PTP_SLAVE;
+
+#if !defined(__APPLE__)
+
+ /*
+ * leap second pending in kernel but no leap second
+ * info from GM - withdraw kernel leap second
+ * if the flags have disappeared but we're past
+ * leap second event, do nothing - kernel flags
+ * will be unset in handleAnnounce()
+ */
+ if((!ptpClock->leap59 && !ptpClock->leap61) &&
+ !ptpClock->leapSecondInProgress &&
+ (checkTimexFlags(STA_INS) || checkTimexFlags(STA_DEL))) {
+ WARNING("=== Leap second pending in kernel but not on "
+ "GM: aborting kernel leap second\n");
+ unsetTimexFlags(STA_INS | STA_DEL, TRUE);
+ }
+#endif /* apple */
break;
default:
@@ -283,11 +318,12 @@
}
-Boolean
+Boolean
doInit(RunTimeOpts *rtOpts, PtpClock *ptpClock)
{
+ NOTIFY("ptpd version: %s\n", PTP_VERSION_STRING);
DBG("manufacturerIdentity: %s\n", MANUFACTURER_ID);
-
+
/* initialize networking */
netShutdown(&ptpClock->netPath);
if (!netInit(&ptpClock->netPath, rtOpts, ptpClock)) {
@@ -295,27 +331,36 @@
toState(PTP_FAULTY, rtOpts, ptpClock);
return FALSE;
}
-
+
+ /* initialize other stuff, including HW if needed */
+ shutdownTime(ptpClock);
+ if(!initTime(rtOpts, ptpClock))
+ {
+ ERROR("failed to initialize timing\n");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ return FALSE;
+ }
+
/* initialize other stuff */
initData(rtOpts, ptpClock);
initTimer();
initClock(rtOpts, ptpClock);
m1(rtOpts, ptpClock );
- msgPackHeader(ptpClock->msgObuf, ptpClock);
-
+ msgPackHeader(ptpClock->msgObuf, ptpClock, SYNC);
+
toState(PTP_LISTENING, rtOpts, ptpClock);
-
+
return TRUE;
}
/* handle actions and events for 'port_state' */
-void
+void
doState(RunTimeOpts *rtOpts, PtpClock *ptpClock)
{
UInteger8 state;
-
+
ptpClock->message_activity = FALSE;
-
+
/* Process record_update (BMC algorithm) before everything else */
switch (ptpClock->portState)
{
@@ -335,11 +380,11 @@
toState(state, rtOpts, ptpClock);
}
break;
-
+
default:
break;
}
-
+
switch (ptpClock->portState)
{
@@ -348,14 +393,26 @@
DBG("event FAULT_CLEARED\n");
toState(PTP_INITIALIZING, rtOpts, ptpClock);
return;
-
+
case PTP_LISTENING:
case PTP_UNCALIBRATED:
case PTP_SLAVE:
// passive mode behaves like the SLAVE state, in order to wait for the announce timeout of the current active master
case PTP_PASSIVE:
+
+ /* If we are in time both mode sync the system and local NIC. This process will continue
+ * even if we lose contact with the master. Note that we only do this if ptpd was
+ * started in a master mode or the primary servo has updated the clock at least once.
+ * bug 26688 This avoids the scenario during slave start up where we start syncing the
+ * system time to the NIC time before the NIC is synced to the master.
+ */
+ if ((rtOpts->time_mode == TIME_BOTH) &&
+ ((rtOpts->master_slave_mode != PTP_MODE_SLAVE) || ptpClock->clock_first_updated)) {
+ syncSystemWithNIC(rtOpts, ptpClock);
+ }
+
handle(rtOpts, ptpClock);
-
+
/*
* handle SLAVE timers:
* - No Announce message was received
@@ -367,7 +424,7 @@
ptpClock->number_foreign_records = 0;
ptpClock->foreign_record_i = 0;
- if(!ptpClock->slaveOnly &&
+ if(rtOpts->master_slave_mode != PTP_MODE_SLAVE &&
ptpClock->clockQuality.clockClass != 255) {
m1(rtOpts,ptpClock);
toState(PTP_MASTER, rtOpts, ptpClock);
@@ -380,7 +437,7 @@
toState(PTP_LISTENING, rtOpts, ptpClock);
}
}
-
+
if (timerExpired(OPERATOR_MESSAGES_TIMER, ptpClock->itimer)) {
reset_operator_messages(rtOpts, ptpClock);
}
@@ -396,32 +453,113 @@
if (timerExpired(PDELAYREQ_INTERVAL_TIMER,
ptpClock->itimer)) {
DBGV("event PDELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n");
- issuePDelayReq(rtOpts,ptpClock);
+ issuePDelayReq(rtOpts,ptpClock);
}
/* FIXME: Path delay should also rearm its timer with the value received from the Master */
}
+
+ /* XXX wowczarek: handle leap second timers */
+ if (ptpClock->leap59 || ptpClock->leap61)
+ DBGV("seconds to midnight: %f\n", secondsToMidnight());
+
+ if(timerExpired(LEAP_SECOND_PENDING_TIMER,ptpClock->itimer)) {
+ /* leap second period is over */
+ if(ptpClock->leapSecondInProgress) {
+ /*
+ * do not unpause offset calculation just
+ * yet, just indicate and it will be
+ * unpaused in handleAnnounce
+ */
+ ptpClock->leapSecondPending = FALSE;
+ timerStop(LEAP_SECOND_PENDING_TIMER,ptpClock->itimer);
+ /* leap second period has just started */
+ } else if(ptpClock->leapSecondPending) {
+ WARNING("=== Leap second event imminent - pausing "
+ "offset updates\n");
+ ptpClock->leapSecondInProgress = TRUE;
+#if !defined(__APPLE__)
+ if (!rtOpts->noResetClock && !rtOpts->resetClockStartupOnly &&
+ !checkTimexFlags(ptpClock->leap61 ? STA_INS : STA_DEL)) {
+ WARNING("=== Kernel leap second flags have "
+ "been unset - attempting to set "
+ "again");
+ setTimexFlags(ptpClock->leap61 ?
+ STA_INS : STA_DEL, FALSE);
+ }
+#endif /* apple */
+ timerStart(LEAP_SECOND_PENDING_TIMER,
+ getPauseBeforeMidnight(ptpClock->logAnnounceInterval) +
+ getPauseAfterMidnight(ptpClock->logAnnounceInterval),
+ ptpClock->itimer);
+
+ if (ptpClock->leap61) {
+ timerStart(LEAP_SECOND_NOW_TIMER,
+ secondsToMidnight(),
+ ptpClock->itimer);
+ } else {
+ timerStart(LEAP_SECOND_NOW_TIMER,
+ secondsToMidnight() - 1.0,
+ ptpClock->itimer);
+ }
+ }
+ }
+
+ if(timerExpired(LEAP_SECOND_NOW_TIMER,ptpClock->itimer)) {
+ if (ptpClock->leap61) {
+ WARNING("=== Leap second added!\n");
+ adjTimeInsertLeapSecond(rtOpts->time_mode, rtOpts, ptpClock);
+ } else {
+ WARNING("=== Leap second deleted!\n");
+ adjTimeDeleteLeapSecond(rtOpts->time_mode, rtOpts, ptpClock);
+ }
+ timerStop(LEAP_SECOND_NOW_TIMER,ptpClock->itimer);
+ }
break;
case PTP_MASTER:
+
+ /* If we are in time both mode, sync the system and local NIC. This process will continue
+ * irrespective of whether theere is communication with one or more slave nodes.
+ */
+ if (rtOpts->time_mode == TIME_BOTH) {
+ syncSystemWithNIC(rtOpts, ptpClock);
+ }
+
/*
- * handle SLAVE timers:
+ * handle MASTER timers:
* - Time to send new Sync
* - Time to send new Announce
* - Time to send new PathDelay
* (DelayResp has no timer - as these are sent and retransmitted by the slaves)
*/
-
+
+#ifdef DBG_SIGRTMIN_LEAP_SECOND
+ if(timerExpired(LEAP_SECOND_NOW_TIMER, ptpClock->itimer)) {
+ /* If we are signalling a leap second, apply the adjustment
+ * and cancel the signalling.
+ */
+ if (ptpClock->leap61)
+ ptpClock->currentUtcOffset++;
+ if (ptpClock->leap59)
+ ptpClock->currentUtcOffset--;
+ INFO("Setting UTC offset to %d\n", ptpClock->currentUtcOffset);
+ ptpClock->leap61 = FALSE;
+ ptpClock->leap59 = FALSE;
+ timerStop(LEAP_SECOND_NOW_TIMER, ptpClock->itimer);
+ }
+#endif
+
if (timerExpired(SYNC_INTERVAL_TIMER, ptpClock->itimer)) {
DBGV("event SYNC_INTERVAL_TIMEOUT_EXPIRES\n");
issueSync(rtOpts, ptpClock);
}
-
+
if (timerExpired(ANNOUNCE_INTERVAL_TIMER, ptpClock->itimer)) {
DBGV("event ANNOUNCE_INTERVAL_TIMEOUT_EXPIRES\n");
issueAnnounce(rtOpts, ptpClock);
}
-
+
if (ptpClock->delayMechanism == P2P) {
if (timerExpired(PDELAYREQ_INTERVAL_TIMER,
ptpClock->itimer)) {
@@ -429,19 +567,20 @@
issuePDelayReq(rtOpts,ptpClock);
}
}
-
+
// TODO: why is handle() below expiretimer, while in slave is the opposite
handle(rtOpts, ptpClock);
-
- if (ptpClock->slaveOnly || ptpClock->clockQuality.clockClass == 255)
+
+ if (rtOpts->master_slave_mode == PTP_MODE_SLAVE ||
+ ptpClock->clockQuality.clockClass == 255)
toState(PTP_LISTENING, rtOpts, ptpClock);
-
+
break;
case PTP_DISABLED:
handle(rtOpts, ptpClock);
break;
-
+
default:
DBG("(doState) do unrecognized state\n");
break;
@@ -456,57 +595,54 @@
int ret;
ssize_t length;
Boolean isFromSelf;
+ Boolean isEvent;
+ Boolean isLoopedBackTx = FALSE;
TimeInternal time = { 0, 0 };
if (!ptpClock->message_activity) {
- ret = netSelect(0, &ptpClock->netPath);
- if (ret < 0) {
- PERROR("failed to poll sockets");
+ /* To support time both mode, we time-out after a second to allow the
+ * the system clock to be synced to the NIC clock. For other modes, this
+ * should have no effect on the behaviour. */
+ TimeInternal timeout;
+ double integerPart, fractionalPart;
+ fractionalPart = modf(rtOpts->system_time_update_interval, &integerPart);
+ timeout.seconds = (Integer32)integerPart;
+ timeout.nanoseconds = (Integer32)(fractionalPart * 1000000000.0);
+
+ ret = netSelect(&timeout, &ptpClock->netPath);
+ if(ret < 0) {
+ PERROR("failed to poll sockets\n");
toState(PTP_FAULTY, rtOpts, ptpClock);
return;
} else if (!ret) {
- /* DBGV("handle: nothing\n"); */
+ /* We timed out, return immediately. */
return;
}
/* else length > 0 */
}
- DBGV("handle: something\n");
+ /*DBGV("handle: something\n");*/
- /* TODO: this should be based on the select actual FDs (if(FD_ISSET(...)) */
- length = netRecvEvent(ptpClock->msgIbuf, &time, &ptpClock->netPath);
-
-
+ isEvent = TRUE;
+ length = netRecvEvent(ptpClock->msgIbuf, &time, ptpClock,
+ rtOpts,
+ &isLoopedBackTx);
if (length < 0) {
- PERROR("failed to receive on the event socket");
+ PERROR("failed to receive on the event socket\n");
toState(PTP_FAULTY, rtOpts, ptpClock);
return;
} else if (!length) {
+ isEvent = FALSE;
length = netRecvGeneral(ptpClock->msgIbuf, &time,
- &ptpClock->netPath);
+ ptpClock, rtOpts);
if (length < 0) {
- PERROR("failed to receive on the general socket");
+ PERROR("failed to receive on the general socket\n");
toState(PTP_FAULTY, rtOpts, ptpClock);
return;
} else if (!length)
return;
}
- /*
- * make sure we use the TAI to UTC offset specified, if the master is sending the UTC_VALID bit
- *
- *
- * On the slave, all timestamps that we handle here have been collected by our local clock (loopback+kernel-level timestamp)
- * This includes delayReq just send, and delayResp, when it arrives.
- *
- * these are then adjusted to the same timebase of the Master (+34 leap seconds, as of 2011)
- *
- */
- DBGV("__UTC_offset: %d %d \n", ptpClock->currentUtcOffsetValid, ptpClock->currentUtcOffset);
- if (ptpClock->currentUtcOffsetValid) {
- time.seconds += ptpClock->currentUtcOffset;
- }
-
ptpClock->message_activity = TRUE;
if (length < HEADER_LENGTH) {
@@ -517,6 +653,30 @@
msgUnpackHeader(ptpClock->msgIbuf, &ptpClock->msgTmpHeader);
+ if(isEvent && !isLoopedBackTx && (ptpClock->tsMethod == TS_METHOD_DRIVER_IOCTL)) {
+ /* query hardware for matching receive time stamp */
+ if(!getReceiveTime(&time,
+ ptpClock->msgTmpHeader.sourcePortIdentity.clockIdentity,
+ ptpClock->msgTmpHeader.sequenceId, rtOpts->time_mode, &ptpClock->netPath)) {
+ /*
+ * This doesn't apply to us as we can timestamp sync and delay requests:
+ * "Incoming packets without hardware time stamp cannot be ignored outright because
+ * a master might only be able to time stamp DelayReq packets; ignoring the Sync
+ * packets from another, better clock would break the clock selection protocol.
+ * Therefore set system time as fallback and decide below what to do."
+ * Instead, ignore the packet outright!
+ */
+ DBGV("*** message with no time stamp ***\n");
+ return;
+ }
+ }
+
+ /* make sure we use the TAI to UTC offset specified, if the master is sending the UTC_VALID bit */
+ DBG2("__UTC_offset: %d %d \n", ptpClock->currentUtcOffsetValid, ptpClock->currentUtcOffset);
+ if((rtOpts->master_slave_mode == PTP_MODE_SLAVE) && ptpClock->currentUtcOffsetValid){
+ time.seconds += ptpClock->currentUtcOffset;
+ }
+
if (ptpClock->msgTmpHeader.versionPTP != ptpClock->versionNumber) {
DBG2("ignore version %d message\n", ptpClock->msgTmpHeader.versionPTP);
return;
@@ -527,13 +687,13 @@
return;
}
- /*Spec 9.5.2.2*/
+ /*Spec 9.5.2.2*/
isFromSelf = (ptpClock->portIdentity.portNumber == ptpClock->msgTmpHeader.sourcePortIdentity.portNumber
&& !memcmp(ptpClock->msgTmpHeader.sourcePortIdentity.clockIdentity, ptpClock->portIdentity.clockIdentity, CLOCK_IDENTITY_LENGTH));
/*
* subtract the inbound latency adjustment if it is not a loop
- * back and the time stamp seems reasonable
+ * back and the time stamp seems reasonable
*/
if (!isFromSelf && time.seconds > 0)
subTime(&time, &time, &rtOpts->inboundLatency);
@@ -576,27 +736,27 @@
switch (ptpClock->msgTmpHeader.messageType)
{
case ANNOUNCE:
- handleAnnounce(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
+ handleAnnounce(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
length, isFromSelf, rtOpts, ptpClock);
break;
case SYNC:
- handleSync(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
+ handleSync(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
length, &time, isFromSelf, rtOpts, ptpClock);
break;
case FOLLOW_UP:
- handleFollowUp(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
+ handleFollowUp(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
length, isFromSelf, rtOpts, ptpClock);
break;
case DELAY_REQ:
- handleDelayReq(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
+ handleDelayReq(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
length, &time, isFromSelf, rtOpts, ptpClock);
break;
case PDELAY_REQ:
- handlePDelayReq(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
+ handlePDelayReq(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
length, &time, isFromSelf, rtOpts, ptpClock);
- break;
+ break;
case DELAY_RESP:
- handleDelayResp(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
+ handleDelayResp(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
length, isFromSelf, rtOpts, ptpClock);
break;
case PDELAY_RESP:
@@ -604,16 +764,16 @@
&time, length, isFromSelf, rtOpts, ptpClock);
break;
case PDELAY_RESP_FOLLOW_UP:
- handlePDelayRespFollowUp(&ptpClock->msgTmpHeader,
- ptpClock->msgIbuf, length,
+ handlePDelayRespFollowUp(&ptpClock->msgTmpHeader,
+ ptpClock->msgIbuf, length,
isFromSelf, rtOpts, ptpClock);
break;
case MANAGEMENT:
- handleManagement(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
+ handleManagement(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
length, isFromSelf, rtOpts, ptpClock);
break;
case SIGNALING:
- handleSignaling(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
+ handleSignaling(&ptpClock->msgTmpHeader, ptpClock->msgIbuf,
length, isFromSelf, rtOpts, ptpClock);
break;
default:
@@ -627,11 +787,11 @@
}
/*spec 9.5.3*/
-void
-handleAnnounce(MsgHeader *header, Octet *msgIbuf, ssize_t length,
+void
+handleAnnounce(MsgHeader *header, Octet *msgIbuf, ssize_t length,
Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock)
{
- Boolean isFromCurrentParent = FALSE;
+ Boolean isFromCurrentParent = FALSE;
DBGV("HandleAnnounce : Announce message received : \n");
@@ -650,40 +810,63 @@
case PTP_DISABLED:
DBG("Handleannounce : disregard\n");
return;
-
- case PTP_UNCALIBRATED:
+
+ case PTP_UNCALIBRATED:
case PTP_SLAVE:
if (isFromSelf) {
DBGV("HandleAnnounce : Ignore message from self \n");
return;
}
-
+
/*
* Valid announce message is received : BMC algorithm
- * will be executed
+ * will be executed
*/
ptpClock->record_update = TRUE;
isFromCurrentParent = !memcmp(
ptpClock->parentPortIdentity.clockIdentity,
header->sourcePortIdentity.clockIdentity,
- CLOCK_IDENTITY_LENGTH) &&
- (ptpClock->parentPortIdentity.portNumber ==
+ CLOCK_IDENTITY_LENGTH) &&
+ (ptpClock->parentPortIdentity.portNumber ==
header->sourcePortIdentity.portNumber);
-
+
switch (isFromCurrentParent) {
case TRUE:
msgUnpackAnnounce(ptpClock->msgIbuf,
&ptpClock->msgTmp.announce);
- /* update datasets (file bmc.c) */
+ /* If the leap second period is over (pending == FALSE,
+ * inProgress == TRUE), unpause offset calculation
+ * (received first announce after leap second)
+ * Note that we must be this before running the BMC
+ * algorithm as this can set leapSecondPending again.
+ */
+ if(ptpClock->leapSecondInProgress &&
+ !ptpClock->leapSecondPending) {
+ WARNING("=== Leap second event over - "
+ "resuming offset updates\n");
+ ptpClock->leapSecondInProgress = FALSE;
+ ptpClock->leap59 = FALSE;
+ ptpClock->leap61 = FALSE;
+ unsetTimexFlags(STA_INS | STA_DEL, TRUE);
+ }
+
+ /* call the BMC algorithm (file bmc.c) */
s1(header,&ptpClock->msgTmp.announce,ptpClock, rtOpts);
+
+ /* update current master in the fmr as well */
+ memcpy(&ptpClock->foreign[ptpClock->foreign_record_best].header,
+ header,sizeof(MsgHeader));
+ memcpy(&ptpClock->foreign[ptpClock->foreign_record_best].announce,
+ &ptpClock->msgTmp.announce,sizeof(MsgAnnounce));
+
DBG2("___ Announce: received Announce from current Master, so reset the Announce timer\n");
/*Reset Timer handling Announce receipt timeout*/
timerStart(ANNOUNCE_RECEIPT_TIMER,
(ptpClock->announceReceiptTimeout) *
- (pow(2,ptpClock->logAnnounceInterval)),
+ rtOpts->announceInterval,
ptpClock->itimer);
#ifdef PTP_EXPERIMENTAL
@@ -691,20 +874,20 @@
// todo: add this to bmc(), to cover the very first packet
ptpClock->MasterAddr = ptpClock->netPath.lastRecvAddr;
#endif
- break;
+ break;
case FALSE:
- /*addForeign takes care of AnnounceUnpacking*/
+ /*addForeign takes care of AnnounceUnpacking*/
/* the actual decision to change masters is only done in doState() / record_update == TRUE / bmc() */
-
+
/* the original code always called: addforeign(new master) + timerstart(announce) */
addForeign(ptpClock->msgIbuf,header,ptpClock);
- timerStart(ANNOUNCE_RECEIPT_TIMER,
+ timerStart(ANNOUNCE_RECEIPT_TIMER,
(ptpClock->announceReceiptTimeout) *
- (pow(2,ptpClock->logAnnounceInterval)),
+ rtOpts->announceInterval,
ptpClock->itimer);
- break;
+ break;
default:
DBG("HandleAnnounce : (isFromCurrentParent)"
@@ -718,7 +901,7 @@
* Passive case: previously, this was handled in the default, just like the master case.
* This the announce would call addForeign(), but NOT reset the timer, so after 12s it would expire and we would come alive periodically
*
- * This code is now merged with the slave case to reset the timer, and call addForeign() if it's a third master
+ * This code is now merged with the slave case to reset the timer, and call addForeign() if it's a third master
*
*/
case PTP_PASSIVE:
@@ -756,7 +939,7 @@
/*Reset Timer handling Announce receipt timeout*/
timerStart(ANNOUNCE_RECEIPT_TIMER,
(ptpClock->announceReceiptTimeout) *
- (pow(2,ptpClock->logAnnounceInterval)),
+ rtOpts->announceInterval,
ptpClock->itimer);
} else {
/*addForeign takes care of AnnounceUnpacking*/
@@ -769,27 +952,27 @@
}
break;
-
+
case PTP_MASTER:
- case PTP_LISTENING: /* listening mode still causes timeouts in order to send IGMP refreshes */
+ case PTP_LISTENING: /* listening mode still causes timeouts in order to send IGMP refreshes */
default :
- if (isFromSelf) {
+ if (isFromSelf) {
DBGV("HandleAnnounce : Ignore message from self \n");
return;
}
DBGV("Announce message from another foreign master\n");
addForeign(ptpClock->msgIbuf,header,ptpClock);
- ptpClock->record_update = TRUE; /* run BMC() as soon as possible */
+ ptpClock->record_update = TRUE; /* run BMC() as soon as possible */
break;
} /* switch on (port_state) */
}
-void
-handleSync(MsgHeader *header, Octet *msgIbuf, ssize_t length,
- TimeInternal *time, Boolean isFromSelf,
+void
+handleSync(MsgHeader *header, Octet *msgIbuf, ssize_t length,
+ TimeInternal *time, Boolean isFromSelf,
RunTimeOpts *rtOpts, PtpClock *ptpClock)
{
TimeInternal OriginTimestamp;
@@ -797,12 +980,12 @@
Boolean isFromCurrentParent = FALSE;
DBGV("Sync message received : \n");
-
+
if (length < SYNC_LENGTH) {
ERROR("short Sync message\n");
toState(PTP_FAULTY, rtOpts, ptpClock);
return;
- }
+ }
switch (ptpClock->portState) {
case PTP_INITIALIZING:
@@ -810,8 +993,8 @@
case PTP_DISABLED:
DBGV("HandleSync : disregard\n");
return;
-
- case PTP_UNCALIBRATED:
+
+ case PTP_UNCALIBRATED:
case PTP_SLAVE:
if (isFromSelf) {
DBGV("HandleSync: Ignore message from self \n");
@@ -820,27 +1003,27 @@
isFromCurrentParent =
!memcmp(ptpClock->parentPortIdentity.clockIdentity,
header->sourcePortIdentity.clockIdentity,
- CLOCK_IDENTITY_LENGTH) &&
- (ptpClock->parentPortIdentity.portNumber ==
+ CLOCK_IDENTITY_LENGTH) &&
+ (ptpClock->parentPortIdentity.portNumber ==
header->sourcePortIdentity.portNumber);
-
+
if (isFromCurrentParent) {
/* We only start our own delayReq timer after receiving the first sync */
if (ptpClock->waiting_for_first_sync) {
ptpClock->waiting_for_first_sync = FALSE;
NOTICE("Received first Sync from Master\n");
- NOTICE(" going to arm DelayReq timer for the first time, with initial rate: %d\n",
- ptpClock->logMinDelayReqInterval
+ NOTICE(" going to arm DelayReq timer for the first time, with initial rate: %Lf\n",
+ ptpClock->minDelayReqInterval
);
if (ptpClock->delayMechanism == E2E)
timerStart(DELAYREQ_INTERVAL_TIMER,
- pow(2,ptpClock->logMinDelayReqInterval),
- ptpClock->itimer);
+ ptpClock->minDelayReqInterval,
+ ptpClock->itimer);
else if (ptpClock->delayMechanism == P2P)
timerStart(PDELAYREQ_INTERVAL_TIMER,
- pow(2,ptpClock->logMinPdelayReqInterval),
- ptpClock->itimer);
+ pow(2,ptpClock->logMinPdelayReqInterval),
+ ptpClock->itimer);
}
ptpClock->sync_receive_time.seconds = time->seconds;
@@ -852,13 +1035,13 @@
DBG2("HandleSync: waiting for follow-up \n");
ptpClock->waitingForFollow = TRUE;
- ptpClock->recvSyncSequenceId =
+ ptpClock->recvSyncSequenceId =
header->sequenceId;
/*Save correctionField of Sync message*/
integer64_to_internalTime(
header->correctionfield,
&correctionField);
- ptpClock->lastSyncCorrectionField.seconds =
+ ptpClock->lastSyncCorrectionField.seconds =
ptpClock->lastSyncCorrectionField.nanoseconds =
@@ -895,9 +1078,9 @@
} if (ptpClock->twoStepFlag) {
DBGV("HandleSync: going to send followup message\n ");
- /*Add latency*/
- addTime(time,time,&rtOpts->outboundLatency);
- issueFollowup(time,rtOpts,ptpClock);
+ /*Add latency*/
+ addTime(time,time,&rtOpts->outboundLatency);
+ issueFollowup(time,rtOpts,ptpClock);
break;
} else {
DBGV("HandleSync: Sync message received from self\n ");
@@ -906,17 +1089,17 @@
}
-void
-handleFollowUp(MsgHeader *header, Octet *msgIbuf, ssize_t length,
+void
+handleFollowUp(MsgHeader *header, Octet *msgIbuf, ssize_t length,
Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock)
{
DBGV("Handlefollowup : Follow up message received \n");
-
+
TimeInternal preciseOriginTimestamp;
TimeInternal correctionField;
Boolean isFromCurrentParent = FALSE;
-
- if (length < FOLLOW_UP_LENGTH)
+
+ if(length < FOLLOW_UP_LENGTH)
{
ERROR("short Follow up message\n");
toState(PTP_FAULTY, rtOpts, ptpClock);
@@ -937,19 +1120,19 @@
case PTP_LISTENING:
DBGV("Handfollowup : disregard\n");
return;
-
- case PTP_UNCALIBRATED:
+
+ case PTP_UNCALIBRATED:
case PTP_SLAVE:
- isFromCurrentParent =
+ isFromCurrentParent =
!memcmp(ptpClock->parentPortIdentity.clockIdentity,
header->sourcePortIdentity.clockIdentity,
- CLOCK_IDENTITY_LENGTH) &&
- (ptpClock->parentPortIdentity.portNumber ==
+ CLOCK_IDENTITY_LENGTH) &&
+ (ptpClock->parentPortIdentity.portNumber ==
header->sourcePortIdentity.portNumber);
-
+
if (isFromCurrentParent) {
if (ptpClock->waitingForFollow) {
- if ((ptpClock->recvSyncSequenceId ==
+ if ((ptpClock->recvSyncSequenceId ==
header->sequenceId)) {
msgUnpackFollowUp(ptpClock->msgIbuf,
&ptpClock->msgTmp.follow);
@@ -979,6 +1162,7 @@
"message \n");
} else
DBG2("Ignored, Follow up message is not from current parent \n");
+ break;
case PTP_MASTER:
case PTP_PASSIVE:
@@ -994,13 +1178,13 @@
void
-handleDelayReq(MsgHeader *header, Octet *msgIbuf, ssize_t length,
+handleDelayReq(MsgHeader *header, Octet *msgIbuf, ssize_t length,
TimeInternal *time, Boolean isFromSelf,
RunTimeOpts *rtOpts, PtpClock *ptpClock)
{
if (ptpClock->delayMechanism == E2E) {
DBGV("delayReq message received : \n");
-
+
if (length < DELAY_REQ_LENGTH) {
ERROR("short DelayReq message\n");
toState(PTP_FAULTY, rtOpts, ptpClock);
@@ -1018,7 +1202,7 @@
return;
case PTP_SLAVE:
- if (isFromSelf) {
+ if (isFromSelf) {
DBG("==> Handle DelayReq (%d)\n",
header->sequenceId);
@@ -1062,14 +1246,14 @@
// remember IP address of this client for -U option
ptpClock->LastSlaveAddr = ptpClock->netPath.lastRecvAddr;
#endif
-
+
issueDelayResp(time,&ptpClock->delayReqHeader,
- rtOpts,ptpClock);
+ rtOpts,ptpClock);
break;
default:
DBG("do unrecognized state2\n");
- break;
+ break;
}
} else /* (Peer to Peer mode) */
ERROR("Delay messages are ignored in Peer to Peer mode\n");
@@ -1108,16 +1292,16 @@
if ((memcmp(ptpClock->parentPortIdentity.clockIdentity,
header->sourcePortIdentity.clockIdentity,
CLOCK_IDENTITY_LENGTH) == 0 ) &&
- (ptpClock->parentPortIdentity.portNumber ==
+ (ptpClock->parentPortIdentity.portNumber ==
header->sourcePortIdentity.portNumber))
isFromCurrentParent = TRUE;
-
+
if ((memcmp(ptpClock->portIdentity.clockIdentity,
CLOCK_IDENTITY_LENGTH) == 0) &&
- ((ptpClock->sentDelayReqSequenceId - 1)==
+ ((UInteger16)(ptpClock->sentDelayReqSequenceId - 1)==
header->sequenceId) &&
- (ptpClock->portIdentity.portNumber ==
+ (ptpClock->portIdentity.portNumber ==
&& isFromCurrentParent) {
DBG("==> Handle DelayResp (%d)\n",
@@ -1131,15 +1315,15 @@
toInternalTime(&requestReceiptTimestamp,
&ptpClock->msgTmp.resp.receiveTimestamp);
- ptpClock->delay_req_receive_time.seconds =
+ ptpClock->delay_req_receive_time.seconds =
- ptpClock->delay_req_receive_time.nanoseconds =
+ ptpClock->delay_req_receive_time.nanoseconds =
integer64_to_internalTime(
header->correctionfield,
&correctionField);
-
+
/*
send_time = delay_req_send_time (received as CMSG in handleEvent)
recv_time = requestReceiptTimestamp (received inside delayResp)
@@ -1150,7 +1334,7 @@
if (ptpClock->waiting_for_first_delayresp) {
ptpClock->waiting_for_first_delayresp = FALSE;
- NOTICE(" received first DelayResp from Master\n");
+ NOTICE(" received first DelayResp from Master\n");
}
if (rtOpts->ignore_delayreq_interval_master == 0) {
@@ -1160,18 +1344,19 @@
/* Accept new DelayReq value from the Master */
if (ptpClock->logMinDelayReqInterval != header->logMessageInterval) {
- NOTICE(" received new DelayReq frequency %d from Master (was: %d)\n",
+ NOTICE(" received new DelayReq frequency %d from Master (was: %d)\n",
header->logMessageInterval, ptpClock->logMinDelayReqInterval );
}
// collect new value indicated from the Master
ptpClock->logMinDelayReqInterval = header->logMessageInterval;
-
+ ptpClock->minDelayReqInterval = pow(2, header->logMessageInterval);
+
/* FIXME: the actual rearming of this timer with the new value only happens later in doState()/issueDelayReq() */
} else {
- if (ptpClock->logMinDelayReqInterval != rtOpts->subsequent_delayreq) {
+ if (ptpClock->minDelayReqInterval != rtOpts->subsequent_delayreq) {
NOTICE(" received new DelayReq frequency %d from command line (was: %d)\n",
- rtOpts->subsequent_delayreq, ptpClock->logMinDelayReqInterval);
+ rtOpts->subsequent_delayreq, ptpClock->minDelayReqInterval);
}
ptpClock->logMinDelayReqInterval = rtOpts->subsequent_delayreq;
}
@@ -1187,9 +1372,9 @@
}
-void
-handlePDelayReq(MsgHeader *header, Octet *msgIbuf, ssize_t length,
- TimeInternal *time, Boolean isFromSelf,
+void
+handlePDelayReq(MsgHeader *header, Octet *msgIbuf, ssize_t length,
+ TimeInternal *time, Boolean isFromSelf,
RunTimeOpts *rtOpts, PtpClock *ptpClock)
{
if (ptpClock->delayMechanism == P2P) {
@@ -1201,7 +1386,7 @@
return;
}
- switch (ptpClock->portState ) {
+ switch(ptpClock->portState ) {
case PTP_INITIALIZING:
case PTP_FAULTY:
case PTP_DISABLED:
@@ -1222,7 +1407,7 @@
time->seconds;
ptpClock->pdelay_req_send_time.nanoseconds =
time->nanoseconds;
-
+
/*Add latency*/
addTime(&ptpClock->pdelay_req_send_time,
&ptpClock->pdelay_req_send_time,
@@ -1230,33 +1415,33 @@
break;
} else {
msgUnpackHeader(ptpClock->msgIbuf,
- &ptpClock->PdelayReqHeader);
- issuePDelayResp(time, header, rtOpts,
- ptpClock);
+ &ptpClock->PdelayReqHeader);
+ issuePDelayResp(time, header, rtOpts,
+ ptpClock);
break;
}
- default:
- DBG("do unrecognized state3\n");
- break;
- }
- } else /* (End to End mode..) */
+ default:
+ DBG("do unrecognized state3\n");
+ break;
+ }
+ } else /* (End to End mode..) */
ERROR("Peer Delay messages are disregarded in End to End "
"mode \n");
}
void
handlePDelayResp(MsgHeader *header, Octet *msgIbuf, TimeInternal *time,
- ssize_t length, Boolean isFromSelf,
+ ssize_t length, Boolean isFromSelf,
RunTimeOpts *rtOpts, PtpClock *ptpClock)
{
if (ptpClock->delayMechanism == P2P) {
/* Boolean isFromCurrentParent = FALSE; NOTE: This is never used in this function */
TimeInternal requestReceiptTimestamp;
TimeInternal correctionField;
-
+
DBGV("PdelayResp message received : \n");
- if (length < PDELAY_RESP_LENGTH) {
+ if (length < PDELAY_RESP_LENGTH) {
ERROR("short PDelayResp message\n");
toState(PTP_FAULTY, rtOpts, ptpClock);
return;
@@ -1276,25 +1461,29 @@
if (ptpClock->twoStepFlag && isFromSelf) {
addTime(time,time,&rtOpts->outboundLatency);
issuePDelayRespFollowUp(time,
- &ptpClock->PdelayReqHeader,
- rtOpts,ptpClock);
+ &ptpClock->PdelayReqHeader,
+ rtOpts,ptpClock);
break;
}
msgUnpackPDelayResp(ptpClock->msgIbuf,
&ptpClock->msgTmp.presp);
-
+
#if 0 /* NOTE: This is never used in this function. Should it? */
isFromCurrentParent = !memcmp(ptpClock->parentPortIdentity.clockIdentity,
- header->sourcePortIdentity.clockIdentity,CLOCK_IDENTITY_LENGTH) &&
- (ptpClock->parentPortIdentity.portNumber ==
+ header->sourcePortIdentity.clockIdentity,CLOCK_IDENTITY_LENGTH) &&
+ (ptpClock->parentPortIdentity.portNumber ==
header->sourcePortIdentity.portNumber);
-#endif
- if (!((ptpClock->sentPDelayReqSequenceId ==
- header->sequenceId) &&
+#endif
+ /* TODO Not convinced that we will detect scenario where peer delay was transmitted but
+ * the retrieval of the timestamp failed. In theory, we should reject the response packet
+ * here because the sequence number doesn't match
+ */
+ if (!((ptpClock->sentPDelayReqSequenceId ==
+ header->sequenceId) &&
(!memcmp(ptpClock->portIdentity.clockIdentity,ptpClock->msgTmp.presp.requestingPortIdentity.clockIdentity,CLOCK_IDENTITY_LENGTH))
&& ( ptpClock->portIdentity.portNumber == ptpClock->msgTmp.presp.requestingPortIdentity.portNumber))) {
- /* Two Step Clock */
+ /* Two Step Clock */
if ((header->flagField[0] & PTP_TWO_STEP) == PTP_TWO_STEP) {
/*Store t4 (Fig 35)*/
ptpClock->pdelay_resp_receive_time.seconds = time->seconds;
@@ -1304,7 +1493,7 @@
&ptpClock->msgTmp.presp.requestReceiptTimestamp);
-
+
integer64_to_internalTime(header->correctionfield,&correctionField);
@@ -1314,7 +1503,7 @@
/*Store t4 (Fig 35)*/
ptpClock->pdelay_resp_receive_time.seconds = time->seconds;
ptpClock->pdelay_resp_receive_time.nanoseconds = time->nanoseconds;
-
+
integer64_to_internalTime(header->correctionfield,&correctionField);
updatePeerDelay (&ptpClock->owd_filt,rtOpts,ptpClock,&correctionField,FALSE);
break;
@@ -1337,23 +1526,23 @@
}
}
-void
-handlePDelayRespFollowUp(MsgHeader *header, Octet *msgIbuf, ssize_t length,
- Boolean isFromSelf, RunTimeOpts *rtOpts,
+void
+handlePDelayRespFollowUp(MsgHeader *header, Octet *msgIbuf, ssize_t length,
+ Boolean isFromSelf, RunTimeOpts *rtOpts,
PtpClock *ptpClock){
if (ptpClock->delayMechanism == P2P) {
TimeInternal responseOriginTimestamp;
TimeInternal correctionField;
-
+
DBGV("PdelayRespfollowup message received : \n");
-
+
if(length < PDELAY_RESP_FOLLOW_UP_LENGTH) {
ERROR("short PDelayRespfollowup message\n");
toState(PTP_FAULTY, rtOpts, ptpClock);
return;
- }
-
+ }
+
switch(ptpClock->portState) {
case PTP_INITIALIZING:
case PTP_FAULTY:
@@ -1361,29 +1550,29 @@
case PTP_UNCALIBRATED:
DBGV("HandlePdelayResp : disregard\n");
return;
-
+
case PTP_SLAVE:
case PTP_MASTER:
if ((header->sequenceId ==
- ptpClock->sentPDelayReqSequenceId-1) && (header->sequenceId == ptpClock->recvPDelayRespSequenceId)) {
+ (UInteger16)(ptpClock->sentPDelayReqSequenceId-1)) && (header->sequenceId == ptpClock->recvPDelayRespSequenceId)) {
msgUnpackPDelayRespFollowUp(
- ptpClock->msgIbuf,
- &ptpClock->msgTmp.prespfollow);
+ ptpClock->msgIbuf,
+ &ptpClock->msgTmp.prespfollow);
toInternalTime(
- &responseOriginTimestamp,
- &ptpClock->msgTmp.prespfollow.responseOriginTimestamp);
- ptpClock->pdelay_resp_send_time.seconds =
- ptpClock->pdelay_resp_send_time.nanoseconds =
+ &responseOriginTimestamp,
+ &ptpClock->msgTmp.prespfollow.responseOriginTimestamp);
+ ptpClock->pdelay_resp_send_time.seconds =
+ ptpClock->pdelay_resp_send_time.nanoseconds =
integer64_to_internalTime(
- ptpClock->msgTmpHeader.correctionfield,
- &correctionField);
+ ptpClock->msgTmpHeader.correctionfield,
+ &correctionField);
addTime(&correctionField,&correctionField,
- &ptpClock->lastPdelayRespCorrectionField);
+ &ptpClock->lastPdelayRespCorrectionField);
updatePeerDelay (&ptpClock->owd_filt,
- rtOpts, ptpClock,
- &correctionField,TRUE);
+ rtOpts, ptpClock,
+ &correctionField,TRUE);
break;
}
default:
@@ -1395,26 +1584,26 @@
}
}
-void
-handleManagement(MsgHeader *header, Octet *msgIbuf, ssize_t length,
+void
+handleManagement(MsgHeader *header, Octet *msgIbuf, ssize_t length,
Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock)
{}
-void
-handleSignaling(MsgHeader *header, Octet *msgIbuf, ssize_t length,
- Boolean isFromSelf, RunTimeOpts *rtOpts,
+void
+handleSignaling(MsgHeader *header, Octet *msgIbuf, ssize_t length,
+ Boolean isFromSelf, RunTimeOpts *rtOpts,
PtpClock *ptpClock)
{}
/*Pack and send on general multicast ip adress an Announce message*/
-void
+void
issueAnnounce(RunTimeOpts *rtOpts,PtpClock *ptpClock)
{
msgPackAnnounce(ptpClock->msgObuf,ptpClock);
- if (!netSendGeneral(ptpClock->msgObuf,ANNOUNCE_LENGTH,
- &ptpClock->netPath, 0)) {
+ if (netSendGeneral(ptpClock->msgObuf,ANNOUNCE_LENGTH,
+ &ptpClock->netPath, 0) != 0) {
toState(PTP_FAULTY,rtOpts,ptpClock);
DBGV("Announce message can't be sent -> FAULTY state \n");
} else {
@@ -1429,20 +1618,30 @@
void
issueSync(RunTimeOpts *rtOpts,PtpClock *ptpClock)
{
- Timestamp originTimestamp;
- TimeInternal internalTime;
- getTime(&internalTime);
- fromInternalTime(&internalTime,&originTimestamp);
+ Timestamp originTimestamp;
+ TimeInternal internalTime;
+ int rc;
+
+ getTime(&internalTime, rtOpts->time_mode, ptpClock);
+ fromInternalTime(&internalTime,&originTimestamp);
- msgPackSync(ptpClock->msgObuf,&originTimestamp,ptpClock);
+ msgPackSync(ptpClock->msgObuf,&originTimestamp,ptpClock);
- if (!netSendEvent(ptpClock->msgObuf,SYNC_LENGTH,&ptpClock->netPath, 0)) {
- toState(PTP_FAULTY,rtOpts,ptpClock);
- DBGV("Sync message can't be sent -> FAULTY state \n");
- } else {
- DBGV("Sync MSG sent ! \n");
- ptpClock->sentSyncSequenceId++;
- }
+ rc = netSendEvent(ptpClock->msgObuf,SYNC_LENGTH,ptpClock,rtOpts, 0);
+ if (rc != 0) {
+ /* If we failed because we didn't retrieve the transmit timestamp this is
+ * most likely because we've lost the network connection. PTPD will recover
+ * if the network connection is reestablished. In this case don't go into the
+ * faulty state as this does a complete re-initialisation.
+ */
+ if (rc != ENOTIMESTAMP) {
+ toState(PTP_FAULTY,rtOpts,ptpClock);
+ DBGV("Sync message can't be sent -> FAULTY state \n");
+ }
+ } else {
+ DBGV("Sync MSG sent ! \n");
+ ptpClock->sentSyncSequenceId++;
+ }
}
@@ -1452,11 +1651,11 @@
{
Timestamp preciseOriginTimestamp;
fromInternalTime(time,&preciseOriginTimestamp);
-
+
msgPackFollowUp(ptpClock->msgObuf,&preciseOriginTimestamp,ptpClock);
-
- if (!netSendGeneral(ptpClock->msgObuf,FOLLOW_UP_LENGTH,
- &ptpClock->netPath, 0)) {
+
+ if (netSendGeneral(ptpClock->msgObuf,FOLLOW_UP_LENGTH,
+ &ptpClock->netPath, 0) != 0) {
toState(PTP_FAULTY,rtOpts,ptpClock);
DBGV("FollowUp message can't be sent -> FAULTY state \n");
} else {
@@ -1471,11 +1670,12 @@
{
Timestamp originTimestamp;
TimeInternal internalTime;
+ int rc;
DBG("==> Issue DelayReq (%d)\n", ptpClock->sentDelayReqSequenceId );
/* call GTOD. This time is later replaced on handle_delayreq, to get the actual send timestamp from the OS */
- getTime(&internalTime);
+ getTime(&internalTime, rtOpts->time_mode, ptpClock);
fromInternalTime(&internalTime,&originTimestamp);
// uses current sentDelayReqSequenceId
@@ -1488,61 +1688,89 @@
}
#endif
- if (!netSendEvent(ptpClock->msgObuf,DELAY_REQ_LENGTH,
- &ptpClock->netPath, dst)) {
- toState(PTP_FAULTY,rtOpts,ptpClock);
- DBGV("delayReq message can't be sent -> FAULTY state \n");
- } else {
- DBGV("DelayReq MSG sent ! \n");
- ptpClock->sentDelayReqSequenceId++;
+ rc = netSendEvent(ptpClock->msgObuf, DELAY_REQ_LENGTH,
+ ptpClock,rtOpts, dst);
+ if (rc != 0) {
+ /* If we failed because we didn't retrieve the transmit timestamp this is
+ * most likely because we've lost the network connection. PTPD will recover
+ * if the network connection is reestablished. In this case don't go into the
+ * faulty state as this does a complete re-initialisation.
+ */
+ if (rc != ENOTIMESTAMP) {
+ toState(PTP_FAULTY,rtOpts,ptpClock);
+ DBGV("delayReq message can't be sent -> FAULTY state \n");
+ }
+ } else {
+ DBGV("DelayReq MSG sent ! \n");
+ ptpClock->sentDelayReqSequenceId++;
/* From now on, we will only accept delayreq and delayresp of (sentDelayReqSequenceId - 1) */
- /* Explicitelly re-arm timer for sending the next delayReq */
- timerStart_random(DELAYREQ_INTERVAL_TIMER,
- pow(2,ptpClock->logMinDelayReqInterval),
- ptpClock->itimer);
- }
+ /* Explicitelly re-arm timer for sending the next delayReq */
+ timerStart_random(DELAYREQ_INTERVAL_TIMER,
+ ptpClock->minDelayReqInterval,
+ ptpClock->itimer);
+ }
}
/*Pack and send on event multicast ip adress a PDelayReq message*/
void
issuePDelayReq(RunTimeOpts *rtOpts,PtpClock *ptpClock)
{
- Timestamp originTimestamp;
- TimeInternal internalTime;
- getTime(&internalTime);
- fromInternalTime(&internalTime,&originTimestamp);
-
- msgPackPDelayReq(ptpClock->msgObuf,&originTimestamp,ptpClock);
+ Timestamp originTimestamp;
+ TimeInternal internalTime;
+ int rc;
+
+ getTime(&internalTime, rtOpts->time_mode, ptpClock);
+ fromInternalTime(&internalTime,&originTimestamp);
- if (!netSendPeerEvent(ptpClock->msgObuf,PDELAY_REQ_LENGTH,
- &ptpClock->netPath)) {
- toState(PTP_FAULTY,rtOpts,ptpClock);
- DBGV("PdelayReq message can't be sent -> FAULTY state \n");
- } else {
- DBGV("PDelayReq MSG sent ! \n");
- ptpClock->sentPDelayReqSequenceId++;
- }
+ msgPackPDelayReq(ptpClock->msgObuf,&originTimestamp,ptpClock);
+
+ rc = netSendPeerEvent(ptpClock->msgObuf,PDELAY_REQ_LENGTH,
+ ptpClock, rtOpts);
+ if (rc != 0) {
+ /* If we failed because we didn't retrieve the transmit timestamp this is
+ * most likely because we've lost the network connection. PTPD will recover
+ * if the network connection is reestablished. In this case don't go into the
+ * faulty state as this does a complete re-initialisation.
+ */
+ if (rc != ENOTIMESTAMP) {
+ toState(PTP_FAULTY,rtOpts,ptpClock);
+ DBGV("PdelayReq message can't be sent -> FAULTY state \n");
+ }
+ } else {
+ DBGV("PDelayReq MSG sent ! \n");
+ ptpClock->sentPDelayReqSequenceId++;
+ }
}
/*Pack and send on event multicast ip adress a PDelayResp message*/
void
issuePDelayResp(TimeInternal *time,MsgHeader *header,RunTimeOpts *rtOpts,
- PtpClock *ptpClock)
+ PtpClock *ptpClock)
{
- Timestamp requestReceiptTimestamp;
- fromInternalTime(time,&requestReceiptTimestamp);
- msgPackPDelayResp(ptpClock->msgObuf,header,
- &requestReceiptTimestamp,ptpClock);
+ Timestamp requestReceiptTimestamp;
+ fromInternalTime(time,&requestReceiptTimestamp);
+ int rc;
+
+ msgPackPDelayResp(ptpClock->msgObuf,header,
+ &requestReceiptTimestamp,ptpClock);
- if (!netSendPeerEvent(ptpClock->msgObuf,PDELAY_RESP_LENGTH,
- &ptpClock->netPath)) {
- toState(PTP_FAULTY,rtOpts,ptpClock);
- DBGV("PdelayResp message can't be sent -> FAULTY state \n");
- } else {
- DBGV("PDelayResp MSG sent ! \n");
- }
+ rc = netSendPeerEvent(ptpClock->msgObuf,PDELAY_RESP_LENGTH,
+ ptpClock, rtOpts);
+ if (rc != 0) {
+ /* If we failed because we didn't retrieve the transmit timestamp this is
+ * most likely because we've lost the network connection. PTPD will recover
+ * if the network connection is reestablished. In this case don't go into the
+ * faulty state as this does a complete re-initialisation.
+ */
+ if (rc != ENOTIMESTAMP) {
+ toState(PTP_FAULTY,rtOpts,ptpClock);
+ DBGV("PdelayResp message can't be sent -> FAULTY state \n");
+ }
+ } else {
+ DBGV("PDelayResp MSG sent ! \n");
+ }
}
@@ -1562,8 +1790,8 @@
}
#endif
- if (!netSendGeneral(ptpClock->msgObuf,PDELAY_RESP_LENGTH,
- &ptpClock->netPath, dst)) {
+ if (netSendGeneral(ptpClock->msgObuf,PDELAY_RESP_LENGTH,
+ &ptpClock->netPath, dst) != 0) {
toState(PTP_FAULTY,rtOpts,ptpClock);
DBGV("delayResp message can't be sent -> FAULTY state \n");
} else {
@@ -1574,73 +1802,73 @@
void
issuePDelayRespFollowUp(TimeInternal *time, MsgHeader *header,
- RunTimeOpts *rtOpts, PtpClock *ptpClock)
+ RunTimeOpts *rtOpts, PtpClock *ptpClock)
{
- Timestamp responseOriginTimestamp;
- fromInternalTime(time,&responseOriginTimestamp);
+ Timestamp responseOriginTimestamp;
+ fromInternalTime(time,&responseOriginTimestamp);
- msgPackPDelayRespFollowUp(ptpClock->msgObuf,header,
- &responseOriginTimestamp,ptpClock);
+ msgPackPDelayRespFollowUp(ptpClock->msgObuf,header,
+ &responseOriginTimestamp,ptpClock);
- if (!netSendPeerGeneral(ptpClock->msgObuf,
- PDELAY_RESP_FOLLOW_UP_LENGTH,
- &ptpClock->netPath)) {
- toState(PTP_FAULTY,rtOpts,ptpClock);
- DBGV("PdelayRespFollowUp message can't be sent -> FAULTY state \n");
- } else {
- DBGV("PDelayRespFollowUp MSG sent ! \n");
- }
+ if (netSendPeerGeneral(ptpClock->msgObuf,
+ PDELAY_RESP_FOLLOW_UP_LENGTH,
+ &ptpClock->netPath) != 0) {
+ toState(PTP_FAULTY,rtOpts,ptpClock);
+ DBGV("PdelayRespFollowUp message can't be sent -> FAULTY state \n");
+ } else {
+ DBGV("PDelayRespFollowUp MSG sent ! \n");
+ }
}
-void
+void
issueManagement(MsgHeader *header,MsgManagement *manage,RunTimeOpts *rtOpts,
PtpClock *ptpClock)
{}
-void
+void
addForeign(Octet *buf,MsgHeader *header,PtpClock *ptpClock)
{
int i,j;
Boolean found = FALSE;
j = ptpClock->foreign_record_best;
-
+
/*Check if Foreign master is already known*/
for (i=0;i<ptpClock->number_foreign_records;i++) {
if (!memcmp(header->sourcePortIdentity.clockIdentity,
ptpClock->foreign[j].foreignMasterPortIdentity.clockIdentity,
- CLOCK_IDENTITY_LENGTH) &&
- (header->sourcePortIdentity.portNumber ==
+ CLOCK_IDENTITY_LENGTH) &&
+ (header->sourcePortIdentity.portNumber ==
ptpClock->foreign[j].foreignMasterPortIdentity.portNumber))
{
/*Foreign Master is already in Foreignmaster data set*/
- ptpClock->foreign[j].foreignMasterAnnounceMessages++;
+ ptpClock->foreign[j].foreignMasterAnnounceMessages++;
found = TRUE;
DBGV("addForeign : AnnounceMessage incremented \n");
msgUnpackHeader(buf,&ptpClock->foreign[j].header);
msgUnpackAnnounce(buf,&ptpClock->foreign[j].announce);
break;
}
-
+
j = (j+1)%ptpClock->number_foreign_records;
}
/*New Foreign Master*/
if (!found) {
- if (ptpClock->number_foreign_records <
+ if (ptpClock->number_foreign_records <
ptpClock->max_foreign_records) {
ptpClock->number_foreign_records++;
}
j = ptpClock->foreign_record_i;
-
+
/*Copy new foreign master data set from Announce message*/
memcpy(ptpClock->foreign[j].foreignMasterPortIdentity.clockIdentity,
header->sourcePortIdentity.clockIdentity,
CLOCK_IDENTITY_LENGTH);
- ptpClock->foreign[j].foreignMasterPortIdentity.portNumber =
+ ptpClock->foreign[j].foreignMasterPortIdentity.portNumber =
header->sourcePortIdentity.portNumber;
ptpClock->foreign[j].foreignMasterAnnounceMessages = 0;
-
+
/*
* header and announce field of each Foreign Master are
* usefull to run Best Master Clock Algorithm
@@ -1648,9 +1876,9 @@
msgUnpackHeader(buf,&ptpClock->foreign[j].header);
msgUnpackAnnounce(buf,&ptpClock->foreign[j].announce);
DBGV("New foreign Master added \n");
-
- ptpClock->foreign_record_i =
- (ptpClock->foreign_record_i+1) %
- ptpClock->max_foreign_records;
+
+ ptpClock->foreign_record_i =
+ (ptpClock->foreign_record_i+1) %
+ ptpClock->max_foreign_records;
}
}
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/ptpd.c
--- a/src/ptpd.c Tue May 14 17:07:59 2013 -0700
+++ b/src/ptpd.c Sun Oct 27 22:49:29 2013 -0700
@@ -1,4 +1,6 @@
/*-
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
* Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
* Martin Burnicki, Gael Mace, Alexandre Van Kempen
* Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
@@ -57,48 +59,54 @@
int
main(int argc, char **argv)
{
- PtpClock *ptpClock;
- Integer16 ret;
+ PtpClock *ptpClock;
+ Integer16 ret;
- /* initialize run-time options to default values */
- rtOpts.announceInterval = DEFAULT_ANNOUNCE_INTERVAL;
- rtOpts.syncInterval = DEFAULT_SYNC_INTERVAL;
- rtOpts.clockQuality.clockAccuracy = DEFAULT_CLOCK_ACCURACY;
- rtOpts.clockQuality.clockClass = DEFAULT_CLOCK_CLASS;
- rtOpts.clockQuality.offsetScaledLogVariance = DEFAULT_CLOCK_VARIANCE;
- rtOpts.priority1 = DEFAULT_PRIORITY1;
- rtOpts.priority2 = DEFAULT_PRIORITY2;
- rtOpts.domainNumber = DEFAULT_DOMAIN_NUMBER;
+ /* initialize run-time options to default values */
+ rtOpts.announceInterval = DEFAULT_ANNOUNCE_INTERVAL;
+ rtOpts.syncInterval = DEFAULT_SYNC_INTERVAL;
+ rtOpts.clockQuality.clockAccuracy = DEFAULT_CLOCK_ACCURACY;
+ rtOpts.clockQuality.clockClass = DEFAULT_CLOCK_CLASS;
+ rtOpts.clockQuality.offsetScaledLogVariance = DEFAULT_CLOCK_VARIANCE;
+ rtOpts.priority1 = DEFAULT_PRIORITY1;
+ rtOpts.priority2 = DEFAULT_PRIORITY2;
+ rtOpts.domainNumber = DEFAULT_DOMAIN_NUMBER;
#ifdef PTP_EXPERIMENTAL
#endif
// rtOpts.slaveOnly = FALSE;
- rtOpts.currentUtcOffset = DEFAULT_UTC_OFFSET;
- rtOpts.ifaceName[0] = '\0';
- rtOpts.do_unicast_mode = 0;
-
- rtOpts.noAdjust = NO_ADJUST; // false
- // rtOpts.displayStats = FALSE;
- // rtOpts.csvStats = FALSE;
- /* Deep display of all packets seen by the daemon */
- rtOpts.displayPackets = FALSE;
- rtOpts.ap = DEFAULT_AP;
- rtOpts.ai = DEFAULT_AI;
- rtOpts.s = DEFAULT_DELAY_S;
- rtOpts.inboundLatency.nanoseconds = DEFAULT_INBOUND_LATENCY;
- rtOpts.outboundLatency.nanoseconds = DEFAULT_OUTBOUND_LATENCY;
- rtOpts.max_foreign_records = DEFAULT_MAX_FOREIGN_RECORDS;
- // rtOpts.ethernet_mode = FALSE;
- // rtOpts.offset_first_updated = FALSE;
- // rtOpts.file[0] = 0;
- rtOpts.logFd = -1;
- rtOpts.recordFP = NULL;
- rtOpts.do_log_to_file = FALSE;
- rtOpts.do_record_quality_file = FALSE;
- rtOpts.nonDaemon = FALSE;
+ rtOpts.currentUtcOffset = DEFAULT_UTC_OFFSET;
+ rtOpts.currentUtcOffsetValid = DEFAULT_UTC_VALID;
+ rtOpts.ifaceName[0] = '\0';
+ rtOpts.do_unicast_mode = 0;
+ rtOpts.resetClockStartupOnly = DEFAULT_RESET_CLOCK_STARTUP_ONLY;
+ rtOpts.maxReset = 0;
+ rtOpts.maxDelay.seconds = 0;
+ rtOpts.maxDelay.nanoseconds = 0;
+ rtOpts.noAdjust = NO_ADJUST; // false
+ // rtOpts.displayStats = FALSE;
+ rtOpts.verboseStats = FALSE;
+ // rtOpts.csvStats = FALSE;
+ /* Deep display of all packets seen by the daemon */
+ rtOpts.displayPackets = FALSE;
+ rtOpts.ap = DEFAULT_AP;
+ rtOpts.ai = DEFAULT_AI;
+ rtOpts.s = DEFAULT_DELAY_S;
+ rtOpts.inboundLatency.nanoseconds = DEFAULT_INBOUND_LATENCY;
+ rtOpts.outboundLatency.nanoseconds = DEFAULT_OUTBOUND_LATENCY;
+ rtOpts.max_foreign_records = DEFAULT_MAX_FOREIGN_RECORDS;
+ rtOpts.ethernet_mode = FALSE;
+ // rtOpts.offset_first_updated = FALSE;
+ // rtOpts.file[0] = 0;
+ rtOpts.logFd = -1;
+ rtOpts.recordFP = NULL;
+ rtOpts.ignore_daemon_lock = FALSE;
+ rtOpts.do_log_to_file = FALSE;
+ rtOpts.do_record_quality_file = FALSE;
+ rtOpts.nonDaemon = FALSE;
/*
* defaults for new options
@@ -112,6 +120,9 @@
#ifdef RUNTIME_DEBUG
rtOpts.debug_level = LOG_INFO; /* by default debug messages as disabled, but INFO messages and below are printed */
#endif
+ rtOpts.time_mode = DEFAULT_SLAVE_TIME_MODE;
+ rtOpts.master_slave_mode = PTP_MODE_NULL;
+ rtOpts.system_time_update_interval = DEFAULT_SYSTEM_TIME_UPDATE_INTERVAL;
rtOpts.ttl = 1;
rtOpts.delayMechanism = DEFAULT_DELAY_MECHANISM;
@@ -121,20 +132,20 @@
rtOpts.initial_delayreq = DEFAULT_DELAYREQ_INTERVAL;
rtOpts.subsequent_delayreq = DEFAULT_DELAYREQ_INTERVAL; // this will be updated if -g is given
- /* Initialize run time options with command line arguments */
- if (!(ptpClock = ptpdStartup(argc, argv, &ret, &rtOpts)))
- return ret;
+ /* Initialize run time options with command line arguments */
+ if (!(ptpClock = ptpdStartup(argc, argv, &ret, &rtOpts)))
+ return ret;
- /* global variable for message(), please see comment on top of this file */
- G_ptpClock = ptpClock;
+ /* global variable for message(), please see comment on top of this file */
+ G_ptpClock = ptpClock;
- /* do the protocol engine */
- protocol(&rtOpts, ptpClock);
- /* forever loop.. */
+ /* do the protocol engine */
+ protocol(&rtOpts, ptpClock);
+ /* forever loop.. */
- ptpdShutdown(ptpClock);
+ ptpdShutdown(ptpClock);
NOTIFY("self shutdown, probably due to an error\n");
- return 1;
+ return 1;
}
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/ptpd.h
--- a/src/ptpd.h Tue May 14 17:07:59 2013 -0700
+++ b/src/ptpd.h Sun Oct 27 22:49:29 2013 -0700
@@ -1,16 +1,47 @@
+/*-
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011-2012 Solarflare Communications Inc
+ * Copyright (c) 2009-2011 George V. Neville-Neil, Steven Kreuzer,
+ * Martin Burnicki, Gael Mace, Alexandre Van Kempen
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
/**
* @file ptpd.h
* @mainpage Ptpd v2 Documentation
- * @authors Martin Burnicki, Alexandre van Kempen, Steven Kreuzer,
+ * @authors Martin Burnicki, Alexandre van Kempen, Steven Kreuzer,
* George Neville-Neil
* @version 2.0
* @date Fri Aug 27 10:22:19 2010
- *
+ *
* @section implementation Implementation
* PTTdV2 is not a full implementation of 1588 - 2008 standard.
* It is implemented only with use of Transparent Clock and Peer delay
* mechanism, according to 802.1AS requierements.
- *
+ *
* This header file includes all others headers.
* It defines functions which are not dependant of the operating system.
*/
@@ -31,7 +62,6 @@
#include <limits.h>
#include <netdb.h>
#include <sys/time.h>
-#include <sys/resource.h>
#ifndef __APPLE__
#include <sys/timex.h>
#endif
@@ -50,6 +80,11 @@
#include "dep/datatypes_dep.h"
#include "datatypes.h"
#include "dep/ptpd_dep.h"
+#ifdef __sun
+#define TIME_BAD TIME_ERROR
+#include <sys/time.h>
+#include <strings.h>
+#endif
#define min(a,b) (((a)<(b))?(a):(b))
#define max(a,b) (((a)>(b))?(a):(b))
@@ -61,10 +96,15 @@
/* arith.c */
/**
- * \brief Convert Integer64 into TimeInternal structure
+ * \brief Convert Integer64 with fractional ns into TimeInternal structure
*/
void integer64_to_internalTime(Integer64,TimeInternal*);
/**
+ * \brief Convert TimeInternal structure to linear 64 bit number
+ */
+int64_t internalTime_to_scalar(TimeInternal*);
+
+/**
* \brief Convert TimeInternal into Timestamp structure (defined by the spec)
*/
void fromInternalTime(TimeInternal*,Timestamp*);
@@ -74,12 +114,6 @@
*/
void toInternalTime(TimeInternal*,Timestamp*);
-void ts_to_InternalTime(struct timespec *, TimeInternal *);
-void tv_to_InternalTime(struct timeval *, TimeInternal *);
-
-
-
-
/**
* \brief Use to normalize a TimeInternal structure
*
@@ -178,6 +212,7 @@
void clearTime(TimeInternal *time);
int isTimeInternalNegative(const TimeInternal * p);
+int log2IntegerSaturateAtZero(LongDouble number);
char *dump_TimeInternal(const TimeInternal * p);
char *dump_TimeInternal2(const char *st1, const TimeInternal * p1, const char *st2, const TimeInternal * p2);
@@ -193,6 +228,9 @@
int is_Time_close(TimeInternal *x, TimeInternal *b, int nanos);
int isTimeInternalNegative(const TimeInternal * p);
+float secondsToMidnight(void);
+float getPauseBeforeMidnight(Integer8 announceInterval);
+float getPauseAfterMidnight(Integer8 announceInterval);
int check_timestamp_is_fresh2(TimeInternal * timeA, TimeInternal * timeB);
diff -r 3f1e7d35d0ab -r 821e8eadeaff src/ptpd2.8
--- a/src/ptpd2.8 Tue May 14 17:07:59 2013 -0700
+++ b/src/ptpd2.8 Sun Oct 27 22:49:29 2013 -0700
@@ -1,10 +1,13 @@
.\" -*- nroff -*"
-.TH ptpd2 8 "January, 2012" "version 2.2.0" "Precision Time Protocol daemon"
+.\"
+.\" Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+.\"
+.TH ptpd "8" "October, 2015" "version 2.2.0" "System Administration Commands"
.SH NAME
-ptpd2 \- Precision Time Protocol daemon (1588-2008)
+ptpd \- Precision Time Protocol daemon (1588-2008)
.SH SYNOPSIS
-.B ptpd2
-[?]
+[-?]
[-B]
[-c]
[-C]
@@ -22,9 +25,8 @@
[-a NUMBER,NUMBER]
[-w NUMBER]
[-b NAME]
+[-K]
[-u ADDRESS]
-[-I group]
-[-U]
[-e]
[-h]
[-l NUMBER,NUMBER]
@@ -41,15 +43,15 @@
[-s NUMBER]
[-p NUMBER]
[-q NUMBER]
-[-G]
[-W]
[-Y NUMBER]
[-L]
[-j]
.SH DESCRIPTION
-Implements the Precision Time Protocol (PTP) Version 2 as defined by the IEEE
-1588-2008 standard. PTP was developed to provide very precise time
-coordination of LAN connected computers.
+The ptpd program is a userland daemon that does very precise
+synchronization of system clocks of LAN connected systems.
+It implements the Precision Time Protocol (PTP) Version 2
+as defined by the IEEE 1588-2008 standard.
.PP
PTPd is a complete implementation of the IEEE 1588 v2 specification for a
standard (ordinary) clock. PTPd has been tested with and is known
@@ -59,6 +61,81 @@
interoperable, and stable IEEE 1588 implementation.
.PP
For more information, see http://ptpd.sourceforge.net/
+.PP
+ptpd in Solaris works over any Ethernet based data link.
+It can also take advantage of PTP specific hardware found
+in certain NICs (Network Interface Card) for improved accuracy.
+.SH SERVICE MANAGEMENT
+The operation of ptpd is managed as a service by the service
+management facility, smf(7), under the service identifier:
+.TP
+.nf
+ svc:/network/ptp:default
+.fi
+.aj
+.PP
+Administrative actions on this service, such as enabling,
+disabling, or requesting restart, can be performed using
+svcadm(8). The service's status can be queried using the
+svcs(1) command. There are several options controlled by
+services properties which can be set by the system
+administrator. The available options can be listed by
+executing the following command:
+.PP
+.nf
+ svccfg -s svc:/network/ptp:default listprop config
+.fi
+.aj
+.PP
+Each of these properties can be set using this command:
+.PP
+.nf
+ svccfg -s svc:/network/ptp:default setprop <propname> = <value>
+.fi
+.aj
+
+
+See svcadm(8) and svccfg(8).
+
+Available options and their meaning are as follows:
+.TP
+.BR config/listen_ifname
+A string specifying the name of the link to which ptp daemon
+should bind to. By default, ptp daemon picks the first usable
+link with an IP address configured.
+.TP
+.BR config/node_type
+A string specifying the working mode of the ptpd. It can
+be either "slave" or "master". Default value is "slave".
+.TP
+.BR config/use_hw
+A boolean which when true, instructs ptpd to take advantage
+of the PTP hardware assist in the NIC. If set to true and
+NIC does not have PTP hardware assist, the ptpd will exit.
+Default is false. See EXAMPLES on how to recognize a NIC
+with PTP hardware assist.
+.TP
+.BR config/domain
+An integer specifying PTP domain as defined in the
+IEEE specification. It can take values from 0 to 3
+both inclusive. Default value is 0.
+.TP
+An integer specifying the interval in seconds between
+successive PTP ANNOUNCE messages sent by PTP master.
+Default value is 2.
+.TP
+.BR config/sync_interval
+An integer specifying the interval between successive
+PTP SYNC messages sent by PTP master. Default value is 1.
+.TP
+.BR config/logfile
+A string specifying the location of the file used for
+log output. The default is /var/log/ptp.log
+.TP
+.BR config/other_options
+A string specifying other command line options mentioned
+below to be passed to svc:/network/ptp:default at startup.
.SH OPTIONS
.TP
.B \-?
@@ -115,15 +192,13 @@
.B \-b NAME
bind PTP to network interface NAME
.TP
+.B \-K
+use PTP hardware in network interface. Exit if
+PTP hardware is not usable or available.
+.TP
.B \-u ADDRESS
also send uni-cast to ADDRESS
.TP
-.B \-I
-multicast group for PTP_EXPERIMENTAL mode
-.B \-U
-enable hybrid mode which uses both unicast and multicast, requires PTP_EXPERIMENTAL
.B \-e
run in ethernet mode (currently unimplemented)
.TP
@@ -172,20 +247,86 @@
.B \-q NUMBER
specify priority2 attribute
.TP
-.B \-G
-run as master with connection to NTP
.B \-W
-run as master without NTP
+run as master only
.TP
.B \-Y NUMBER
set an initial delay request value
.TP
.B \-L
-enable running multiple ptpd2 daemons
+enable running multiple ptpd daemons
.TP
.B \-j
turn off IGMP refresh messages
+.SH EXAMPLES
+Example 1 Identify the presence of PTP hardware assist. If the
+ptp property is equal to 1, PTP hardware assist can
+be enabled for that NIC through the config/use_hw
+property.
+
+.nf
+#dladm show-linkprop -p ptp
+
+LINK PROPERTY PERM VALUE EFFECTIVE DEFAULT POSSIBLE
+net1 ptp r- 0 0 0 0,1
+net2 ptp r- 0 0 0 0,1
+net0 ptp r- 0 0 0 0,1
+net3 ptp r- 0 0 0 0,1
+net7 ptp r- 0 0 0 0,1
+net8 ptp r- 1 1 0 0,1
+
+In the example above, the link net8 supports ptp in hardware.
+.fi
+
+Example 2 Starting the ptp service in slave mode.
+
+First bind ptp service to net8 -
+.nf
+#svccfg -s svc:/network/ptp:default \\
+ setprop config/listen_ifname=net8
+
+Then set the mode to "slave" -
+#svccfg -s svc:/network/ptp:default \\
+ setprop config/node_type=slave
+
+Enable the service -
+#svcadm enable svc:/network/ptp:default
+
+.fi
+Starting the service in slave mode will allow the ptp
+service to set system time to an external clock (PTP master)
+on the network connected to interface net8.
+.nf
+
+Example 3 Force ptp service to use PTP hardware in the NIC.
+
+First configure the service to use the PTP hardware -
+#svccfg -s svc:/network/ptp:default \\
+ setprop config/use_hw=true
+
+.fi
+Then refresh the service, assuming it is enabled and online
+already -
+.nf
+#svcadm refresh svc:/network/ptp:default
+
+.fi
+.SH NOTES
+Be careful when logging is enabled. In default setting it can
+generate upto 40 MB of data in a window of 24 hours and many
+times more if enabled with -P option.
+.P
+Solaris does not allow creation of a vnic on a network
+interface which is providing hardware assistance to ptpd.
+Vice versa, ptpd cannot get hardware assistance from an
+interfac which has pre-existing vnic(s).
+
+.SH SEE ALSO
+svcs(1), svcadm(8), attributes(7), smf(7)
+
+
+IEEE Standard 1588-2008, Precision Clock Synchronization
+Protocol for Networked Measurement and Control Systems, 2008
.SH AUTHORS
Gael Mace <gael_mace@users.sourceforge.net> & Alexandre Van