# HG changeset patch # User Prakash Jalan # Date 1386202641 28800 # Node ID dece556dd5e7df58beb88aad95569ef98dc7f28a # Parent 59f52cde58ccf33917eb68a192f31d2faab91a10 PSARC/2013/284 PTP (Precision Time Protocol) 2.2.0 15820662 SUNBT7203023 PTP support in Solaris diff -r 59f52cde58cc -r dece556dd5e7 components/ntp/Solaris/ntp.xml --- a/components/ntp/Solaris/ntp.xml Wed Dec 04 06:55:26 2013 -0800 +++ b/components/ntp/Solaris/ntp.xml Wed Dec 04 16:17:21 2013 -0800 @@ -20,7 +20,7 @@ CDDL HEADER END - Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. NOTE: This service manifest is not editable; its contents will be overwritten by package or patch operations, including @@ -51,6 +51,14 @@ + + + + + + +When Manage PTP Service States is in the Authorizations Include +column, it grants the authorization to enable, disable, or restart the +ptp daemon. +

+If Manage PTP Service States is grayed, then you are not entitled to +Add or Remove this authorization. +
  + + diff -r 59f52cde58cc -r dece556dd5e7 components/ptp/Solaris/SmfValuePTP.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/ptp/Solaris/SmfValuePTP.html Wed Dec 04 16:17:21 2013 -0800 @@ -0,0 +1,35 @@ + + + + +When Value PTP Properties is in the Authorizations Included +column, it grants the authorization to change PTP service property values. +

+If Value PTP Properties is grayed, then you are not entitled to +Add or Remove this authorization. +
  + + diff -r 59f52cde58cc -r dece556dd5e7 components/ptp/Solaris/auth_attr --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/ptp/Solaris/auth_attr Wed Dec 04 16:17:21 2013 -0800 @@ -0,0 +1,2 @@ +solaris.smf.manage.ptp:::Manage PTP service states::help=SmfPTPStates.html +solaris.smf.value.ptp:::Change PTP value properties::help=SmfValuePTP.html diff -r 59f52cde58cc -r dece556dd5e7 components/ptp/Solaris/prof_attr --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/ptp/Solaris/prof_attr Wed Dec 04 16:17:21 2013 -0800 @@ -0,0 +1,1 @@ +PTP Management:RO::Manage the PTP service:auths=solaris.smf.manage.ptp,solaris.smf.value.ptp diff -r 59f52cde58cc -r dece556dd5e7 components/ptp/Solaris/ptp.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/ptp/Solaris/ptp.sh Wed Dec 04 16:17:21 2013 -0800 @@ -0,0 +1,108 @@ +#!/sbin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +# + +# Standard prolog +# +. /lib/svc/share/smf_include.sh +. /lib/svc/share/net_include.sh + +EXECFILE="/usr/lib/inet/ptpd" + +if [ -z $SMF_FMRI ]; then + echo "SMF framework variables are not initialized." + exit $SMF_EXIT_ERR_NOSMF +fi + +# +# get_prop fmri propname +# +get_prop () { + VALUE="`$SVCPROP -cp config/$1 $SMF_FMRI 2>/dev/null`" + # Empty astring_list values show up as "" - do not return this. + if [ "$VALUE" != "\"\"" ]; then + echo $VALUE + fi +} + +errlog () { + echo $1 >&2 +} + +#Do not refresh IGMP group +CMD_LINE_ARGS=" -j" + +LISTEN_IFNAME="`get_prop listen_ifname`" +if [ -n "$LISTEN_IFNAME" ]; then + CMD_LINE_ARGS="$CMD_LINE_ARGS -b $LISTEN_IFNAME" +fi + +NODE_TYPE="`get_prop node_type`" +if [ -n "$NODE_TYPE" ]; then + if [ "$NODE_TYPE" = "master" ]; then + CMD_LINE_ARGS="$CMD_LINE_ARGS -W" + elif [ "$NODE_TYPE" = "slave" ]; then + CMD_LINE_ARGS="$CMD_LINE_ARGS -g" + else + errlog "node_type needs to be either slave or master. See ptp (1M). Exiting." + exit $SMF_EXIT_ERR_CONFIG + fi +else + CMD_LINE_ARGS="$CMD_LINE_ARGS -g" +fi + +USE_HW="`get_prop use_hw`" +if [ "$USE_HW" = "true" ];then + CMD_LINE_ARGS="$CMD_LINE_ARGS -K" +fi + +DOMAIN="`get_prop domain`" +if [ "$DOMAIN" -gt 0 -a "$DOMAIN" -lt 4 ]; then + CMD_LINE_ARGS="$CMD_LINE_ARGS -i $DOMAIN" +fi + +ANN_INTVL="`get_prop announce_interval`" +if [ "$ANN_INTVL" -gt 0 -a "$ANN_INTVL" -ne 2 ]; then + CMD_LINE_ARGS="$CMD_LINE_ARGS -n $ANN_INTVL" +fi + +SYNC_INTVL="`get_prop sync_interval`" +if [ "$SYNC_INTVL" -gt 0 -a "$SYNC_INTVL" -ne 1 ]; then + CMD_LINE_ARGS="$CMD_LINE_ARGS -y $SYNC_INTVL" +fi + +LOGFILE="`get_prop logfile`" +if [ -n "$LOGFILE" -a "$LOGFILE" != "/var/log/ptp.log" ]; then + CMD_LINE_ARGS="$CMD_LINE_ARGS -f $LOGFILE" +fi + +OTHER_OPTIONS="`get_prop other_options`" +if [ -n "$OTHER_OPTIONS" ]; then + CMD_LINE_ARGS="$CMD_LINE_ARGS $OTHER_OPTIONS" +fi + + +# start ptp daemon +$EXECFILE $CMD_LINE_ARGS +exit $SMF_EXIT_OK diff -r 59f52cde58cc -r dece556dd5e7 components/ptp/Solaris/ptp.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/ptp/Solaris/ptp.xml Wed Dec 04 16:17:21 2013 -0800 @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 59f52cde58cc -r dece556dd5e7 components/ptp/patches/00-sol_sf_patch_over_community_edition.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/ptp/patches/00-sol_sf_patch_over_community_edition.patch Wed Dec 04 16:17:21 2013 -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 + #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 ++#include + + /*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 + # include + # include +@@ -48,6 +78,9 @@ + # if defined(__FreeBSD__) || defined(__APPLE__) + # include + # include ++# elif defined(__sun) ++# include ++# include + # else + # include + # 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 + # 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 ++#include ++ + /** + *\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 ++#include ++#include "sfxge_ioctl.h" ++#include ++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 ++ * Maintained by Solarflare Communications ++ * ++ * 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 ++#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 + #include + +-#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 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 ++ + #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 ++#include ++ ++#ifdef __sun ++#include ++#include ++#endif ++ ++ ++#ifdef linux ++ ++#include ++#include ++ ++/* 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;inumber_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 + #include + #include +-#include + #ifndef __APPLE__ + #include + #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 ++#include ++#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 = ++.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 & Alexandre Van diff -r 59f52cde58cc -r dece556dd5e7 components/ptp/ptp.license --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/ptp/ptp.license Wed Dec 04 16:17:21 2013 -0800 @@ -0,0 +1,31 @@ +Copyright (c) 2009-2012 George V. Neville-Neil, + Steven Kreuzer, + Martin Burnicki, + Jan Breuer, + 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. diff -r 59f52cde58cc -r dece556dd5e7 components/ptp/ptp.p5m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/ptp/ptp.p5m Wed Dec 04 16:17:21 2013 -0800 @@ -0,0 +1,53 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +# + + default mangler.man.stability uncommitted> +set name=pkg.fmri \ + value=pkg:/service/network/ptp@$(IPS_COMPONENT_VERSION),$(BUILD_VERSION) +set name=pkg.summary \ + value="Precision Time Protocol (PTP) IEEE 1588-2008 (Version 2)" +set name=pkg.description \ + value="Precision Time Protocol IEEE 1588-2008 (Version 2), PTP Daemon. Works either as PTP Master or PTP Slave." +set name=com.oracle.info.description \ + value="Precision Time Protocol (PTP) IEEE 1588-2008 (Version 2)" +set name=info.classification value=org.opensolaris.category.2008:System/Services +set name=info.source-url value=$(COMPONENT_ARCHIVE_URL) +set name=info.upstream-url value=$(COMPONENT_PROJECT_URL) +set name=org.opensolaris.arc-caseid value=PSARC/2013/284 +set name=org.opensolaris.consolidation value=$(CONSOLIDATION) +file Solaris/auth_attr path=etc/security/auth_attr.d/ptp +file Solaris/prof_attr path=etc/security/prof_attr.d/ptp +file Solaris/ptp.xml path=lib/svc/manifest/network/ptp.xml +file Solaris/ptp.sh path=lib/svc/method/ptp +file Solaris/SmfPTPStates.html \ + path=usr/lib/help/auths/locale/C/SmfPTPStates.html +file Solaris/SmfValuePTP.html path=usr/lib/help/auths/locale/C/SmfValuePTP.html +file path=usr/lib/inet/ptpd +file path=usr/share/man/man1m/ptpd.1m +license ptp.license license=BSD \ + com.oracle.info.description="the community version of ptpd" \ + com.oracle.info.name=PTP com.oracle.info.tpno=12665 +license solarflare.license license=BSD \ + com.oracle.info.description="the Solarflare enhancement to community version of ptpd" \ + com.oracle.info.name=Solarflare com.oracle.info.tpno=15817 + diff -r 59f52cde58cc -r dece556dd5e7 components/ptp/resolve.deps --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/ptp/resolve.deps Wed Dec 04 16:17:21 2013 -0800 @@ -0,0 +1,4 @@ +shell/ksh93 +system/core-os +system/library +system/library/math diff -r 59f52cde58cc -r dece556dd5e7 components/ptp/solarflare.license --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/ptp/solarflare.license Wed Dec 04 16:17:21 2013 -0800 @@ -0,0 +1,36 @@ +This source code is a modified version of the PTP sourceforge project +available at http://ptpd.sourceforge.net/ + +It is distributed under a BSD-style license available in the COPYRIGHT file." + +License from the Copyright file: +"/*- + * 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. + */ +"