--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/ptp/patches/00-sol_sf_patch_over_community_edition.patch Thu Jan 23 15:00:25 2014 -0800
@@ -0,0 +1,10524 @@
+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, 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/man1m/
++ ginstall -D -p -m 444 src/ptpd2.8 $(DESTDIR)/usr/share/man/man1m/ptpd.1m
++
+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
+ SRCS = ptpd.c arith.c bmc.c protocol.c display.c\
+- dep/msg.c dep/net.c dep/servo.c dep/startup.c dep/sys.c dep/timer.c
++ dep/msg.c dep/net.c dep/servo.c dep/startup.c dep/sys.c dep/timer.c dep/time.c
+
+ OBJS = $(SRCS:.c=.o)
+
+ HDRS = ptpd.h constants.h datatypes.h \
+- dep/ptpd_dep.h dep/constants_dep.h dep/datatypes_dep.h
++ dep/ptpd_dep.h dep/constants_dep.h dep/datatypes_dep.h \
++ dep/sfxge_ioctl.h
+
+ 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",
++ now.seconds, now.nanoseconds);
++ 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",
++ now.seconds, now.nanoseconds);
++ 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",
+ slave_to_master_delay.nanoseconds);
+ 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",
+- slave_to_master_delay.nanoseconds,
+- 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",
++ slave_to_master_delay.seconds,
++ slave_to_master_delay.nanoseconds,
++ 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",
+- master_to_slave_delay.nanoseconds,
+- 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",
++ master_to_slave_delay.seconds,
++ master_to_slave_delay.nanoseconds,
++ 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",
++ master_to_slave_delay.seconds, master_to_slave_delay.nanoseconds);
++
+ /*
+ * 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:
++ * http://sourceforge.net/tracker/?func=detail&aid=2995791&group_id=139814&atid=744632
++ */
+
+
+ /* 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 Thu Jan 01 00:00:00 1970 +0000
++++ b/src/dep/sfxge_ioctl.h Sun Oct 27 22:49:29 2013 -0700
+@@ -0,0 +1,135 @@
++/*
++ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
++ */
++/****************************************************************************
++ * Driver for Solarflare network controllers
++ * (including support for SFE4001 10GBT NIC)
++ *
++ * Copyright 2005-2006: Fen Systems Ltd.
++ * Copyright 2006-2010: Solarflare Communications Inc,
++ * 9501 Jeronimo Road, Suite 250,
++ * Irvine, CA 92618, USA
++ *
++ * Initially developed by Michael Brown <[email protected]>
++ * Maintained by Solarflare Communications <[email protected]>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published
++ * by the Free Software Foundation, incorporated herein by reference.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ ****************************************************************************
++ */
++
++#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);
++ delay.seconds = req.u.ts_sync.ts.tv_sec;
++ delay.nanoseconds = 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;
++ 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
++
++ NOTIFY("adding NIC offset %ld.%09d (%ld/%p)\n",
++ req.u.ts_settime.ts.tv_sec, req.u.ts_settime.ts.tv_nsec,
++ 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",
++ req.u.ts_settime.ts.tv_sec,
++ req.u.ts_settime.ts.tv_nsec);
++ }
++ 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),
++ 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,
++ req.u.ts_read.uuid[0], req.u.ts_read.uuid[1], req.u.ts_read.uuid[2],
++ req.u.ts_read.uuid[3], req.u.ts_read.uuid[4], req.u.ts_read.uuid[5]);
++ }
++
++ if(req.u.ts_read.tx_valid)
++ {
++ if (rawTime) {
++ lastSendTime.seconds = req.u.ts_read.tx_ts_hw.tv_sec;
++ lastSendTime.nanoseconds = req.u.ts_read.tx_ts_hw.tv_nsec;
++ } else {
++ lastSendTime.seconds = req.u.ts_read.tx_ts.tv_sec;
++ lastSendTime.nanoseconds = req.u.ts_read.tx_ts.tv_nsec;
++ }
++
++ DBGV("tx time %d.%09d (%d.%09d)\n",
++ lastSendTime.seconds, lastSendTime.nanoseconds,
++ req.u.ts_read.tx_ts.tv_sec, req.u.ts_read.tx_ts.tv_nsec);
++ }
++ } 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);
++
++ if(lastSendTime.seconds || lastSendTime.nanoseconds)
++ {
++ *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 =
+ correctionField.seconds;
+ ptpClock->lastSyncCorrectionField.nanoseconds =
+ correctionField.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,
+ ptpClock->msgTmp.resp.requestingPortIdentity.clockIdentity,
+ CLOCK_IDENTITY_LENGTH) == 0) &&
+- ((ptpClock->sentDelayReqSequenceId - 1)==
++ ((UInteger16)(ptpClock->sentDelayReqSequenceId - 1)==
+ header->sequenceId) &&
+- (ptpClock->portIdentity.portNumber ==
++ (ptpClock->portIdentity.portNumber ==
+ ptpClock->msgTmp.resp.requestingPortIdentity.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 =
+ requestReceiptTimestamp.seconds;
+- ptpClock->delay_req_receive_time.nanoseconds =
++ ptpClock->delay_req_receive_time.nanoseconds =
+ requestReceiptTimestamp.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);
+ ptpClock->pdelay_req_receive_time.seconds = requestReceiptTimestamp.seconds;
+ ptpClock->pdelay_req_receive_time.nanoseconds = requestReceiptTimestamp.nanoseconds;
+-
++
+ integer64_to_internalTime(header->correctionfield,&correctionField);
+ ptpClock->lastPdelayRespCorrectionField.seconds = correctionField.seconds;
+ ptpClock->lastPdelayRespCorrectionField.nanoseconds = correctionField.nanoseconds;
+@@ -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 =
+- responseOriginTimestamp.seconds;
+- ptpClock->pdelay_resp_send_time.nanoseconds =
+- responseOriginTimestamp.nanoseconds;
++ &responseOriginTimestamp,
++ &ptpClock->msgTmp.prespfollow.responseOriginTimestamp);
++ ptpClock->pdelay_resp_send_time.seconds =
++ responseOriginTimestamp.seconds;
++ ptpClock->pdelay_resp_send_time.nanoseconds =
++ responseOriginTimestamp.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
+ rtOpts.mcast_group_Number = 0;
+ rtOpts.do_hybrid_mode = 0;
+ #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.unicastAddress
+- 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.unicastAddress
++ 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, 2011, Oracle and/or its affiliates. All rights reserved.
++.\"
++.TH ptpd "1M" "January, 2012" "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 /usr/lib/inet/ptpd
++[-?]
+ [-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(5), 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(1M). 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(1M) and svccfg(1M).
++
++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
++.BR config/announce_interval
++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
+-.TP
+-.B \-U
+-enable hybrid mode which uses both unicast and multicast, requires PTP_EXPERIMENTAL
+-.TP
+ .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
+-.TP
+ .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=yes
++
++.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(1M), attributes(5), smf(5)
++
++
++IEEE Standard 1588-2008, Precision Clock Synchronization
++Protocol for Networked Measurement and Control Systems, 2008
+
+ .SH AUTHORS
+ Gael Mace <[email protected]> & Alexandre Van