ptpd-2.2.2/0000700000175000017500000000000011770426165011451 5ustar ernieernieptpd-2.2.2/ChangeLog0000600000175000017500000001016711770426154013230 0ustar ernieernie2012-06-20 George Neville-Neil * 2.2.2 release * Leap second fix 2012-01-05 George Neville-Neil * 2.2.0 release * Patches: 3134556, 3296405 * Added support for Mac OS X (tested on Snow Leopard and Lion) * Protocol implementation Fixes: * Bugs Fixed - Client now correctly accepts the Master DelayReq rate - DelayMS and delaySM now correctly show the sign when negative - Sanity Flags: client now requires -g or -G for slave/master operation. - Client can print the MAC address of its master (instead of EEUI) - master now sends ARB timescale only with utc_offset of zero - slave now only uses the last UTC_Offset if UTC_Valid bit is on. - passive masters no longer become active every 12s, - first delayreq is postponed until we receive the first sync - -G (master with ntpd) now announces a better clock class of 13 - delayReq period is now uniformly randomized from range - updated to the PTPv2 messages rates (sync / delayreq / announce ) - operator is warned once when the we slew the clock at maximum speed - and several others too minor too mention * System fixes and new features - Frequency adjustment is now calculated in double precision - Kernel timestamps are now in nanoseconds precision - Timer system overhead was reduced to 16 alarms per second (was 1000) - each reset now generates an IGMP leave/join operation to the multicast group - Log file is now appended, with the right permissions - Debug messages show a timestamp - Signals are now processed synchronously (to avoid race conditions) - Configurable amount of logging (to avoid filling up /var/log) - client now checks own filelock, $0 and well-known daemons. - unicast messages can use DNS - syslog support (-S) - quality file can be generated with received syncs (-R) - messages can be dumped for debug (-P) - gnore packets that result in large deltas (-M) - SIGUSR1 now steps the clock to the current PTP offset - SIGUSR2 now cycles the domain number (useful for testing) - reverted R135 timer change from integer back to floating point - rand() is now seeded with last digits of our own mac address - IGMP_refresh waits 100ms between drop() and add() - checked to run without leaks inside valgrind 3.5.0 - last message received is identified by a column on the statistics log - messages are sent to Syslog by default. reversed -S flag - statistcs file now display /etc/ethers names (besides mac address) - option -C is console mode with full verbosity - startup warnings are also duplicated in stdout - startup: lockfile is checked twice: once at init, to return correct errorlevel, and a second time after deaemon() - check for root uid() - improvements in parallel daemons checking - command line parameters are dumped at init - Set the unicast flag when sending unicast packets (experimental, hybrid mode only). - Reimplemented integer64_to_internalTime not to use doubles - Replaced divTime by div2Time - Replaced all time.seconds = time.nanoseconds = 0 by clearTime(&time) - Replaced all hex values by named flags - Optimized comparison of clockIdentity in bmc.c - Resolved issue of comparison of offsetScaledLogVariance - Optimized bmcStateDecision not to call bmcDataSetComparison so often with the same parameters - displayStats now uses getTime instead of gettimeofday 2011-02-01 George Neville-Neil * Add support for DNS lookup of timeserver for unicast. * Add support for unicasting delay requests. * Add code to dump packets on demand via the -P flag as well as in response to updates that violate either the -M or -O flags. 2010-10-12 George Neville-Neil * 2.1.0 First main line release of PTPv2 code base (IEEE-1588-2008) * Add code to limit how much of an offset or delay the client is willing to tolerate. * Add support for BINTIME on FreeBSD which gives more accurate packet timestamps. * Add quality file support * Fix significant bugs that prevented correct operation in End-to-End mode. * Add support for syslog. * Add support for user configurable TTL. * Clean up code formatting, headers, comments etc. ptpd-2.2.2/RELEASE_NOTES0000777000175000017500000000000011770426165015621 2../RELEASE_NOTESustar ernieernieptpd-2.2.2/COPYRIGHT0000600000175000017500000000376311770426154012755 0ustar ernieernie/*- * Copyright (c) 2011-2012 George V. Neville-Neil, * Steven Kreuzer, * Martin Burnicki, * Jan Breuer, * Gael Mace, * Alexandre Van Kempen, * Inaqui Delgado, * Rick Ratzel, * National Instruments. * Copyright (c) 2009-2010 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. */ ptpd-2.2.2/tools/0000700000175000017500000000000012127234262012601 5ustar ernieernieptpd-2.2.2/tools/filter_response.m0000700000175000017500000000073611770426152016177 0ustar ernieernie#!/usr/bin/octave -qf # the IIR filter s = 2^4; a = [ s -(s-1) ]; b = [ 1/2 1/2 ]; [h w] = freqz(b, a, 100000); subplot(211); plot(w/pi,abs(h),";;"); axis(); #plot(w/pi,20*log(abs(h))); #axis([0 1 -120 0]); #semilogx(w/pi,20*log(abs(h)),";;"); #axis([1e-3 1 -120 0]); ylabel("gain"); subplot(212); plot(w/pi,unwrap(angle(h)),";;"); axis([0 1 -pi 0]); #semilogx(w/pi,unwrap(angle(h)),";;"); #axis([1e-3 1 -pi 0]); ylabel("phase (rad)"); xlabel("frequency"); replot; pause; ptpd-2.2.2/tools/graph.R0000700000175000017500000000605511770426152014042 0ustar ernieernie#!/usr/local/bin/Rscript --slave # Copyright (c) 2012, Neville-Neil Consulting # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 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. # # Neither the name of Neville-Neil Consulting nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER 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. # # Author: George V. Neville-Neil # # Description: A tool to graph the offset vs. the master to slave # delay vs. the slave to mater delay vs. the calculated delay from a # PTPd output file generated with the -D flag. # # Usage: graph.R input_file [output_file] # argv <- commandArgs(TRUE) file = argv[1] output = argv[2] if (is.na(output)) output = paste(basename(file), ".png", sep="") ptplog = read.table(file, fill=TRUE, sep=",", col.names=c("timestamp", "state", "clockID", "delay", "offset", "master.to.slave", "slave.to.master", "drift", "packet"), blank.lines.skip=TRUE, header=FALSE, skip=100) ymin = min(min(ptplog$offset, na.rm=TRUE), min(ptplog$delay, na.rm=TRUE), min(ptplog$master.to.slave, na.rm=TRUE), min(ptplog$slave.to.master, na.rm=TRUE)) ymax = max(max(ptplog$offset, na.rm=TRUE), max(ptplog$delay, na.rm=TRUE), max(ptplog$master.to.slave, na.rm=TRUE), max(ptplog$slave.to.master, na.rm=TRUE)) png(filename=output, height=960, width=1280, bg="white") plot(ptplog$delay, y=NULL, xaxt = "n" ,type="n", ylim=range(ymin, ymax), main="PTP Results", xlab="Time", ylab="Nanoseconds") legend(100, ymax, c("Delay", "Offset", "M->S", "S->M"), col=c("orange", "blue", "red", "green"), pch=21:24) points(ptplog$offset, y=NULL, cex=.1, col="blue", pch=21) points(ptplog$master.to.slave, y=NULL, cex=.1, col="red", pch=22) points(ptplog$slave.to.master, y=NULL, cex=.1, col="green", pch=23) points(ptplog$delay, y=NULL, cex=.1, col="orange", pch=24) axis(1, at=ptplog$timestamp, labels=ptplog$timestamp, ) ptpd-2.2.2/tools/offset_stats.m0000700000175000017500000000370311770426152015475 0ustar ernieernie#!/usr/bin/octave -qf printf("start\n"); load t; load tr; # time window to analyze t_beg = 1; t_fin = length(t); # parameters for synthetic reference time #start_time = round(t(t_beg)); #sample_interval = 1; # parameters for histogram h_beg = -15e-6; h_fin = 15e-6; h_sz = 1e-6; # parameters for allan variance # tune for your computational power tau_beg = 1; tau_fin = length(t)/10; tau_maxsamps = 100; # ---------- printf("data loaded\n"); # create synthetic reference time #tr = t; #tr(t_beg) = start_time; #for k = (t_beg+1):t_fin # # tr(k) = tr(k-1) + sample_interval; # #endfor # time offset computation o = t - tr; o_min = min(o(t_beg:t_fin)); o_max = max(o(t_beg:t_fin)); o_mean = mean(o(t_beg:t_fin)); # relative tick rate computation r = t; for k = (t_beg+1):t_fin r(k) = o(k) - o(k-1); endfor r_min = min(r((t_beg+1):t_fin)); r_max = max(r((t_beg+1):t_fin)); r_mean = mean(r((t_beg+1):t_fin)); figure; h_bins = h_beg:h_sz:h_fin; hist( o(t_beg:t_fin), h_bins, 1); printf("histogram plotted\n"); figure; subplot(211); plot( t_beg:t_fin, o(t_beg:t_fin), "r;time offset;", [t_beg t_fin], [o_min o_min], "g;;", [t_beg t_fin], [o_max o_max], "g;;", [t_beg t_fin], [o_mean o_mean], "b;;"); subplot(212); plot( (t_beg+1):t_fin, r((t_beg+1):t_fin), "r;relative tick rate;", [(t_beg+1) t_fin], [r_min r_min], "g;;", [(t_beg+1) t_fin], [r_max r_max], "g;;", [(t_beg+1) t_fin], [r_mean r_mean], "b;;") replot; printf("offset plotted\n"); # the allan vaiance computation from the IEEE 1588 spec a = t; for tau = tau_beg:tau_fin beg = t_beg; if (t_fin-2*tau) > tau_maxsamps fin = tau_maxsamps; else fin = (t_fin-2*tau); end a(tau) = sum((t(beg:fin) - 2*t((beg+tau):(fin+tau)) + t((beg+2*tau):(fin+2*tau))).^2); a(tau) /= 2*(fin-beg)*(tau^2); endfor figure; loglog( tau_beg:tau_fin, a(tau_beg:tau_fin), ";allan variance;"); replot; printf("variance plotted\n"); input("done\n"); ptpd-2.2.2/tools/stats.R0000700000175000017500000000677111770426152014104 0ustar ernieernie#!/usr/local/bin/Rscript --slave # Copyright (c) 2012, Neville-Neil Consulting # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 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. # # Neither the name of Neville-Neil Consulting nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER 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. # # Author: George V. Neville-Neil # # Description: A tool, written in R, that will process a PTPd log and # give various statistics about PTP performance. Statistics are given # for the offset from master (Offset), one way delay (Delay), Master # to Slave and Slave to Master timings. An argument is required which # is a raw ptpd log produced by the -D flag to ptpd2. argv <- commandArgs(TRUE) file = argv[1] ptplog = read.table(file, fill=TRUE, sep=",", col.names=c("timestamp", "state", "clockID", "delay", "offset", "master.to.slave", "slave.to.master", "drift", "packet"), blank.lines.skip=TRUE, header=FALSE, skip=100) cat("Offset", "\nmin:", min(ptplog$offset, na.rm=TRUE), " max: ", max(ptplog$offset, na.rm=TRUE), " median: ", median(ptplog$offset, na.rm=TRUE), " mean: ", mean(ptplog$offset, na.rm=TRUE), "\nstd dev: ", sd(ptplog$offset, na.rm=TRUE), " variance: ", var(ptplog$offset, na.rm=TRUE), "\n") cat("Delay", "\nmin:", min(ptplog$delay, na.rm=TRUE), " max: ", max(ptplog$delay, na.rm=TRUE), " median: ", median(ptplog$delay, na.rm=TRUE), " mean: ", mean(ptplog$delay, na.rm=TRUE), "\nstd dev: ", sd(ptplog$delay, na.rm=TRUE), " variance: ", var(ptplog$delay, na.rm=TRUE), "\n") cat("Master -> Slave", "\nmin:", min(ptplog$master.to.slave, na.rm=TRUE), " max: ", max(ptplog$master.to.slave, na.rm=TRUE), " median: ", median(ptplog$master.to.slave, na.rm=TRUE), " mean: ", mean(ptplog$master.to.slave, na.rm=TRUE), "\nstd dev: ", sd(ptplog$master.to.slave, na.rm=TRUE), " variance: ", var(ptplog$master.to.slave, na.rm=TRUE), "\n") cat("Slave -> Master", "\nmin:", min(ptplog$slave.to.master, na.rm=TRUE), " max: ", max(ptplog$slave.to.master, na.rm=TRUE), " median: ", median(ptplog$slave.to.master, na.rm=TRUE), " mean: ", mean(ptplog$slave.to.master, na.rm=TRUE), "\nstd dev: ", sd(ptplog$slave.to.master, na.rm=TRUE), " variance: ", var(ptplog$slave.to.master, na.rm=TRUE), "\n") ptpd-2.2.2/Makefile0000600000175000017500000000151511770426154013113 0ustar ernieernie# Root Makefile for ptpd, used for cutting releases and release candidates NAME = ptpd VERSION = ${NAME}-2.2.2 RC = ${NAME}-2-RC-0 rc: (cd src; make clean) mkdir $(RC) (cd $(RC); \ 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 $(RC).tar.gz -L --exclude .o --exclude Doxygen --exclude .svn --exclude .dep --exclude core $(RC) 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) ptpd-2.2.2/src/0000700000175000017500000000000012127234262012230 5ustar ernieernieptpd-2.2.2/src/protocol.c0000600000175000017500000017754011770426154014263 0ustar ernieernie/*- * Copyright (c) 2011-2012 George V. Neville-Neil, * Steven Kreuzer, * Martin Burnicki, * Jan Breuer, * Gael Mace, * Alexandre Van Kempen, * Inaqui Delgado, * Rick Ratzel, * National Instruments. * Copyright (c) 2009-2010 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. */ /** * @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" Boolean doInit(RunTimeOpts*,PtpClock*); void doState(RunTimeOpts*,PtpClock*); void toState(UInteger8,RunTimeOpts*,PtpClock*); void handle(RunTimeOpts*,PtpClock*); void handleAnnounce(MsgHeader*,Octet*,ssize_t,Boolean,RunTimeOpts*,PtpClock*); void handleSync(MsgHeader*,Octet*,ssize_t,TimeInternal*,Boolean,RunTimeOpts*,PtpClock*); void handleFollowUp(MsgHeader*,Octet*,ssize_t,Boolean,RunTimeOpts*,PtpClock*); void handlePDelayReq(MsgHeader*,Octet*,ssize_t,TimeInternal*,Boolean,RunTimeOpts*,PtpClock*); void handleDelayReq(MsgHeader*,Octet*,ssize_t,TimeInternal*,Boolean,RunTimeOpts*,PtpClock*); void handlePDelayResp(MsgHeader*,Octet*,TimeInternal* ,ssize_t,Boolean,RunTimeOpts*,PtpClock*); void handleDelayResp(MsgHeader*,Octet*,ssize_t,Boolean,RunTimeOpts*,PtpClock*); void handlePDelayRespFollowUp(MsgHeader*,Octet*,ssize_t,Boolean,RunTimeOpts*,PtpClock*); void handleManagement(MsgHeader*,Octet*,ssize_t,Boolean,RunTimeOpts*,PtpClock*); void handleSignaling(MsgHeader*,Octet*,ssize_t,Boolean,RunTimeOpts*,PtpClock*); void issueAnnounce(RunTimeOpts*,PtpClock*); void issueSync(RunTimeOpts*,PtpClock*); void issueFollowup(TimeInternal*,RunTimeOpts*,PtpClock*); void issuePDelayReq(RunTimeOpts*,PtpClock*); void issueDelayReq(RunTimeOpts*,PtpClock*); void issuePDelayResp(TimeInternal*,MsgHeader*,RunTimeOpts*,PtpClock*); void issueDelayResp(TimeInternal*,MsgHeader*,RunTimeOpts*,PtpClock*); void issuePDelayRespFollowUp(TimeInternal*,MsgHeader*,RunTimeOpts*,PtpClock*); void issueManagement(MsgHeader*,MsgManagement*,RunTimeOpts*,PtpClock*); void issueManagementRespOrAck(MsgManagement*,RunTimeOpts*,PtpClock*); void issueManagementErrorStatus(MsgManagement*,RunTimeOpts*,PtpClock*); void addForeign(Octet*,MsgHeader*,PtpClock*); /* loop forever. doState() has a switch for the actions and events to be 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 protocol(RunTimeOpts *rtOpts, PtpClock *ptpClock) { DBG("event POWERUP\n"); toState(PTP_INITIALIZING, rtOpts, ptpClock); DBG("Debug Initializing...\n"); for (;;) { /* 20110701: this main loop was rewritten to be more clear */ if (ptpClock->portState == PTP_INITIALIZING) { if (!doInit(rtOpts, ptpClock)) { return; } } else { doState(rtOpts, ptpClock); } if (ptpClock->message_activity) DBGV("activity\n"); /* Perform the heavy signal processing synchronously */ check_signals(rtOpts, ptpClock); } } /* perform actions required when leaving 'port_state' and entering 'state' */ 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(ANNOUNCE_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); 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"); ptpClock->portState = PTP_INITIALIZING; break; case PTP_FAULTY: DBG("state PTP_FAULTY\n"); ptpClock->portState = PTP_FAULTY; break; case PTP_DISABLED: DBG("state PTP_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 */ if (ptpClock->portState != PTP_LISTENING) { ptpClock->reset_count++; } /* Revert to the original DelayReq interval, and ignore the one for the last master */ ptpClock->logMinDelayReqInterval = 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, (ptpClock->announceReceiptTimeout) * (pow(2,ptpClock->logAnnounceInterval)), ptpClock->itimer); ptpClock->portState = PTP_LISTENING; break; case PTP_MASTER: DBG("state PTP_MASTER\n"); INFO(" now in state PTP_MASTER\n"); timerStart(SYNC_INTERVAL_TIMER, pow(2,ptpClock->logSyncInterval), ptpClock->itimer); DBG("SYNC INTERVAL TIMER : %f \n", pow(2,ptpClock->logSyncInterval)); timerStart(ANNOUNCE_INTERVAL_TIMER, pow(2,ptpClock->logAnnounceInterval), 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"); timerStart(PDELAYREQ_INTERVAL_TIMER, pow(2,ptpClock->logMinPdelayReqInterval), ptpClock->itimer); timerStart(ANNOUNCE_RECEIPT_TIMER, (ptpClock->announceReceiptTimeout) * (pow(2,ptpClock->logAnnounceInterval)), ptpClock->itimer); ptpClock->portState = PTP_PASSIVE; p1(ptpClock, rtOpts); break; case PTP_UNCALIBRATED: DBG("state PTP_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); ptpClock->waitingForFollow = FALSE; ptpClock->waitingForDelayResp = FALSE; // FIXME: clear these vars inside initclock clearTime(&ptpClock->delay_req_send_time); clearTime(&ptpClock->delay_req_receive_time); clearTime(&ptpClock->pdelay_req_send_time); 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)), 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 Now, we only start the * timer after we receive the first sync (in * handle_sync()) */ ptpClock->waiting_for_first_sync = TRUE; 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: DBG("to unrecognized state\n"); break; } if (rtOpts->displayStats) displayStats(rtOpts, ptpClock); } Boolean doInit(RunTimeOpts *rtOpts, PtpClock *ptpClock) { DBG("manufacturerIdentity: %s\n", MANUFACTURER_ID); DBG("manufacturerOUI: %02hhx:%02hhx:%02hhx \n", MANUFACTURER_ID_OUI0, MANUFACTURER_ID_OUI1, MANUFACTURER_ID_OUI2); /* initialize networking */ netShutdown(&ptpClock->netPath); if (!netInit(&ptpClock->netPath, rtOpts, ptpClock)) { ERROR("failed to initialize network\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); toState(PTP_LISTENING, rtOpts, ptpClock); return TRUE; } /* handle actions and events for 'port_state' */ void doState(RunTimeOpts *rtOpts, PtpClock *ptpClock) { UInteger8 state; ptpClock->message_activity = FALSE; /* Process record_update (BMC algorithm) before everything else */ switch (ptpClock->portState) { case PTP_LISTENING: case PTP_PASSIVE: case PTP_SLAVE: case PTP_MASTER: /*State decision Event*/ /* If we received a valid Announce message, and can use it (record_update), then run the BMC algorithm */ if(ptpClock->record_update) { DBG2("event STATE_DECISION_EVENT\n"); ptpClock->record_update = FALSE; state = bmc(ptpClock->foreign, rtOpts, ptpClock); if(state != ptpClock->portState) toState(state, rtOpts, ptpClock); } break; default: break; } switch (ptpClock->portState) { case PTP_FAULTY: /* imaginary troubleshooting */ 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: handle(rtOpts, ptpClock); /* * handle SLAVE timers: * - No Announce message was received * - Time to send new delayReq (miss of delayResp is not monitored explicitelly) */ if (timerExpired(ANNOUNCE_RECEIPT_TIMER, ptpClock->itimer)) { DBG("event ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES\n"); ptpClock->number_foreign_records = 0; ptpClock->foreign_record_i = 0; if(!ptpClock->slaveOnly && ptpClock->clockQuality.clockClass != SLAVE_ONLY_CLOCK_CLASS) { m1(rtOpts,ptpClock); toState(PTP_MASTER, rtOpts, ptpClock); } else { /* * Force a reset when getting a timeout in state listening, that will lead to an IGMP reset * previously this was not the case when we were already in LISTENING mode */ toState(PTP_LISTENING, rtOpts, ptpClock); } } if (timerExpired(OPERATOR_MESSAGES_TIMER, ptpClock->itimer)) { reset_operator_messages(rtOpts, ptpClock); } if (ptpClock->delayMechanism == E2E) { if(timerExpired(DELAYREQ_INTERVAL_TIMER, ptpClock->itimer)) { DBG2("event DELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n"); issueDelayReq(rtOpts,ptpClock); } } else if (ptpClock->delayMechanism == P2P) { if (timerExpired(PDELAYREQ_INTERVAL_TIMER, ptpClock->itimer)) { DBGV("event PDELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n"); issuePDelayReq(rtOpts,ptpClock); } /* FIXME: Path delay should also rearm its timer with the value received from the Master */ } if (ptpClock->leap59 || ptpClock->leap61) DBGV("seconds to midnight: %.3f\n",secondsToMidnight()); /* leap second period is over */ if(timerExpired(LEAP_SECOND_PAUSE_TIMER,ptpClock->itimer) && ptpClock->leapSecondInProgress) { /* * do not unpause offset calculation just * yet, just indicate and it will be * unpaused in handleAnnounce() */ ptpClock->leapSecondPending = FALSE; timerStop(LEAP_SECOND_PAUSE_TIMER,ptpClock->itimer); } /* check if leap second is near and if we should pause updates */ if( ptpClock->leapSecondPending && !ptpClock->leapSecondInProgress && (secondsToMidnight() <= getPauseAfterMidnight(ptpClock->logAnnounceInterval))) { WARNING("=== Leap second event imminent - pausing " "clock and offset updates\n"); ptpClock->leapSecondInProgress = TRUE; #if !defined(__APPLE__) if(!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 */ /* * start pause timer from now until [pause] after * midnight, plus an extra second if inserting * a leap second */ timerStart(LEAP_SECOND_PAUSE_TIMER, secondsToMidnight() + (int)ptpClock->leap61 + getPauseAfterMidnight(ptpClock->logAnnounceInterval), ptpClock->itimer); } break; case PTP_MASTER: /* * handle SLAVE 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) */ 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)) { DBGV("event PDELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n"); issuePDelayReq(rtOpts,ptpClock); } } // TODO: why is handle() below expiretimer, while in slave is the opposite handle(rtOpts, ptpClock); if (ptpClock->slaveOnly || ptpClock->clockQuality.clockClass == SLAVE_ONLY_CLOCK_CLASS) toState(PTP_LISTENING, rtOpts, ptpClock); break; case PTP_DISABLED: handle(rtOpts, ptpClock); break; default: DBG("(doState) do unrecognized state\n"); break; } } /* check and handle received messages */ void handle(RunTimeOpts *rtOpts, PtpClock *ptpClock) { int ret; ssize_t length; Boolean isFromSelf; TimeInternal time = { 0, 0 }; if (!ptpClock->message_activity) { ret = netSelect(0, &ptpClock->netPath); if (ret < 0) { PERROR("failed to poll sockets"); toState(PTP_FAULTY, rtOpts, ptpClock); return; } else if (!ret) { /* DBGV("handle: nothing\n"); */ return; } /* else length > 0 */ } DBGV("handle: something\n"); /* TODO: this should be based on the select actual FDs (if(FD_ISSET(...)) */ length = netRecvEvent(ptpClock->msgIbuf, &time, &ptpClock->netPath); if (length < 0) { PERROR("failed to receive on the event socket"); toState(PTP_FAULTY, rtOpts, ptpClock); return; } else if (!length) { length = netRecvGeneral(ptpClock->msgIbuf, &time, &ptpClock->netPath); if (length < 0) { PERROR("failed to receive on the general socket"); 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) { ERROR("message shorter than header length\n"); toState(PTP_FAULTY, rtOpts, ptpClock); return; } msgUnpackHeader(ptpClock->msgIbuf, &ptpClock->msgTmpHeader); if (ptpClock->msgTmpHeader.versionPTP != ptpClock->versionNumber) { DBG2("ignore version %d message\n", ptpClock->msgTmpHeader.versionPTP); return; } if(ptpClock->msgTmpHeader.domainNumber != ptpClock->domainNumber) { DBG2("ignore message from domainNumber %d\n", ptpClock->msgTmpHeader.domainNumber); return; } /*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 */ if (!isFromSelf && time.seconds > 0) subTime(&time, &time, &rtOpts->inboundLatency); #ifdef PTPD_DBG /* easy display of received messages */ char *st; switch(ptpClock->msgTmpHeader.messageType) { case ANNOUNCE: st = "Announce"; break; case SYNC: st = "Sync"; break; case FOLLOW_UP: st = "FollowUp"; break; case DELAY_REQ: st = "DelayReq"; break; case DELAY_RESP: st = "DelayResp"; break; case MANAGEMENT: st = "Management"; break; default: st = "Unk"; break; } DBG(" ==> %s received\n", st); #endif /* * on the table below, note that only the event messsages are passed the local time, * (collected by us by loopback+kernel TS, and adjusted with UTC seconds * * (SYNC / DELAY_REQ / PDELAY_REQ / PDELAY_RESP) */ switch(ptpClock->msgTmpHeader.messageType) { case ANNOUNCE: handleAnnounce(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, rtOpts, ptpClock); break; case SYNC: handleSync(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, &time, isFromSelf, rtOpts, ptpClock); break; case FOLLOW_UP: handleFollowUp(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, rtOpts, ptpClock); break; case DELAY_REQ: handleDelayReq(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, &time, isFromSelf, rtOpts, ptpClock); break; case PDELAY_REQ: handlePDelayReq(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, &time, isFromSelf, rtOpts, ptpClock); break; case DELAY_RESP: handleDelayResp(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, rtOpts, ptpClock); break; case PDELAY_RESP: handlePDelayResp(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, &time, length, isFromSelf, rtOpts, ptpClock); break; case PDELAY_RESP_FOLLOW_UP: handlePDelayRespFollowUp(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, rtOpts, ptpClock); break; case MANAGEMENT: handleManagement(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, rtOpts, ptpClock); break; case SIGNALING: handleSignaling(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, rtOpts, ptpClock); break; default: DBG("handle: unrecognized message\n"); break; } if (rtOpts->displayPackets) msgDump(ptpClock); } /*spec 9.5.3*/ void handleAnnounce(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock) { Boolean isFromCurrentParent = FALSE; DBGV("HandleAnnounce : Announce message received : \n"); if(length < ANNOUNCE_LENGTH) { ERROR("short Announce message\n"); toState(PTP_FAULTY, rtOpts, ptpClock); return; } //DBGV(" >> HandleAnnounce : %d \n", ptpClock->portState); switch (ptpClock->portState) { case PTP_INITIALIZING: case PTP_FAULTY: case PTP_DISABLED: DBG("Handleannounce : disregard\n"); return; 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 */ ptpClock->record_update = TRUE; isFromCurrentParent = !memcmp( ptpClock->parentPortIdentity.clockIdentity, header->sourcePortIdentity.clockIdentity, CLOCK_IDENTITY_LENGTH) && (ptpClock->parentPortIdentity.portNumber == header->sourcePortIdentity.portNumber); switch (isFromCurrentParent) { case TRUE: msgUnpackAnnounce(ptpClock->msgIbuf, &ptpClock->msgTmp.announce); /* update datasets (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)); if(ptpClock->leapSecondInProgress) { /* * if leap second period is over * (pending == FALSE, inProgress == * TRUE), unpause offset calculation * (received first announce after leap * second) */ if (!ptpClock->leapSecondPending) { WARNING("=== Leap second event over - " "resuming clock and offset updates\n"); ptpClock->leapSecondInProgress=FALSE; ptpClock->leap59 = FALSE; ptpClock->leap61 = FALSE; unsetTimexFlags(STA_INS | STA_DEL, TRUE); } } 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)), ptpClock->itimer); #ifdef PTP_EXPERIMENTAL // remember IP address of our master for -U option // todo: add this to bmc(), to cover the very first packet ptpClock->MasterAddr = ptpClock->netPath.lastRecvAddr; #endif break; case FALSE: /*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, (ptpClock->announceReceiptTimeout) * (pow(2,ptpClock->logAnnounceInterval)), ptpClock->itimer); break; default: DBG("HandleAnnounce : (isFromCurrentParent)" "strange value ! \n"); return; } /* switch on (isFromCurrentParrent) */ break; /* * 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 * */ case PTP_PASSIVE: if (isFromSelf) { DBGV("HandleAnnounce : Ignore message from self \n"); return; } /* * Valid announce message is received : BMC algorithm * will be executed */ ptpClock->record_update = TRUE; isFromCurrentParent = !memcmp( ptpClock->parentPortIdentity.clockIdentity, header->sourcePortIdentity.clockIdentity, CLOCK_IDENTITY_LENGTH) && (ptpClock->parentPortIdentity.portNumber == header->sourcePortIdentity.portNumber); if (isFromCurrentParent) { msgUnpackAnnounce(ptpClock->msgIbuf, &ptpClock->msgTmp.announce); /* TODO: not in spec * datasets should not be updated by another master * this is the reason why we are PASSIVE and not SLAVE * this should be p1(ptpClock, rtOpts); */ /* update datasets (file bmc.c) */ s1(header,&ptpClock->msgTmp.announce,ptpClock, rtOpts); DBG("___ Announce: received Announce from current Master, so reset the Announce timer\n\n"); /*Reset Timer handling Announce receipt timeout*/ timerStart(ANNOUNCE_RECEIPT_TIMER, (ptpClock->announceReceiptTimeout) * (pow(2,ptpClock->logAnnounceInterval)), ptpClock->itimer); } else { /*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) */ DBG("___ Announce: received Announce from another master, will add to the list, as it might be better\n\n"); DBGV("this is to be decided immediatly by bmc())\n\n"); addForeign(ptpClock->msgIbuf,header,ptpClock); } break; case PTP_MASTER: case PTP_LISTENING: /* listening mode still causes timeouts in order to send IGMP refreshes */ default : 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 */ break; } /* switch on (port_state) */ } void handleSync(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInternal *time, Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock) { TimeInternal OriginTimestamp; TimeInternal correctionField; 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: case PTP_FAULTY: case PTP_DISABLED: DBGV("HandleSync : disregard\n"); return; case PTP_UNCALIBRATED: case PTP_SLAVE: if (isFromSelf) { DBGV("HandleSync: Ignore message from self \n"); return; } isFromCurrentParent = !memcmp(ptpClock->parentPortIdentity.clockIdentity, header->sourcePortIdentity.clockIdentity, 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 ); if (ptpClock->delayMechanism == E2E) timerStart(DELAYREQ_INTERVAL_TIMER, pow(2,ptpClock->logMinDelayReqInterval), ptpClock->itimer); else if (ptpClock->delayMechanism == P2P) timerStart(PDELAYREQ_INTERVAL_TIMER, pow(2,ptpClock->logMinPdelayReqInterval), ptpClock->itimer); } ptpClock->sync_receive_time.seconds = time->seconds; ptpClock->sync_receive_time.nanoseconds = time->nanoseconds; recordSync(rtOpts, header->sequenceId, time); if ((header->flagField0 & PTP_TWO_STEP) == PTP_TWO_STEP) { DBG2("HandleSync: waiting for follow-up \n"); ptpClock->waitingForFollow = TRUE; ptpClock->recvSyncSequenceId = header->sequenceId; /*Save correctionField of Sync message*/ integer64_to_internalTime( header->correctionField, &correctionField); ptpClock->lastSyncCorrectionField.seconds = correctionField.seconds; ptpClock->lastSyncCorrectionField.nanoseconds = correctionField.nanoseconds; break; } else { msgUnpackSync(ptpClock->msgIbuf, &ptpClock->msgTmp.sync); integer64_to_internalTime( ptpClock->msgTmpHeader.correctionField, &correctionField); timeInternal_display(&correctionField); ptpClock->waitingForFollow = FALSE; toInternalTime(&OriginTimestamp, &ptpClock->msgTmp.sync.originTimestamp); updateOffset(&OriginTimestamp, &ptpClock->sync_receive_time, &ptpClock->ofm_filt,rtOpts, ptpClock,&correctionField); updateClock(rtOpts,ptpClock); break; } } else { DBG("HandleSync: Sync message received from " "another Master not our own \n"); } break; case PTP_MASTER: default : if (!isFromSelf) { DBGV("HandleSync: Sync message received from " "another Master \n"); break; } if (ptpClock->twoStepFlag) { DBGV("HandleSync: going to send followup message\n "); /*Add latency*/ addTime(time,time,&rtOpts->outboundLatency); issueFollowup(time,rtOpts,ptpClock); break; } else { DBGV("HandleSync: Sync message received from self\n "); } } } 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) { ERROR("short Follow up message\n"); toState(PTP_FAULTY, rtOpts, ptpClock); return; } if (isFromSelf) { DBGV("Handlefollowup : Ignore message from self \n"); return; } switch (ptpClock->portState) { case PTP_INITIALIZING: case PTP_FAULTY: case PTP_DISABLED: case PTP_LISTENING: DBGV("Handfollowup : disregard\n"); return; case PTP_UNCALIBRATED: case PTP_SLAVE: isFromCurrentParent = !memcmp(ptpClock->parentPortIdentity.clockIdentity, header->sourcePortIdentity.clockIdentity, CLOCK_IDENTITY_LENGTH) && (ptpClock->parentPortIdentity.portNumber == header->sourcePortIdentity.portNumber); if (isFromCurrentParent) { if (ptpClock->waitingForFollow) { if ((ptpClock->recvSyncSequenceId == header->sequenceId)) { msgUnpackFollowUp(ptpClock->msgIbuf, &ptpClock->msgTmp.follow); ptpClock->waitingForFollow = FALSE; toInternalTime(&preciseOriginTimestamp, &ptpClock->msgTmp.follow.preciseOriginTimestamp); integer64_to_internalTime(ptpClock->msgTmpHeader.correctionField, &correctionField); addTime(&correctionField,&correctionField, &ptpClock->lastSyncCorrectionField); /* send_time = preciseOriginTimestamp (received inside followup) recv_time = sync_receive_time (received as CMSG in handleEvent) */ updateOffset(&preciseOriginTimestamp, &ptpClock->sync_receive_time,&ptpClock->ofm_filt, rtOpts,ptpClock, &correctionField); updateClock(rtOpts,ptpClock); break; } else INFO("Ignored followup, SequenceID doesn't match with " "last Sync message \n"); } else DBG2("Ignored followup, Slave was not waiting a follow up " "message \n"); } else DBG2("Ignored, Follow up message is not from current parent \n"); case PTP_MASTER: case PTP_PASSIVE: DBGV("Ignored, Follow up message received from another master \n"); break; default: DBG("do unrecognized state1\n"); break; } /* Switch on (port_state) */ } void 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); return; } switch (ptpClock->portState) { case PTP_INITIALIZING: case PTP_FAULTY: case PTP_DISABLED: case PTP_UNCALIBRATED: case PTP_LISTENING: case PTP_PASSIVE: DBGV("HandledelayReq : disregard\n"); return; case PTP_SLAVE: if (isFromSelf) { DBG("==> Handle DelayReq (%d)\n", header->sequenceId); if ((ptpClock->sentDelayReqSequenceId - 1) != header->sequenceId) { INFO("HandledelayReq : disregard delayreq because of wrong SeqNo\n"); break; } /* * Get sending timestamp from IP stack * with SO_TIMESTAMP */ /* * Make sure we process the REQ _before_ the RESP. While we could do this by any order, * (because it's implicitly indexed by (ptpClock->sentDelayReqSequenceId - 1), this is * now made explicit */ ptpClock->waitingForDelayResp = TRUE; ptpClock->delay_req_send_time.seconds = time->seconds; ptpClock->delay_req_send_time.nanoseconds = time->nanoseconds; /*Add latency*/ addTime(&ptpClock->delay_req_send_time, &ptpClock->delay_req_send_time, &rtOpts->outboundLatency); break; } else { DBG2("HandledelayReq : disreguard delayreq from other client\n"); } break; case PTP_MASTER: msgUnpackHeader(ptpClock->msgIbuf, &ptpClock->delayReqHeader); #ifdef PTP_EXPERIMENTAL // remember IP address of this client for -U option ptpClock->LastSlaveAddr = ptpClock->netPath.lastRecvAddr; #endif issueDelayResp(time,&ptpClock->delayReqHeader, rtOpts,ptpClock); break; default: DBG("do unrecognized state2\n"); break; } } else /* (Peer to Peer mode) */ ERROR("Delay messages are ignored in Peer to Peer mode\n"); } void handleDelayResp(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock) { if (ptpClock->delayMechanism == E2E) { Boolean isFromCurrentParent = FALSE; TimeInternal requestReceiptTimestamp; TimeInternal correctionField; DBGV("delayResp message received : \n"); if(length < DELAY_RESP_LENGTH) { ERROR("short DelayResp message\n"); toState(PTP_FAULTY, rtOpts, ptpClock); return; } switch(ptpClock->portState) { case PTP_INITIALIZING: case PTP_FAULTY: case PTP_DISABLED: case PTP_UNCALIBRATED: case PTP_LISTENING: DBGV("HandledelayResp : disregard\n"); return; case PTP_SLAVE: msgUnpackDelayResp(ptpClock->msgIbuf, &ptpClock->msgTmp.resp); if ((memcmp(ptpClock->parentPortIdentity.clockIdentity, header->sourcePortIdentity.clockIdentity, CLOCK_IDENTITY_LENGTH) == 0 ) && (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)== header->sequenceId) && (ptpClock->portIdentity.portNumber == ptpClock->msgTmp.resp.requestingPortIdentity.portNumber) && isFromCurrentParent) { DBG("==> Handle DelayResp (%d)\n", header->sequenceId); if (!ptpClock->waitingForDelayResp) { break; } ptpClock->waitingForDelayResp = FALSE; toInternalTime(&requestReceiptTimestamp, &ptpClock->msgTmp.resp.receiveTimestamp); ptpClock->delay_req_receive_time.seconds = requestReceiptTimestamp.seconds; 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) */ updateDelay(&ptpClock->owd_filt, rtOpts,ptpClock, &correctionField); if (ptpClock->waiting_for_first_delayresp) { ptpClock->waiting_for_first_delayresp = FALSE; NOTICE(" received first DelayResp from Master\n"); } if (rtOpts->ignore_delayreq_interval_master == 0) { DBGV("current delay_req: %d new delay req: %d \n", ptpClock->logMinDelayReqInterval, header->logMessageInterval); /* Accept new DelayReq value from the Master */ if (ptpClock->logMinDelayReqInterval != header->logMessageInterval) { 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; /* FIXME: the actual rearming of this timer with the new value only happens later in doState()/issueDelayReq() */ } else { if (ptpClock->logMinDelayReqInterval != rtOpts->subsequent_delayreq) { NOTICE(" received new DelayReq frequency %d from command line (was: %d)\n", rtOpts->subsequent_delayreq, ptpClock->logMinDelayReqInterval); } ptpClock->logMinDelayReqInterval = rtOpts->subsequent_delayreq; } } else { DBG("HandledelayResp : delayResp doesn't match with the delayReq. \n"); break; } } } else { /* (Peer to Peer mode) */ ERROR("Delay messages are disregarded in Peer to Peer mode \n"); } } void handlePDelayReq(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInternal *time, Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock) { if (ptpClock->delayMechanism == P2P) { DBGV("PdelayReq message received : \n"); if(length < PDELAY_REQ_LENGTH) { ERROR("short PDelayReq message\n"); toState(PTP_FAULTY, rtOpts, ptpClock); return; } switch (ptpClock->portState ) { case PTP_INITIALIZING: case PTP_FAULTY: case PTP_DISABLED: case PTP_UNCALIBRATED: case PTP_LISTENING: DBGV("HandlePdelayReq : disregard\n"); return; case PTP_SLAVE: case PTP_MASTER: case PTP_PASSIVE: if (isFromSelf) { /* * Get sending timestamp from IP stack * with SO_TIMESTAMP */ ptpClock->pdelay_req_send_time.seconds = time->seconds; ptpClock->pdelay_req_send_time.nanoseconds = time->nanoseconds; /*Add latency*/ addTime(&ptpClock->pdelay_req_send_time, &ptpClock->pdelay_req_send_time, &rtOpts->outboundLatency); break; } else { msgUnpackHeader(ptpClock->msgIbuf, &ptpClock->PdelayReqHeader); issuePDelayResp(time, header, rtOpts, ptpClock); break; } 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, 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) { ERROR("short PDelayResp message\n"); toState(PTP_FAULTY, rtOpts, ptpClock); return; } switch (ptpClock->portState ) { case PTP_INITIALIZING: case PTP_FAULTY: case PTP_DISABLED: case PTP_UNCALIBRATED: case PTP_LISTENING: DBGV("HandlePdelayResp : disregard\n"); return; case PTP_SLAVE: case PTP_MASTER: if (ptpClock->twoStepFlag && isFromSelf) { addTime(time,time,&rtOpts->outboundLatency); issuePDelayRespFollowUp(time, &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.portNumber); #endif 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 */ if ((header->flagField0 & PTP_TWO_STEP) == PTP_TWO_STEP) { /*Store t4 (Fig 35)*/ ptpClock->pdelay_resp_receive_time.seconds = time->seconds; ptpClock->pdelay_resp_receive_time.nanoseconds = time->nanoseconds; /*store t2 (Fig 35)*/ toInternalTime(&requestReceiptTimestamp, &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; } else { /* One step Clock */ /*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); } ptpClock->recvPDelayRespSequenceId = header->sequenceId; break; } else { DBGV("HandlePdelayResp : Pdelayresp doesn't " "match with the PdelayReq. \n"); break; } break; /* XXX added by gnn for safety */ default: DBG("do unrecognized state4\n"); break; } } else { /* (End to End mode..) */ ERROR("Peer Delay messages are disregarded in End to End " "mode \n"); } } 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: case PTP_DISABLED: case PTP_UNCALIBRATED: DBGV("HandlePdelayResp : disregard\n"); return; case PTP_SLAVE: case PTP_MASTER: if ((header->sequenceId == ptpClock->sentPDelayReqSequenceId-1) && (header->sequenceId == ptpClock->recvPDelayRespSequenceId)) { msgUnpackPDelayRespFollowUp( 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; integer64_to_internalTime( ptpClock->msgTmpHeader.correctionField, &correctionField); addTime(&correctionField,&correctionField, &ptpClock->lastPdelayRespCorrectionField); updatePeerDelay (&ptpClock->owd_filt, rtOpts, ptpClock, &correctionField,TRUE); break; } default: DBGV("Disregard PdelayRespFollowUp message \n"); } } else { /* (End to End mode..) */ ERROR("Peer Delay messages are disregarded in End to End " "mode \n"); } } void handleManagement(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock) { DBGV("Management message received : \n"); if (isFromSelf) { DBGV("handleManagement: Ignore message from self \n"); return; } msgUnpackManagement(ptpClock->msgIbuf,&ptpClock->msgTmp.manage, header, ptpClock); if(ptpClock->msgTmp.manage.tlv == NULL) { DBGV("handleManagement: TLV is empty\n"); return; } /* is this an error status management TLV? */ if(ptpClock->msgTmp.manage.tlv->tlvType == TLV_MANAGEMENT_ERROR_STATUS) { DBGV("handleManagement: Error Status TLV\n"); unpackMMErrorStatus(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMErrorStatus(&ptpClock->msgTmp.manage); /* cleanup msgTmp managementTLV */ if(ptpClock->msgTmp.manage.tlv) { DBGV("cleanup ptpClock->msgTmp.manage message \n"); if(ptpClock->msgTmp.manage.tlv->dataField) { freeMMErrorStatusTLV(ptpClock->msgTmp.manage.tlv); free(ptpClock->msgTmp.manage.tlv->dataField); } free(ptpClock->msgTmp.manage.tlv); } return; } else if (ptpClock->msgTmp.manage.tlv->tlvType != TLV_MANAGEMENT) { /* do nothing, implemention specific handling */ DBGV("handleManagement: Currently unsupported management TLV type\n"); return; } switch(ptpClock->msgTmp.manage.tlv->managementId) { case MM_NULL_MANAGEMENT: DBGV("handleManagement: Null Management\n"); handleMMNullManagement(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_CLOCK_DESCRIPTION: DBGV("handleManagement: Clock Description\n"); unpackMMClockDescription(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMClockDescription(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_USER_DESCRIPTION: DBGV("handleManagement: User Description\n"); unpackMMUserDescription(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMUserDescription(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_SAVE_IN_NON_VOLATILE_STORAGE: DBGV("handleManagement: Save In Non-Volatile Storage\n"); handleMMSaveInNonVolatileStorage(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_RESET_NON_VOLATILE_STORAGE: DBGV("handleManagement: Reset Non-Volatile Storage\n"); handleMMResetNonVolatileStorage(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_INITIALIZE: DBGV("handleManagement: Initialize\n"); unpackMMInitialize(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMInitialize(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_DEFAULT_DATA_SET: DBGV("handleManagement: Default Data Set\n"); unpackMMDefaultDataSet(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMDefaultDataSet(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_CURRENT_DATA_SET: DBGV("handleManagement: Current Data Set\n"); unpackMMCurrentDataSet(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMCurrentDataSet(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_PARENT_DATA_SET: DBGV("handleManagement: Parent Data Set\n"); unpackMMParentDataSet(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMParentDataSet(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_TIME_PROPERTIES_DATA_SET: DBGV("handleManagement: TimeProperties Data Set\n"); unpackMMTimePropertiesDataSet(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMTimePropertiesDataSet(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_PORT_DATA_SET: DBGV("handleManagement: Port Data Set\n"); unpackMMPortDataSet(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMPortDataSet(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_PRIORITY1: DBGV("handleManagement: Priority1\n"); unpackMMPriority1(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMPriority1(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_PRIORITY2: DBGV("handleManagement: Priority2\n"); unpackMMPriority2(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMPriority2(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_DOMAIN: DBGV("handleManagement: Domain\n"); unpackMMDomain(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMDomain(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_SLAVE_ONLY: DBGV("handleManagement: Slave Only\n"); unpackMMSlaveOnly(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMSlaveOnly(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_LOG_ANNOUNCE_INTERVAL: DBGV("handleManagement: Log Announce Interval\n"); unpackMMLogAnnounceInterval(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMLogAnnounceInterval(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_ANNOUNCE_RECEIPT_TIMEOUT: DBGV("handleManagement: Announce Receipt Timeout\n"); unpackMMAnnounceReceiptTimeout(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMAnnounceReceiptTimeout(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_LOG_SYNC_INTERVAL: DBGV("handleManagement: Log Sync Interval\n"); unpackMMLogSyncInterval(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMLogSyncInterval(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_VERSION_NUMBER: DBGV("handleManagement: Version Number\n"); unpackMMVersionNumber(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMVersionNumber(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_ENABLE_PORT: DBGV("handleManagement: Enable Port\n"); handleMMEnablePort(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_DISABLE_PORT: DBGV("handleManagement: Disable Port\n"); handleMMDisablePort(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_TIME: DBGV("handleManagement: Time\n"); unpackMMTime(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMTime(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_CLOCK_ACCURACY: DBGV("handleManagement: Clock Accuracy\n"); unpackMMClockAccuracy(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMClockAccuracy(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_UTC_PROPERTIES: DBGV("handleManagement: Utc Properties\n"); unpackMMUtcProperties(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMUtcProperties(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_TRACEABILITY_PROPERTIES: DBGV("handleManagement: Traceability Properties\n"); unpackMMTraceabilityProperties(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMTraceabilityProperties(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_DELAY_MECHANISM: DBGV("handleManagement: Delay Mechanism\n"); unpackMMDelayMechanism(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMDelayMechanism(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_LOG_MIN_PDELAY_REQ_INTERVAL: DBGV("handleManagement: Log Min Pdelay Req Interval\n"); unpackMMLogMinPdelayReqInterval(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock); handleMMLogMinPdelayReqInterval(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock); break; case MM_FAULT_LOG: case MM_FAULT_LOG_RESET: case MM_TIMESCALE_PROPERTIES: case MM_UNICAST_NEGOTIATION_ENABLE: case MM_PATH_TRACE_LIST: case MM_PATH_TRACE_ENABLE: case MM_GRANDMASTER_CLUSTER_TABLE: case MM_UNICAST_MASTER_TABLE: case MM_UNICAST_MASTER_MAX_TABLE_SIZE: case MM_ACCEPTABLE_MASTER_TABLE: case MM_ACCEPTABLE_MASTER_TABLE_ENABLED: case MM_ACCEPTABLE_MASTER_MAX_TABLE_SIZE: case MM_ALTERNATE_MASTER: case MM_ALTERNATE_TIME_OFFSET_ENABLE: case MM_ALTERNATE_TIME_OFFSET_NAME: case MM_ALTERNATE_TIME_OFFSET_MAX_KEY: case MM_ALTERNATE_TIME_OFFSET_PROPERTIES: case MM_TRANSPARENT_CLOCK_DEFAULT_DATA_SET: case MM_TRANSPARENT_CLOCK_PORT_DATA_SET: case MM_PRIMARY_DOMAIN: DBGV("handleManagement: Currently unsupported managementTLV %d\n", ptpClock->msgTmp.manage.tlv->managementId); handleErrorManagementMessage(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock, ptpClock->msgTmp.manage.tlv->managementId, NOT_SUPPORTED); break; default: DBGV("handleManagement: Unknown managementTLV %d\n", ptpClock->msgTmp.manage.tlv->managementId); handleErrorManagementMessage(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock, ptpClock->msgTmp.manage.tlv->managementId, NO_SUCH_ID); } /* send management message response or acknowledge */ if(ptpClock->outgoingManageTmp.tlv->tlvType == TLV_MANAGEMENT) { if(ptpClock->outgoingManageTmp.actionField == RESPONSE || ptpClock->outgoingManageTmp.actionField == ACKNOWLEDGE) { issueManagementRespOrAck(&ptpClock->outgoingManageTmp, rtOpts, ptpClock); } } else if(ptpClock->outgoingManageTmp.tlv->tlvType == TLV_MANAGEMENT_ERROR_STATUS) { issueManagementErrorStatus(&ptpClock->outgoingManageTmp, rtOpts, ptpClock); } /* cleanup msgTmp managementTLV */ freeManagementTLV(&ptpClock->msgTmp.manage); /* cleanup outgoing managementTLV */ freeManagementTLV(&ptpClock->outgoingManageTmp); } 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 issueAnnounce(RunTimeOpts *rtOpts,PtpClock *ptpClock) { msgPackAnnounce(ptpClock->msgObuf,ptpClock); if (!netSendGeneral(ptpClock->msgObuf,ANNOUNCE_LENGTH, &ptpClock->netPath, 0)) { toState(PTP_FAULTY,rtOpts,ptpClock); DBGV("Announce message can't be sent -> FAULTY state \n"); } else { DBGV("Announce MSG sent ! \n"); ptpClock->sentAnnounceSequenceId++; } } /*Pack and send on event multicast ip adress a Sync message*/ void issueSync(RunTimeOpts *rtOpts,PtpClock *ptpClock) { Timestamp originTimestamp; TimeInternal internalTime; getTime(&internalTime); fromInternalTime(&internalTime,&originTimestamp); 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++; } } /*Pack and send on general multicast ip adress a FollowUp message*/ void issueFollowup(TimeInternal *time,RunTimeOpts *rtOpts,PtpClock *ptpClock) { Timestamp preciseOriginTimestamp; fromInternalTime(time,&preciseOriginTimestamp); msgPackFollowUp(ptpClock->msgObuf,&preciseOriginTimestamp,ptpClock); if (!netSendGeneral(ptpClock->msgObuf,FOLLOW_UP_LENGTH, &ptpClock->netPath, 0)) { toState(PTP_FAULTY,rtOpts,ptpClock); DBGV("FollowUp message can't be sent -> FAULTY state \n"); } else { DBGV("FollowUp MSG sent ! \n"); } } /*Pack and send on event multicast ip adress a DelayReq message*/ void issueDelayReq(RunTimeOpts *rtOpts,PtpClock *ptpClock) { Timestamp originTimestamp; TimeInternal internalTime; 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); fromInternalTime(&internalTime,&originTimestamp); // uses current sentDelayReqSequenceId msgPackDelayReq(ptpClock->msgObuf,&originTimestamp,ptpClock); Integer32 dst = 0; #ifdef PTP_EXPERIMENTAL if (rtOpts->do_hybrid_mode) { dst = ptpClock->MasterAddr; } #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++; /* 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); } } /*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); 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++; } } /*Pack and send on event multicast ip adress a PDelayResp message*/ void issuePDelayResp(TimeInternal *time,MsgHeader *header,RunTimeOpts *rtOpts, PtpClock *ptpClock) { Timestamp requestReceiptTimestamp; fromInternalTime(time,&requestReceiptTimestamp); 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"); } } /*Pack and send on event multicast ip adress a DelayResp message*/ void issueDelayResp(TimeInternal *time,MsgHeader *header,RunTimeOpts *rtOpts, PtpClock *ptpClock) { Timestamp requestReceiptTimestamp; fromInternalTime(time,&requestReceiptTimestamp); msgPackDelayResp(ptpClock->msgObuf,header,&requestReceiptTimestamp, ptpClock); Integer32 dst = 0; #ifdef PTP_EXPERIMENTAL if (rtOpts->do_hybrid_mode) { dst = ptpClock->LastSlaveAddr; } #endif if (!netSendGeneral(ptpClock->msgObuf,PDELAY_RESP_LENGTH, &ptpClock->netPath, dst)) { toState(PTP_FAULTY,rtOpts,ptpClock); DBGV("delayResp message can't be sent -> FAULTY state \n"); } else { DBGV("PDelayResp MSG sent ! \n"); } } void issuePDelayRespFollowUp(TimeInternal *time, MsgHeader *header, RunTimeOpts *rtOpts, PtpClock *ptpClock) { Timestamp responseOriginTimestamp; fromInternalTime(time,&responseOriginTimestamp); 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"); } } void issueManagement(MsgHeader *header,MsgManagement *manage,RunTimeOpts *rtOpts, PtpClock *ptpClock) {} void issueManagementRespOrAck(MsgManagement *outgoing, RunTimeOpts *rtOpts, PtpClock *ptpClock) { /* pack ManagementTLV */ msgPackManagementTLV( ptpClock->msgObuf, outgoing, ptpClock); /* set header messageLength, the outgoing->tlv->lengthField is now valid */ outgoing->header.messageLength = MANAGEMENT_LENGTH + TLV_LENGTH + outgoing->tlv->lengthField; msgPackManagement( ptpClock->msgObuf, outgoing, ptpClock); if(!netSendGeneral(ptpClock->msgObuf, outgoing->header.messageLength, &ptpClock->netPath, 0)) { DBGV("Management response/acknowledge can't be sent -> FAULTY state \n"); toState(PTP_FAULTY, rtOpts, ptpClock); } else { DBGV("Management response/acknowledge msg sent \n"); } } void issueManagementErrorStatus(MsgManagement *outgoing, RunTimeOpts *rtOpts, PtpClock *ptpClock) { /* pack ManagementErrorStatusTLV */ msgPackManagementErrorStatusTLV( ptpClock->msgObuf, outgoing, ptpClock); /* set header messageLength, the outgoing->tlv->lengthField is now valid */ outgoing->header.messageLength = MANAGEMENT_LENGTH + TLV_LENGTH + outgoing->tlv->lengthField; msgPackManagement( ptpClock->msgObuf, outgoing, ptpClock); if(!netSendGeneral(ptpClock->msgObuf, outgoing->header.messageLength, &ptpClock->netPath, 0)) { DBGV("Management error status can't be sent -> FAULTY state \n"); toState(PTP_FAULTY, rtOpts, ptpClock); } else { DBGV("Management error status msg sent \n"); } } 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 == ptpClock->foreign[j].foreignMasterPortIdentity.portNumber)) { /*Foreign Master is already in Foreignmaster data set*/ 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 < ptpClock->max_foreign_records) { ptpClock->number_foreign_records++; } j = ptpClock->foreign_record_i; /*Copy new foreign master data set from Announce message*/ copyClockIdentity(ptpClock->foreign[j].foreignMasterPortIdentity.clockIdentity, header->sourcePortIdentity.clockIdentity); 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 */ 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; } } ptpd-2.2.2/src/Doxyfile0000600000175000017500000017371611770426154013765 0ustar ernieernie# Doxyfile 1.5.8 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = PTPDv2 # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = Doxygen # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, # Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, # Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, # Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it parses. # With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this tag. # The format is ext=language, where ext is a file extension, and language is one of # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = YES # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = YES # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). GENERATE_HTMLHELP = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. # For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's # filter section matches. # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to FRAME, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. Other possible values # for this tag are: HIERARCHIES, which will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list; # ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which # disables this behavior completely. For backwards compatibility with previous # releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE # respectively. GENERATE_TREEVIEW = NONE # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = YES # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = YES # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = YES # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = YES # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Options related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = YES ptpd-2.2.2/src/def/0000700000175000017500000000000012127234262012766 5ustar ernieernieptpd-2.2.2/src/def/managementTLV/0000700000175000017500000000000012127234262015470 5ustar ernieernieptpd-2.2.2/src/def/managementTLV/domain.def0000600000175000017500000000034111770426154017425 0ustar ernieernie/* Spec Table 53 - DOMAIN management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( domainNumber, 1, UInteger8) OPERATE( reserved, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/logSyncInterval.def0000600000175000017500000000035611770426153021306 0ustar ernieernie/* Spec Table 64 - LOG_SYNC_INTERVAL management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( logSyncInterval, 1, Integer8) OPERATE( reserved, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/portDataSet.def0000600000175000017500000000116711770426153020416 0ustar ernieernie/* Spec Table 61 - PORT_DATA_SET management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( portIdentity, 10, PortIdentity) OPERATE( portState, 1, Enumeration8) OPERATE( logMinDelayReqInterval, 1, Integer8) OPERATE( peerMeanPathDelay, 8, TimeInterval) OPERATE( logAnnounceInterval, 1, Integer8) OPERATE( announceReceiptTimeout, 1, UInteger8) OPERATE( logSyncInterval, 1, Integer8) OPERATE( delayMechanism, 1, Enumeration8) OPERATE( logMinPdelayReqInterval, 1, Integer8) OPERATE( reserved, 0, NibbleUpper) OPERATE( versionNumber, 1, UInteger4Lower) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/priority2.def0000600000175000017500000000034111770426154020121 0ustar ernieernie/* Spec Table 52 - PRIORITY2 management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( priority2, 1, UInteger8) OPERATE( reserved, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/utcProperties.def0000600000175000017500000000042011770426154021024 0ustar ernieernie/* Spec Table 58 - UTC_PROPERTIES management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( currentUtcOffset, 2, Integer16) OPERATE( utcv_li59_li61, 1, Octet) OPERATE( reserved, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/slaveOnly.def0000600000175000017500000000033111770426154020131 0ustar ernieernie/* Spec Table 54 - SLAVE_ONLY management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( so, 1, Boolean) OPERATE( reserved, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/delayMechanism.def0000600000175000017500000000035711770426153021107 0ustar ernieernie/* Spec Table 65 - DELAY_MECHANISM management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( delayMechanism, 1, Enumeration8) OPERATE( reserved, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/managementTLV.def0000600000175000017500000000040611770426154020662 0ustar ernieernie/* Spec Table 39 - Management TLV fields */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( tlvType, 2, Enumeration16) OPERATE( lengthField, 2, UInteger16) OPERATE( managementId, 2, Enumeration16) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/errorStatus.def0000600000175000017500000000046211770426154020517 0ustar ernieernie/* Spec Table 71 - MANAGEMENT_ERROR_STATUS TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( managementId, 2, Enumeration16) OPERATE( reserved, 4, UInteger32) OPERATE( displayData, 1 + data->displayData.lengthField, PTPText) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/priority1.def0000600000175000017500000000034111770426154020120 0ustar ernieernie/* Spec Table 51 - PRIORITY1 management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( priority1, 1, UInteger8) OPERATE( reserved, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/currentDataSet.def0000600000175000017500000000044411770426153021111 0ustar ernieernie/* Spec Table 55 - CURRENT_DATA_SET management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( stepsRemoved, 2, UInteger16) OPERATE( offsetFromMaster, 8, TimeInterval) OPERATE( meanPathDelay, 8, TimeInterval) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/traceabilityProperties.def0000600000175000017500000000035311770426153022711 0ustar ernieernie/* Spec Table 59 - TRACEABILITY_PROPERTIES management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( ftra_ttra, 1, Octet) OPERATE( reserved, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/timePropertiesDataSet.def0000600000175000017500000000046111770426154022442 0ustar ernieernie/* Spec Table 57 - TIME_PROPERTIES_DATA_SET management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( currentUtcOffset, 2, Integer16) OPERATE( ftra_ttra_ptp_utcv_li59_li61, 1, Octet) OPERATE( timeSource, 1, Enumeration8) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/announceReceiptTimeout.def0000600000175000017500000000037511770426153022655 0ustar ernieernie/* Spec Table 63 - ANNOUNCE_RECEIPT_TIMEOUT management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( announceReceiptTimeout, 1, UInteger8) OPERATE( reserved, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/defaultDataSet.def0000600000175000017500000000074011770426153021052 0ustar ernieernie/* Spec Table 50 - DEFAULT_DATA_SET management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( so_tsc, 1, Octet) OPERATE( reserved0, 1, Octet) OPERATE( numberPorts, 2, UInteger16) OPERATE( priority1, 1, UInteger8) OPERATE( clockQuality, 4, ClockQuality) OPERATE( priority2, 1, UInteger8) OPERATE( clockIdentity, 8, ClockIdentity) OPERATE( domainNumber, 1, UInteger8) OPERATE( reserved1, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/clockDescription.def0000600000175000017500000000213011770426154021453 0ustar ernieernie/* Spec Table 41 - CLOCK_DESCRIPTION management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( clockType0, 1, Octet) OPERATE( clockType1, 1, Octet) OPERATE( physicalLayerProtocol, 1 + data->physicalLayerProtocol.lengthField, PTPText) OPERATE( physicalAddress, 2 + data->physicalAddress.addressLength, PhysicalAddress) OPERATE( protocolAddress, 4 + data->protocolAddress.addressLength, PortAddress) OPERATE( manufacturerIdentity0, 1, Octet) OPERATE( manufacturerIdentity1, 1, Octet) OPERATE( manufacturerIdentity2, 1, Octet) OPERATE( reserved, 1, Octet) OPERATE( productDescription, 1 + data->productDescription.lengthField, PTPText) OPERATE( revisionData, 1 + data->revisionData.lengthField, PTPText) OPERATE( userDescription, 1 + data->userDescription.lengthField, PTPText) OPERATE( profileIdentity0, 1, Octet) OPERATE( profileIdentity1, 1, Octet) OPERATE( profileIdentity2, 1, Octet) OPERATE( profileIdentity3, 1, Octet) OPERATE( profileIdentity4, 1, Octet) OPERATE( profileIdentity5, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/initialize.def0000600000175000017500000000032011770426154020314 0ustar ernieernie/* Spec Table 43 - USER_DESCRIPTION management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( initializeKey, 2, UInteger16) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/logMinPdelayReqInterval.def0000600000175000017500000000040011770426154022713 0ustar ernieernie/* Spec Table 66 - LOG_MIN_PDELAY_REQ_INTERVAL management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( logMinPdelayReqInterval, 1, Integer8) OPERATE( reserved, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/parentDataSet.def0000600000175000017500000000110411770426154020713 0ustar ernieernie/* Spec Table 56 - PARENT_DATA_SET management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( parentPortIdentity, 10, PortIdentity) OPERATE( PS, 1, Boolean) OPERATE( reserved, 1, Octet) OPERATE( observedParentOffsetScaledLogVariance, 2, UInteger16) OPERATE( observedParentClockPhaseChangeRate, 4, Integer32) OPERATE( grandmasterPriority1, 1, UInteger8) OPERATE( grandmasterClockQuality, 4, ClockQuality) OPERATE( grandmasterPriority2, 1, UInteger8) OPERATE( grandmasterIdentity, 8, ClockIdentity) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/versionNumber.def0000600000175000017500000000042411770426154021016 0ustar ernieernie/* Spec Table 67 - VERSION_NUMBER management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( reserved0, 0, NibbleUpper) OPERATE( versionNumber, 1, UInteger4Lower) OPERATE( reserved1, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/clockAccuracy.def0000600000175000017500000000035511770426154020731 0ustar ernieernie/* Spec Table 49 - CLOCK_ACCURACY management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( clockAccuracy, 1, Enumeration8) OPERATE( reserved, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/time.def0000600000175000017500000000030211770426154017111 0ustar ernieernie/* Spec Table 48 - TIME management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( currentTime, 10, Timestamp) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/logAnnounceInterval.def0000600000175000017500000000036611770426154022142 0ustar ernieernie/* Spec Table 62 - LOG_ANNOUNCE_INTERVAL management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( logAnnounceInterval, 1, Integer8) OPERATE( reserved, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/managementTLV/userDescription.def0000600000175000017500000000036511770426153021345 0ustar ernieernie/* Spec Table 43 - USER_DESCRIPTION management TLV data field */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( userDescription, 1 + data->userDescription.lengthField, PTPText) #undef OPERATE ptpd-2.2.2/src/def/derivedData/0000700000175000017500000000000012127234262015202 5ustar ernieernieptpd-2.2.2/src/def/derivedData/faultRecord.def0000600000175000017500000000070611770426154020147 0ustar ernieernie/* Spec 5.3.10 FaultRecord */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( faultRecordLength, 2, UInteger16) OPERATE( faultTime, 10, Timestamp) OPERATE( severityCode, 1, Enumeration8) OPERATE( faultName, 1 + data->faultName.lengthField, PTPText) OPERATE( faultValue, 1 + data->faultValue.lengthField, PTPText) OPERATE( faultDescription, 1 + data->faultDescription.lengthField, PTPText) #undef OPERATE ptpd-2.2.2/src/def/derivedData/portAddress.def0000600000175000017500000000041411770426154020163 0ustar ernieernie/* Spec 5.3.6 PortAddress */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( networkProtocol, 2, Enumeration16) OPERATE( addressLength, 2, UInteger16) OPERATE( addressField, data->addressLength, Octet*) #undef OPERATE ptpd-2.2.2/src/def/derivedData/tlv.def0000600000175000017500000000036611770426154016504 0ustar ernieernie/* Spec 5.3.8 TLV */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( tlvType, 2, Enumeration16) OPERATE( lengthField, 2, UInteger16) OPERATE( valueField, data->lengthField, Octet*) #undef OPERATE ptpd-2.2.2/src/def/derivedData/physicalAddress.def0000600000175000017500000000057211770426154021020 0ustar ernieernie/* PhysicalAddress */ /* * This is not a derived data type from the standard. * Defining this type simplifies the implementation of * the Management Clock Description message */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( addressLength, 2, UInteger16) OPERATE( addressField, data->addressLength, Octet*) #undef OPERATE ptpd-2.2.2/src/def/derivedData/timeInterval.def0000600000175000017500000000025711770426154020341 0ustar ernieernie/* Spec 5.3.2 TimeInterval*/ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( scaledNanoseconds, 8, Integer64) #undef OPERATE ptpd-2.2.2/src/def/derivedData/timestamp.def0000600000175000017500000000032311770426154017673 0ustar ernieernie/* Spec 5.3.3 Timestamp */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( secondsField, 6, UInteger48) OPERATE( nanosecondsField, 4, UInteger32) #undef OPERATE ptpd-2.2.2/src/def/derivedData/ptpText.def0000600000175000017500000000032411770426154017341 0ustar ernieernie/* Spec 5.3.9 PTPText */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( lengthField, 1, UInteger8) OPERATE( textField, data->lengthField, Octet*) #undef OPERATE ptpd-2.2.2/src/def/derivedData/portIdentity.def0000600000175000017500000000035011770426154020366 0ustar ernieernie/* Spec 5.3.5 PortIdentity */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( clockIdentity, CLOCK_IDENTITY_LENGTH, ClockIdentity) OPERATE( portNumber, 2, UInteger16) #undef OPERATE ptpd-2.2.2/src/def/derivedData/clockQuality.def0000600000175000017500000000040511770426154020335 0ustar ernieernie/* Spec 5.3.7 ClockQuality */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( clockClass, 1, UInteger8 ) OPERATE( clockAccuracy, 1, Enumeration8 ) OPERATE( offsetScaledLogVariance, 2, UInteger16) #undef OPERATE ptpd-2.2.2/src/def/README0000600000175000017500000000066711770426154013667 0ustar ernieernieThis directory contains component macro .def files that are referenced by X-Macros in the ptpd source code. The component macros are used to define message, derived data types, and management TLV fields. The X-Macros are used to automatically generate most of the code used to pack, unpack, and free ptp data fields. Documentation on how to use X-Macros can be found at: http://en.wikibooks.org/wiki/C_Programming/Preprocessor#X-Macros ptpd-2.2.2/src/def/message/0000700000175000017500000000000012127234262014412 5ustar ernieernieptpd-2.2.2/src/def/message/management.def0000600000175000017500000000064711770426154017225 0ustar ernieernie/* Spec Table 37 - Management message fields */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( header, 34, MsgHeader) OPERATE( targetPortIdentity, 10, PortIdentity) OPERATE( startingBoundaryHops, 1, UInteger8) OPERATE( boundaryHops, 1, UInteger8) OPERATE( reserved0, 0, NibbleUpper) OPERATE( actionField, 1, Enumeration4Lower) OPERATE( reserved1, 1, Octet) #undef OPERATE ptpd-2.2.2/src/def/message/header.def0000600000175000017500000000131511770426154016332 0ustar ernieernie/* Spec Table 18 - Common message header */ /* to use these definitions, #define OPERATE then #include this file in your source */ OPERATE( transportSpecific, 0, NibbleUpper) OPERATE( messageType, 1, Enumeration4Lower) OPERATE( reserved0, 0, NibbleUpper) OPERATE( versionPTP, 1, UInteger4Lower) OPERATE( messageLength, 2, UInteger16) OPERATE( domainNumber, 1, UInteger8) OPERATE( reserved1, 1, Octet) OPERATE( flagField0, 1, Octet) OPERATE( flagField1, 1, Octet) OPERATE( correctionField, 8, Integer64) OPERATE( reserved2, 4, UInteger32) OPERATE( sourcePortIdentity, 10, PortIdentity) OPERATE( sequenceId, 2, UInteger16) OPERATE( controlField, 1, UInteger8) OPERATE( logMessageInterval, 1, Integer8) #undef OPERATE ptpd-2.2.2/src/bmc.c0000600000175000017500000004235011770426154013151 0ustar ernieernie/*- * Copyright (c) 2011-2012 George V. Neville-Neil, * Steven Kreuzer, * Martin Burnicki, * Jan Breuer, * Gael Mace, * Alexandre Van Kempen, * Inaqui Delgado, * Rick Ratzel, * National Instruments. * Copyright (c) 2009-2010 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. */ /** * @file bmc.c * @date Wed Jun 23 09:36:09 2010 * * @brief Best master clock selection code. * * The functions in this file are used by the daemon to select the * best master clock from any number of possibilities. */ #include "ptpd.h" /* Init ptpClock with run time values (initialization constants are in constants.h)*/ void initData(RunTimeOpts *rtOpts, PtpClock *ptpClock) { int i,j; j=0; DBG("initData\n"); /* Default data set */ ptpClock->twoStepFlag = TWO_STEP_FLAG; /* * init clockIdentity with MAC address and 0xFF and 0xFE. see * spec 7.5.2.2.2 */ for (i=0;iclockIdentity[i]=0xFF; else if (i==4) ptpClock->clockIdentity[i]=0xFE; else { ptpClock->clockIdentity[i]=ptpClock->port_uuid_field[j]; j++; } } ptpClock->numberPorts = NUMBER_PORTS; ptpClock->clockQuality.clockAccuracy = rtOpts->clockQuality.clockAccuracy; ptpClock->clockQuality.clockClass = rtOpts->clockQuality.clockClass; ptpClock->clockQuality.offsetScaledLogVariance = rtOpts->clockQuality.offsetScaledLogVariance; ptpClock->priority1 = rtOpts->priority1; ptpClock->priority2 = rtOpts->priority2; ptpClock->domainNumber = rtOpts->domainNumber; ptpClock->slaveOnly = rtOpts->slaveOnly; if(rtOpts->slaveOnly) rtOpts->clockQuality.clockClass = SLAVE_ONLY_CLOCK_CLASS; /* Port configuration data set */ /* * PortIdentity Init (portNumber = 1 for an ardinary clock spec * 7.5.2.3) */ copyClockIdentity(ptpClock->portIdentity.clockIdentity, ptpClock->clockIdentity); ptpClock->portIdentity.portNumber = NUMBER_PORTS; /* select the initial rate of delayreqs until we receive the first announce message */ ptpClock->logMinDelayReqInterval = rtOpts->initial_delayreq; clearTime(&ptpClock->peerMeanPathDelay); ptpClock->logAnnounceInterval = rtOpts->announceInterval; ptpClock->announceReceiptTimeout = rtOpts->announceReceiptTimeout; ptpClock->logSyncInterval = rtOpts->syncInterval; ptpClock->delayMechanism = rtOpts->delayMechanism; ptpClock->logMinPdelayReqInterval = DEFAULT_PDELAYREQ_INTERVAL; ptpClock->versionNumber = VERSION_PTP; /* * Initialize random number generator using same method as ptpv1: * seed is now initialized from the last bytes of our mac addres (collected in net.c:findIface()) */ srand((ptpClock->port_uuid_field[PTP_UUID_LENGTH - 1] << 8) + ptpClock->port_uuid_field[PTP_UUID_LENGTH - 2]); /*Init other stuff*/ ptpClock->number_foreign_records = 0; ptpClock->max_foreign_records = rtOpts->max_foreign_records; } /*Local clock is becoming Master. Table 13 (9.3.5) of the spec.*/ void m1(RunTimeOpts *rtOpts, PtpClock *ptpClock) { /*Current data set update*/ ptpClock->stepsRemoved = 0; clearTime(&ptpClock->offsetFromMaster); clearTime(&ptpClock->meanPathDelay); /*Parent data set*/ copyClockIdentity(ptpClock->parentPortIdentity.clockIdentity, ptpClock->clockIdentity); ptpClock->parentPortIdentity.portNumber = 0; ptpClock->parentStats = DEFAULT_PARENTS_STATS; ptpClock->observedParentClockPhaseChangeRate = 0; ptpClock->observedParentOffsetScaledLogVariance = 0; copyClockIdentity(ptpClock->grandmasterIdentity, ptpClock->clockIdentity); ptpClock->grandmasterClockQuality.clockAccuracy = ptpClock->clockQuality.clockAccuracy; ptpClock->grandmasterClockQuality.clockClass = ptpClock->clockQuality.clockClass; ptpClock->grandmasterClockQuality.offsetScaledLogVariance = ptpClock->clockQuality.offsetScaledLogVariance; ptpClock->grandmasterPriority1 = ptpClock->priority1; ptpClock->grandmasterPriority2 = ptpClock->priority2; /*Time Properties data set*/ ptpClock->timeSource = INTERNAL_OSCILLATOR; /* UTC vs TAI timescales */ ptpClock->currentUtcOffsetValid = DEFAULT_UTC_VALID; ptpClock->currentUtcOffset = rtOpts->currentUtcOffset; } /* first cut on a passive mode specific BMC actions */ void p1(PtpClock *ptpClock, RunTimeOpts *rtOpts) { /* make sure we revert to ARB timescale in Passive mode*/ if(ptpClock->portState == PTP_PASSIVE){ ptpClock->currentUtcOffsetValid = DEFAULT_UTC_VALID; ptpClock->currentUtcOffset = rtOpts->currentUtcOffset; } } /*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; /* Parent DS */ copyClockIdentity(ptpClock->parentPortIdentity.clockIdentity, header->sourcePortIdentity.clockIdentity); ptpClock->parentPortIdentity.portNumber = header->sourcePortIdentity.portNumber; copyClockIdentity(ptpClock->grandmasterIdentity, announce->grandmasterIdentity); ptpClock->grandmasterClockQuality.clockAccuracy = announce->grandmasterClockQuality.clockAccuracy; ptpClock->grandmasterClockQuality.clockClass = announce->grandmasterClockQuality.clockClass; ptpClock->grandmasterClockQuality.offsetScaledLogVariance = announce->grandmasterClockQuality.offsetScaledLogVariance; ptpClock->grandmasterPriority1 = announce->grandmasterPriority1; ptpClock->grandmasterPriority2 = announce->grandmasterPriority2; /* Timeproperties DS */ ptpClock->currentUtcOffset = announce->currentUtcOffset; /* "Valid" is bit 2 in second octet of flagfield */ ptpClock->currentUtcOffsetValid = IS_SET(header->flagField1, UTCV); /* set PTP_PASSIVE-specific state */ p1(ptpClock, rtOpts); /* only set leap state in slave mode */ if (ptpClock->portState == PTP_SLAVE) { ptpClock->leap59 = IS_SET(header->flagField1, LI59); ptpClock->leap61 = IS_SET(header->flagField1, LI61); } ptpClock->timeTraceable = IS_SET(header->flagField1, TTRA); ptpClock->frequencyTraceable = IS_SET(header->flagField1, FTRA); ptpClock->ptpTimescale = IS_SET(header->flagField1, PTPT); ptpClock->timeSource = announce->timeSource; #if defined(MOD_TAI) && NTP_API == 4 /* * update kernel TAI offset, but only if timescale is * PTP not ARB - spec section 7.2 */ if (ptpClock->ptpTimescale && (ptpClock->currentUtcOffset != previousUtcOffset)) { setKernelUtcOffset(ptpClock->currentUtcOffset); } #endif /* MOD_TAI */ /* Leap second handling */ if (ptpClock->portState == PTP_SLAVE) { if(ptpClock->leap59 && ptpClock->leap61) { ERROR("Both Leap59 and Leap61 flags set!\n"); toState(PTP_FAULTY, rtOpts, ptpClock); return; } /* one of the leap second flags has suddenly been unset */ if(ptpClock->leapSecondPending && !ptpClock->leapSecondInProgress && ((previousLeap59 != ptpClock->leap59) || (previousLeap61 != ptpClock->leap61))) { WARNING("=== Leap second event aborted by GM!"); ptpClock->leapSecondPending = FALSE; ptpClock->leapSecondInProgress = FALSE; timerStop(LEAP_SECOND_PAUSE_TIMER, ptpClock->itimer); #if !defined(__APPLE__) unsetTimexFlags(STA_INS | STA_DEL,TRUE); #endif /* apple */ } /* * one of the leap second flags has been set * or flags are lit but we have no event pending */ if( (ptpClock->leap59 || ptpClock->leap61) && ( (!ptpClock->leapSecondPending && !ptpClock->leapSecondInProgress ) || ((!previousLeap59 && ptpClock->leap59) || (!previousLeap61 && ptpClock->leap61)))) { #if !defined(__APPLE__) WARNING("=== Leap second pending! Setting kernel to %s " "one second at midnight\n", ptpClock->leap61 ? "add" : "delete"); if (!checkTimexFlags(ptpClock->leap61 ? STA_INS : STA_DEL)) { unsetTimexFlags(ptpClock->leap61 ? STA_DEL : STA_INS, TRUE); 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 */ /* only set the flag, the rest happens in doState() */ ptpClock->leapSecondPending = TRUE; } 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); } } } /*Copy local data set into header and announce message. 9.3.4 table 12*/ void copyD0(MsgHeader *header, MsgAnnounce *announce, PtpClock *ptpClock) { announce->grandmasterPriority1 = ptpClock->priority1; copyClockIdentity(announce->grandmasterIdentity, ptpClock->clockIdentity); announce->grandmasterClockQuality.clockClass = ptpClock->clockQuality.clockClass; announce->grandmasterClockQuality.clockAccuracy = ptpClock->clockQuality.clockAccuracy; announce->grandmasterClockQuality.offsetScaledLogVariance = ptpClock->clockQuality.offsetScaledLogVariance; announce->grandmasterPriority2 = ptpClock->priority2; announce->stepsRemoved = 0; copyClockIdentity(header->sourcePortIdentity.clockIdentity, ptpClock->clockIdentity); } /*Data set comparison bewteen two foreign masters (9.3.4 fig 27) * return similar to memcmp() */ Integer8 bmcDataSetComparison(MsgHeader *headerA, MsgAnnounce *announceA, MsgHeader *headerB,MsgAnnounce *announceB, PtpClock *ptpClock) { DBGV("Data set comparison \n"); short comp = 0; /*Identity comparison*/ if (!memcmp(announceA->grandmasterIdentity, announceB->grandmasterIdentity,CLOCK_IDENTITY_LENGTH)){ /* Algorithm part2 Fig 28 */ if (announceA->stepsRemoved > announceB->stepsRemoved+1) { return 1; } else if (announceB->stepsRemoved > announceA->stepsRemoved+1) { return -1; } else { /* A within 1 of B */ if (announceA->stepsRemoved > announceB->stepsRemoved) { if (!memcmp(headerA->sourcePortIdentity.clockIdentity,ptpClock->parentPortIdentity.clockIdentity,CLOCK_IDENTITY_LENGTH)) { DBG("Sender=Receiver : Error -1"); return 0; } else { return 1; } } else if (announceB->stepsRemoved > announceA->stepsRemoved) { if (!memcmp(headerB->sourcePortIdentity.clockIdentity,ptpClock->parentPortIdentity.clockIdentity,CLOCK_IDENTITY_LENGTH)) { DBG("Sender=Receiver : Error -1"); return 0; } else { return -1; } } else { /* steps removed A = steps removed B */ comp = memcmp(headerA->sourcePortIdentity.clockIdentity,headerB->sourcePortIdentity.clockIdentity,CLOCK_IDENTITY_LENGTH); if (!comp) { DBG("Sender=Receiver : Error -2"); return 0; } else if (comp<0) { return -1; } else { return 1; } } } } else { /* GrandMaster are not identical */ if(announceA->grandmasterPriority1 == announceB->grandmasterPriority1) { if (announceA->grandmasterClockQuality.clockClass == announceB->grandmasterClockQuality.clockClass) { if (announceA->grandmasterClockQuality.clockAccuracy == announceB->grandmasterClockQuality.clockAccuracy) { if (announceA->grandmasterClockQuality.offsetScaledLogVariance == announceB->grandmasterClockQuality.offsetScaledLogVariance) { if (announceA->grandmasterPriority2 == announceB->grandmasterPriority2) { comp = memcmp(announceA->grandmasterIdentity,announceB->grandmasterIdentity,CLOCK_IDENTITY_LENGTH); if (comp < 0) return -1; else if (comp > 0) return 1; else return 0; } else { /* Priority2 are not identical */ comp =memcmp(&announceA->grandmasterPriority2,&announceB->grandmasterPriority2,1); if (comp < 0) return -1; else if (comp > 0) return 1; else return 0; } } else { /* offsetScaledLogVariance are not identical */ comp = announceA->grandmasterClockQuality.offsetScaledLogVariance < announceB->grandmasterClockQuality.offsetScaledLogVariance ? -1 : 1; if (comp < 0) return -1; else if (comp > 0) return 1; else return 0; } } else { /* Accuracy are not identitcal */ comp = memcmp(&announceA->grandmasterClockQuality.clockAccuracy,&announceB->grandmasterClockQuality.clockAccuracy,1); if (comp < 0) return -1; else if (comp > 0) return 1; else return 0; } } else { /* ClockClass are not identical */ comp = memcmp(&announceA->grandmasterClockQuality.clockClass,&announceB->grandmasterClockQuality.clockClass,1); if (comp < 0) return -1; else if (comp > 0) return 1; else return 0; } } else { /* Priority1 are not identical */ comp = memcmp(&announceA->grandmasterPriority1,&announceB->grandmasterPriority1,1); if (comp < 0) return -1; else if (comp > 0) return 1; else return 0; } } } /*State decision algorithm 9.3.3 Fig 26*/ UInteger8 bmcStateDecision(MsgHeader *header, MsgAnnounce *announce, RunTimeOpts *rtOpts, PtpClock *ptpClock) { Integer8 comp; if (rtOpts->slaveOnly) { s1(header,announce,ptpClock, rtOpts); return PTP_SLAVE; } if ((!ptpClock->number_foreign_records) && (ptpClock->portState == PTP_LISTENING)) return PTP_LISTENING; copyD0(&ptpClock->msgTmpHeader,&ptpClock->msgTmp.announce,ptpClock); DBGV("local clockQuality.clockClass: %d \n", ptpClock->clockQuality.clockClass); comp = bmcDataSetComparison(&ptpClock->msgTmpHeader, &ptpClock->msgTmp.announce, header, announce, ptpClock); if (ptpClock->clockQuality.clockClass < 128) { if (comp < 0) { m1(rtOpts, ptpClock); return PTP_MASTER; } else if (comp > 0) { s1(header,announce,ptpClock, rtOpts); return PTP_PASSIVE; } else { DBG("Error in bmcDataSetComparison..\n"); } } else { if (comp < 0) { m1(rtOpts,ptpClock); return PTP_MASTER; } else if (comp > 0) { s1(header,announce,ptpClock, rtOpts); return PTP_SLAVE; } else { DBG("Error in bmcDataSetComparison..\n"); } } /* MB: Is this the return code below correct? */ /* Anyway, it's a valid return code. */ return PTP_FAULTY; } UInteger8 bmc(ForeignMasterRecord *foreignMaster, RunTimeOpts *rtOpts, PtpClock *ptpClock) { Integer16 i,best; DBGV("number_foreign_records : %d \n", ptpClock->number_foreign_records); if (!ptpClock->number_foreign_records) if (ptpClock->portState == PTP_MASTER) { m1(rtOpts,ptpClock); return ptpClock->portState; } for (i=1,best = 0; inumber_foreign_records;i++) if ((bmcDataSetComparison(&foreignMaster[i].header, &foreignMaster[i].announce, &foreignMaster[best].header, &foreignMaster[best].announce, ptpClock)) < 0) best = i; DBGV("Best record : %d \n",best); ptpClock->foreign_record_best = best; return (bmcStateDecision(&foreignMaster[best].header, &foreignMaster[best].announce, rtOpts,ptpClock)); } /* 13.3.2.6, page 126 PTPv2 valid flags per packet type: ALL: .... .0.. .... .... = PTP_UNICAST SYNC+Pdelay Resp: .... ..0. .... .... = PTP_TWO_STEP Announce only: .... .... ..0. .... = FREQUENCY_TRACEABLE .... .... ...0 .... = TIME_TRACEABLE .... .... .... 0... = PTP_TIMESCALE .... .... .... .0.. = PTP_UTC_REASONABLE .... .... .... ..0. = PTP_LI_59 .... .... .... ...0 = PTP_LI_61 */ ptpd-2.2.2/src/dep/0000700000175000017500000000000012127234262013000 5ustar ernieernieptpd-2.2.2/src/dep/startup.c0000600000175000017500000007564411770426153014675 0ustar ernieernie/*- * Copyright (c) 2011-2012 George V. Neville-Neil, * Steven Kreuzer, * Martin Burnicki, * Jan Breuer, * Gael Mace, * Alexandre Van Kempen, * Inaqui Delgado, * Rick Ratzel, * National Instruments. * Copyright (c) 2009-2010 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. */ /** * @file startup.c * @date Wed Jun 23 09:33:27 2010 * * @brief Code to handle daemon startup, including command line args * * The function in this file are called when the daemon starts up * and include the getopt() command line argument parsing. */ #include "../ptpd.h" /* * valgrind 3.5.0 currently reports no errors (last check: 20110512) * valgrind 3.4.1 lacks an adjtimex handler * * to run: sudo valgrind --show-reachable=yes --leak-check=full --track-origins=yes -- ./ptpd2 -c ... */ /* to test daemon locking and startup sequence itself, try: function s() { set -o pipefail ; eval "$@" | sed 's/^/\t/' ; echo $?; } sudo killall ptpd2 s ./ptpd2 s sudo ./ptpd2 s sudo ./ptpd2 -t -g s sudo ./ptpd2 -t -g -b eth0 s sudo ./ptpd2 -t -g -b eth0 ps -ef | grep ptpd2 */ /* * Synchronous signal processing: * original idea: http://www.openbsd.org/cgi-bin/cvsweb/src/usr.sbin/ntpd/ntpd.c?rev=1.68;content-type=text%2Fplain */ volatile sig_atomic_t sigint_received = 0; volatile sig_atomic_t sigterm_received = 0; volatile sig_atomic_t sighup_received = 0; volatile sig_atomic_t sigusr1_received = 0; volatile sig_atomic_t sigusr2_received = 0; /* * Function to catch signals asynchronously. * Assuming that the daemon periodically calls check_signals(), then all operations are safely done synchrously at a later opportunity. * * Please do NOT call any functions inside this handler - especially DBG() and its friends, or any glibc. */ void catch_signals(int sig) { switch (sig) { case SIGINT: sigint_received = 1; break; 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; } } /* * exit the program cleanly */ void do_signal_close(PtpClock * ptpClock) { ptpdShutdown(ptpClock); NOTIFY("shutdown on close signal\n"); exit(0); } /** * Signal handler for HUP which tells us to swap the log file. * * @param sig */ void do_signal_sighup(RunTimeOpts * rtOpts) { if(rtOpts->do_record_quality_file) if(!recordToFile(rtOpts)) NOTIFY("SIGHUP recordToFile failed\n"); if(rtOpts->do_log_to_file) if(!logToFile(rtOpts)) NOTIFY("SIGHUP logToFile failed\n"); NOTIFY("I've been SIGHUP'd\n"); } /* * Synchronous signal processing: * This function should be called regularly from the main loop */ void check_signals(RunTimeOpts * rtOpts, PtpClock * ptpClock) { /* * note: * alarm signals are handled in a similar way in dep/timer.c */ if(sigint_received || sigterm_received){ do_signal_close(ptpClock); } if(sighup_received){ do_signal_sighup(rtOpts); sighup_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; } #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_SIGUSR2_CHANGE_DEBUG #ifdef RUNTIME_DEBUG if(sigusr2_received){ /* cycle debug levels, from INFO (=no debug) to Verbose */ INFO("Current debug level: %d\n", rtOpts->debug_level); (rtOpts->debug_level)++; if(rtOpts->debug_level > LOG_DEBUGV ){ rtOpts->debug_level = LOG_INFO; } INFO("New debug level: %d\n", rtOpts->debug_level); sigusr2_received = 0; } #endif #endif } #ifdef RUNTIME_DEBUG /* These functions are useful to temporarily enable Debug around parts of code, similar to bash's "set -x" */ void enable_runtime_debug(void ) { extern RunTimeOpts rtOpts; rtOpts.debug_level = max(LOG_DEBUGV, rtOpts.debug_level); } void disable_runtime_debug(void ) { extern RunTimeOpts rtOpts; rtOpts.debug_level = LOG_INFO; } #endif /* * Lock via filesystem implementation, as described in "Advanced Programming in the UNIX Environment, 2nd ed" */ #include #include #include #include #include #include #include #include #include #define LOCKFILE "/var/run/kernel_clock" #define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) int global_lock_fd; /* try to apply a write lock on the file */ int lockfile(int fd) { struct flock fl; fl.l_type = F_WRLCK; 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 */ int daemon_already_running(void) { char buf[16]; 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); } int query_shell(char *command, char *answer, int answer_size); /* return number of pgrep exact matches to the given string * -1: error * 0: no matches * >0: number of matches */ int pgrep_matches(char *name) { char command[BUF_SIZE]; char answer[BUF_SIZE]; int matches; /* use pgrep to count processes with the given name * but exclude our own pid from the search results */ snprintf(command, BUF_SIZE - 1, "pgrep -x %s | grep -v ^%ld$ | wc -l", name, (long)getpid()); if( query_shell(command, answer, BUF_SIZE) < 0){ return -1; }; sscanf(answer, "%d", &matches); return (matches); } /* * This function executes a given shell command (including pipes), and returns the first line of the output * these methods are only suitable for initialization time :) * * * ret: -1 error ; 0 ok * */ int query_shell(char *command, char *answer, int answer_size) { int status; FILE *fp; // clear previous answer answer[0] = '\0'; fp = popen(command, "r"); if (fp == NULL){ PERROR("can't call %s", command); 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; } /* from Man page: Use macros described under wait() to inspect `status' in order to determine success/failure of command executed by popen() */ return 0; } /* * expected: number of expected deamons * strict: if it doesn't match what is expected, should we exit? * * return: * 1: ok * 0: error, will exit program */ int check_parallel_daemons(char *name, int expected, int strict, RunTimeOpts * rtOpts) { 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; } 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; } } } /** * Log output to a file * * * @return True if success, False if failure */ 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 */ 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 * * * @return True if success, False if failure */ int recordToFile(RunTimeOpts * rtOpts) { if (rtOpts->recordFP != NULL) fclose(rtOpts->recordFP); if ((rtOpts->recordFP = fopen(rtOpts->recordFile, "w")) == NULL) PERROR("could not open sync recording file"); else setlinebuf(rtOpts->recordFP); return (rtOpts->recordFP != NULL); } void ptpdShutdown(PtpClock * ptpClock) { netShutdown(&ptpClock->netPath); free(ptpClock->foreign); /* free management messages, they can have dynamic memory allocated */ if(ptpClock->msgTmpHeader.messageType == MANAGEMENT) freeManagementTLV(&ptpClock->msgTmp.manage); freeManagementTLV(&ptpClock->outgoingManageTmp); free(ptpClock); ptpClock = NULL; extern PtpClock* G_ptpClock; G_ptpClock = NULL; /* properly clean lockfile (eventough new deaemons can adquire 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]); } INFO("\n"); INFO("Starting %s daemon with parameters: %s\n", PTPD_PROGNAME, sbuf); } void display_short_help(char *error) { printf( //"\n" PTPD_PROGNAME ":\n" " -gGW Protocol mode (slave/master with ntp/master without ntp)\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" "\n" "ERROR: %s\n\n", error ); } PtpClock * 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 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); const char *getopt_string = "HgGWb:cCf:ST:DPR:xO:tM:a:w:u:Uehzl:o:i:I:n:N:y:m:v:r:s:p:q:Y:BjLV:A:"; /* 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" "\n" #define GETOPT_START_OF_OPTIONS "-H show detailed help page\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" "\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" "-T NUMBER set multicast time to live\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" "-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" "-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" "\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" #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" "\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" /* "-k NUMBER,NUMBER send a management message of key, record, then exit\n" implemented later.. */ "\n" ); *ret = 1; return 0; break; case 'c': rtOpts->nonDaemon = 1; break; case 'C': rtOpts->nonDaemon = 2; rtOpts->useSysLog = FALSE; rtOpts->syslog_startup_messages_also_to_stdout = TRUE; rtOpts->displayStats = 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; case 'f': /* this option handling is now after the arguments processing (to show the help in case of -?) */ strncpy(rtOpts->file, optarg, PATH_MAX); rtOpts->do_log_to_file = TRUE; break; case 'B': #ifdef RUNTIME_DEBUG (rtOpts->debug_level)++; if(rtOpts->debug_level > LOG_DEBUGV ){ rtOpts->debug_level = LOG_DEBUGV; } #else INFO("runtime debug not enabled. Please compile with RUNTIME_DEBUG\n"); #endif break; case 'D': rtOpts->displayStats = 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 '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 'A': rtOpts->maxDelayAutoTune = TRUE; rtOpts->discardedPacketThreshold = atoi(optarg); break; case 'M': rtOpts->maxDelay = rtOpts->origMaxDelay = 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; 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; #else INFO("Hybrid mode not enabled. Please compile with PTP_EXPERIMENTAL\n"); #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': #ifdef PTP_EXPERIMENTAL rtOpts->mcast_group_Number = strtol(optarg, &optarg, 0); #else INFO("Multicast group selection not enabled. Please compile with PTP_EXPERIMENTAL\n"); #endif break; case 'y': rtOpts->syncInterval = strtol(optarg, 0, 0); break; case 'n': rtOpts->announceInterval = strtol(optarg, 0, 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 'V': rtOpts->log_seconds_between_message = strtol(optarg, &optarg, 0); break; case 'z': rtOpts->delayMechanism = P2P; 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 /* 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; /* 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; /* * 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; /* 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; case 'Y': rtOpts->initial_delayreq = strtol(optarg, &optarg, 0); rtOpts->subsequent_delayreq = rtOpts->initial_delayreq; rtOpts->ignore_delayreq_interval_master = FALSE; /* 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; default: ERROR("Unknown parameter %c \n", c); *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"); *ret = 1; return 0; } if(!mode_selected){ display_short_help("Please select program mode"); *ret = 1; return 0; } #ifdef PTP_EXPERIMENTAL if(rtOpts->do_unicast_mode && rtOpts->do_hybrid_mode){ ERROR("Cant specify both -u and -U\n"); *ret = 3; return 0; } #endif 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. * * * Locking mechanism: * - Slave PTPs require locking to avoid having multiple discipliners adjusting the Kernel's Clock, via adjtimex(). * The most common use case is to avoid running multiple ptpd2 processes, but we also want to avoid running when other * known time discipliners (ntpd/ptpd) are there. * * 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(). * 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 * works, it is inelegant and confusing. * * - For PTP Masters with NTP (-G), no locking is needed as we only read the clock. For standalone masters (-W) * it is a similar situation as Slaves, because we can need write permission at any time. * * * Demonizing Mechanism: * If we survive locking and init checks, then the next step is to call the daemon() command to force us to * detach from the shell, and attach to process 1 (init). * As it is suggested in http://mywiki.wooledge.org/ProcessManagement#How_can_I_check_to_see_if_my_game_server_is_still_running.3F__I.27ll_put_a_script_in_crontab.2C_and_if_it.27s_not_running.2C_I.27ll_restart_it... * self-backgrounding has a lot of issues and pitfalls, so newer software packages that provide services as increasingly * moving to run in foreground, or provide a flag to do so. Then, the foreground process is managed by inittab, daemontools, etc * * Its still unclear how such daemons are managed by /etc/init.d/ style of scripts * */ /* Init user_description */ memset(ptpClock->user_description, 0, sizeof(ptpClock->user_description)); memcpy(ptpClock->user_description, &USER_DESCRIPTION, sizeof(USER_DESCRIPTION)); /* Init outgoing management message */ ptpClock->outgoingManageTmp.tlv = NULL; /* First lock check, just to be user-friendly to the operator */ if(rtOpts->ignore_daemon_lock == 0){ /* check and create Lock */ if(daemon_already_running()){ ERROR(" Error: Multiple " PTPD_PROGNAME " instances detected (-L to ignore lock file %s)\n", LOCKFILE); *ret = 3; return 0; } } 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; ntp_daemons_strict=0; } if(check_parallel_daemons(PTPD_PROGNAME, ptp_daemons_expected, ptp_daemons_strict, rtOpts) && check_parallel_daemons("ntpd", ntp_daemons_expected, ntp_daemons_strict, rtOpts)) { /* ok */ } else { *ret = 3; 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; } /* DAEMON */ #ifdef PTPD_NO_DAEMON 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; } 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()){ ERROR("Multiple instances of this daemon detected (Use option -L to ignore lock file %s)\n", LOCKFILE); *ret = 3; 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); *ret = 0; INFO(" Info: Startup finished sucessfully\n"); return ptpClock; } ptpd-2.2.2/src/dep/msg.c0000600000175000017500000021370511770426153013751 0ustar ernieernie/*- * Copyright (c) 2011-2012 George V. Neville-Neil, * Steven Kreuzer, * Martin Burnicki, * Jan Breuer, * Gael Mace, * Alexandre Van Kempen, * Inaqui Delgado, * Rick Ratzel, * National Instruments. * Copyright (c) 2009-2010 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. */ /** * @file msg.c * @author George Neville-Neil * @date Tue Jul 20 16:17:05 2010 * * @brief Functions to pack and unpack messages. * * See spec annex d */ #include "../ptpd.h" #ifdef PTP_EXPERIMENTAL extern RunTimeOpts rtOpts; #endif #define PACK_SIMPLE( type ) \ void pack##type( void* from, void* to ) \ { \ *(type *)to = *(type *)from; \ } \ void unpack##type( void* from, void* to, PtpClock *ptpClock ) \ { \ pack##type( from, to ); \ } #define PACK_ENDIAN( type, size ) \ void pack##type( void* from, void* to ) \ { \ *(type *)to = flip##size( *(type *)from ); \ } \ void unpack##type( void* from, void* to, PtpClock *ptpClock ) \ { \ pack##type( from, to ); \ } #define PACK_LOWER_AND_UPPER( type ) \ void pack##type##Lower( void* from, void* to ) \ { \ *(char *)to = *(char *)to & 0xF0; \ *(char *)to = *(char *)to | *(type *)from; \ } \ \ void pack##type##Upper( void* from, void* to ) \ { \ *(char *)to = *(char *)to & 0x0F; \ *(char *)to = *(char *)to | (*(type *)from << 4); \ } \ \ void unpack##type##Lower( void* from, void* to, PtpClock *ptpClock ) \ { \ *(type *)to = *(char *)from & 0x0F; \ } \ \ void unpack##type##Upper( void* from, void* to, PtpClock *ptpClock ) \ { \ *(type *)to = *(char *)from & 0xF0; \ } PACK_SIMPLE( Boolean ) PACK_SIMPLE( UInteger8 ) PACK_SIMPLE( Octet ) PACK_SIMPLE( Enumeration8 ) PACK_SIMPLE( Integer8 ) PACK_ENDIAN( Enumeration16, 16 ) PACK_ENDIAN( Integer16, 16 ) PACK_ENDIAN( UInteger16, 16 ) PACK_ENDIAN( Integer32, 32 ) PACK_ENDIAN( UInteger32, 32 ) PACK_LOWER_AND_UPPER( Enumeration4 ) PACK_LOWER_AND_UPPER( UInteger4 ) PACK_LOWER_AND_UPPER( Nibble ) /* The free function is intentionally empty. However, this simplifies * the procedure to deallocate complex data types */ #define FREE( type ) \ void free##type( void* x) \ {} FREE ( Boolean ) FREE ( UInteger8 ) FREE ( Octet ) FREE ( Enumeration8 ) FREE ( Integer8 ) FREE ( Enumeration16 ) FREE ( Integer16 ) FREE ( UInteger16 ) FREE ( Integer32 ) FREE ( UInteger32 ) FREE ( Enumeration4 ) FREE ( UInteger4 ) FREE ( Nibble ) void unpackUInteger48( void *buf, void *i, PtpClock *ptpClock) { unpackUInteger32(buf, &((UInteger48*)i)->lsb, ptpClock); unpackUInteger16(buf + 4, &((UInteger48*)i)->msb, ptpClock); } void packUInteger48( void *i, void *buf) { packUInteger32(&((UInteger48*)i)->lsb, buf); packUInteger16(&((UInteger48*)i)->msb, buf + 4); } void unpackInteger64( void *buf, void *i, PtpClock *ptpClock) { unpackUInteger32(buf, &((Integer64*)i)->lsb, ptpClock); unpackInteger32(buf + 4, &((Integer64*)i)->msb, ptpClock); } void packInteger64( void* i, void *buf ) { packUInteger32(&((Integer64*)i)->lsb, buf); packInteger32(&((Integer64*)i)->msb, buf + 4); } /* NOTE: the unpack functions for management messages can probably be refactored into a macro */ void unpackMMSlaveOnly( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMSlaveOnly)); MMSlaveOnly* data = (MMSlaveOnly*)m->tlv->dataField; /* see src/def/README for a note on this X-macro */ #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/slaveOnly.def" #ifdef PTPD_DBG mMSlaveOnly_display(data, ptpClock); #endif /* PTPD_DBG */ } /* NOTE: the pack functions for management messsages can probably be refactored into a macro */ UInteger16 packMMSlaveOnly( MsgManagement* m, Octet *buf) { int offset = 0; MMSlaveOnly* data = (MMSlaveOnly*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/slaveOnly.def" /* return length */ return offset; } void unpackMMClockDescription( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMClockDescription)); MMClockDescription* data = (MMClockDescription*)m->tlv->dataField; memset(data, 0, sizeof(MMClockDescription)); #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/clockDescription.def" #ifdef PTPD_DBG mMClockDescription_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMClockDescription( MsgManagement* m, Octet *buf) { int offset = 0; Octet pad = 0; MMClockDescription* data = (MMClockDescription*)m->tlv->dataField; data->reserved = 0; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset); \ offset = offset + size; #include "../def/managementTLV/clockDescription.def" /* is the TLV length odd? TLV must be even according to Spec 5.3.8 */ if(offset % 2) { /* add pad of 1 according to Table 41 to make TLV length even */ packOctet(&pad, buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset); offset = offset + 1; } /* return length */ return offset; } void freeMMClockDescription( MMClockDescription* data) { #define OPERATE( name, size, type ) \ free##type( &data->name); #include "../def/managementTLV/clockDescription.def" } void unpackMMUserDescription( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMUserDescription)); MMUserDescription* data = (MMUserDescription*)m->tlv->dataField; memset(data, 0, sizeof(MMUserDescription)); #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/userDescription.def" #ifdef PTPD_DBG /* mMUserDescription_display(data, ptpClock); */ #endif /* PTPD_DBG */ } UInteger16 packMMUserDescription( MsgManagement* m, Octet *buf) { int offset = 0; Octet pad = 0; MMUserDescription* data = (MMUserDescription*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset); \ offset = offset + size; #include "../def/managementTLV/userDescription.def" /* is the TLV length odd? TLV must be even according to Spec 5.3.8 */ if(offset % 2) { /* add pad of 1 according to Table 41 to make TLV length even */ packOctet(&pad, buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset); offset = offset + 1; } /* return length */ return offset; } void freeMMUserDescription( MMUserDescription* data) { #define OPERATE( name, size, type ) \ free##type( &data->name); #include "../def/managementTLV/userDescription.def" } void unpackMMInitialize( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMInitialize)); MMInitialize* data = (MMInitialize*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/initialize.def" #ifdef PTPD_DBG mMInitialize_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMInitialize( MsgManagement* m, Octet *buf) { int offset = 0; MMInitialize* data = (MMInitialize*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/initialize.def" /* return length*/ return offset; } void unpackMMDefaultDataSet( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMDefaultDataSet)); MMDefaultDataSet* data = (MMDefaultDataSet*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/defaultDataSet.def" #ifdef PTPD_DBG mMDefaultDataSet_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMDefaultDataSet( MsgManagement* m, Octet *buf) { int offset = 0; MMDefaultDataSet* data = (MMDefaultDataSet*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/defaultDataSet.def" /* return length*/ return offset; } void unpackMMCurrentDataSet( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMCurrentDataSet)); MMCurrentDataSet* data = (MMCurrentDataSet*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/currentDataSet.def" #ifdef PTPD_DBG mMCurrentDataSet_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMCurrentDataSet( MsgManagement* m, Octet *buf) { int offset = 0; MMCurrentDataSet* data = (MMCurrentDataSet*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/currentDataSet.def" /* return length*/ return offset; } void unpackMMParentDataSet( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMParentDataSet)); MMParentDataSet* data = (MMParentDataSet*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/parentDataSet.def" #ifdef PTPD_DBG mMParentDataSet_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMParentDataSet( MsgManagement* m, Octet *buf) { int offset = 0; MMParentDataSet* data = (MMParentDataSet*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/parentDataSet.def" /* return length*/ return offset; } void unpackMMTimePropertiesDataSet( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMTimePropertiesDataSet)); MMTimePropertiesDataSet* data = (MMTimePropertiesDataSet*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/timePropertiesDataSet.def" #ifdef PTPD_DBG mMTimePropertiesDataSet_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMTimePropertiesDataSet( MsgManagement* m, Octet *buf) { int offset = 0; MMTimePropertiesDataSet* data = (MMTimePropertiesDataSet*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/timePropertiesDataSet.def" /* return length*/ return offset; } void unpackMMPortDataSet( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMPortDataSet)); MMPortDataSet* data = (MMPortDataSet*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/portDataSet.def" #ifdef PTPD_DBG mMPortDataSet_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMPortDataSet( MsgManagement* m, Octet *buf) { int offset = 0; MMPortDataSet* data = (MMPortDataSet*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/portDataSet.def" /* return length*/ return offset; } void unpackMMPriority1( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMPriority1)); MMPriority1* data = (MMPriority1*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/priority1.def" #ifdef PTPD_DBG mMPriority1_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMPriority1( MsgManagement* m, Octet *buf) { int offset = 0; MMPriority1* data = (MMPriority1*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/priority1.def" /* return length*/ return offset; } void unpackMMPriority2( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMPriority2)); MMPriority2* data = (MMPriority2*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/priority2.def" #ifdef PTPD_DBG mMPriority2_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMPriority2( MsgManagement* m, Octet *buf) { int offset = 0; MMPriority2* data = (MMPriority2*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/priority2.def" /* return length*/ return offset; } void unpackMMDomain( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMDomain)); MMDomain* data = (MMDomain*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/domain.def" #ifdef PTPD_DBG mMDomain_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMDomain( MsgManagement* m, Octet *buf) { int offset = 0; MMDomain* data = (MMDomain*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/domain.def" /* return length*/ return offset; } void unpackMMLogAnnounceInterval( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMLogAnnounceInterval)); MMLogAnnounceInterval* data = (MMLogAnnounceInterval*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/logAnnounceInterval.def" #ifdef PTPD_DBG mMLogAnnounceInterval_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMLogAnnounceInterval( MsgManagement* m, Octet *buf) { int offset = 0; MMLogAnnounceInterval* data = (MMLogAnnounceInterval*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/logAnnounceInterval.def" /* return length*/ return offset; } void unpackMMAnnounceReceiptTimeout( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField,sizeof(MMAnnounceReceiptTimeout)); MMAnnounceReceiptTimeout* data = (MMAnnounceReceiptTimeout*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/announceReceiptTimeout.def" #ifdef PTPD_DBG mMAnnounceReceiptTimeout_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMAnnounceReceiptTimeout( MsgManagement* m, Octet *buf) { int offset = 0; MMAnnounceReceiptTimeout* data = (MMAnnounceReceiptTimeout*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/announceReceiptTimeout.def" /* return length*/ return offset; } void unpackMMLogSyncInterval( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMLogSyncInterval)); MMLogSyncInterval* data = (MMLogSyncInterval*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/logSyncInterval.def" #ifdef PTPD_DBG mMLogSyncInterval_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMLogSyncInterval( MsgManagement* m, Octet *buf) { int offset = 0; MMLogSyncInterval* data = (MMLogSyncInterval*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/logSyncInterval.def" /* return length*/ return offset; } void unpackMMVersionNumber( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMVersionNumber)); MMVersionNumber* data = (MMVersionNumber*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/versionNumber.def" #ifdef PTPD_DBG mMVersionNumber_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMVersionNumber( MsgManagement* m, Octet *buf) { int offset = 0; MMVersionNumber* data = (MMVersionNumber*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/versionNumber.def" /* return length*/ return offset; } void unpackMMTime( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMTime)); MMTime* data = (MMTime*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/time.def" #ifdef PTPD_DBG mMTime_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMTime( MsgManagement* m, Octet *buf) { int offset = 0; MMTime* data = (MMTime*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/time.def" /* return length*/ return offset; } void unpackMMClockAccuracy( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMClockAccuracy)); MMClockAccuracy* data = (MMClockAccuracy*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/clockAccuracy.def" #ifdef PTPD_DBG mMClockAccuracy_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMClockAccuracy( MsgManagement* m, Octet *buf) { int offset = 0; MMClockAccuracy* data = (MMClockAccuracy*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/clockAccuracy.def" /* return length*/ return offset; } void unpackMMUtcProperties( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMUtcProperties)); MMUtcProperties* data = (MMUtcProperties*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/utcProperties.def" #ifdef PTPD_DBG mMUtcProperties_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMUtcProperties( MsgManagement* m, Octet *buf) { int offset = 0; MMUtcProperties* data = (MMUtcProperties*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/utcProperties.def" /* return length*/ return offset; } void unpackMMTraceabilityProperties( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMTraceabilityProperties)); MMTraceabilityProperties* data = (MMTraceabilityProperties*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/traceabilityProperties.def" #ifdef PTPD_DBG mMTraceabilityProperties_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMTraceabilityProperties( MsgManagement* m, Octet *buf) { int offset = 0; MMTraceabilityProperties* data = (MMTraceabilityProperties*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/traceabilityProperties.def" /* return length*/ return offset; } void unpackMMDelayMechanism( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMDelayMechanism)); MMDelayMechanism* data = (MMDelayMechanism*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/delayMechanism.def" #ifdef PTPD_DBG mMDelayMechanism_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMDelayMechanism( MsgManagement* m, Octet *buf) { int offset = 0; MMDelayMechanism* data = (MMDelayMechanism*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/delayMechanism.def" /* return length*/ return offset; } void unpackMMLogMinPdelayReqInterval( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMLogMinPdelayReqInterval)); MMLogMinPdelayReqInterval* data = (MMLogMinPdelayReqInterval*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/logMinPdelayReqInterval.def" #ifdef PTPD_DBG mMLogMinPdelayReqInterval_display(data, ptpClock); #endif /* PTPD_DBG */ } UInteger16 packMMLogMinPdelayReqInterval( MsgManagement* m, Octet *buf) { int offset = 0; MMLogMinPdelayReqInterval* data = (MMLogMinPdelayReqInterval*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/logMinPdelayReqInterval.def" /* return length*/ return offset; } void unpackMMErrorStatus( Octet *buf, MsgManagement* m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv->dataField, sizeof(MMErrorStatus)); MMErrorStatus* data = (MMErrorStatus*)m->tlv->dataField; #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\ &data->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/errorStatus.def" #ifdef PTPD_DBG mMErrorStatus_display(data, ptpClock); #endif /* PTPD_DBG */ } void freeMMErrorStatus( MMErrorStatus* data) { #define OPERATE( name, size, type ) \ free##type( &data->name); #include "../def/managementTLV/errorStatus.def" } UInteger16 packMMErrorStatus( MsgManagement* m, Octet *buf) { int offset = 0; Octet pad = 0; MMErrorStatus* data = (MMErrorStatus*)m->tlv->dataField; #define OPERATE( name, size, type ) \ pack##type( &data->name,\ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/errorStatus.def" /* is the TLV length odd? TLV must be even according to Spec 5.3.8 */ if(offset % 2) { /* add pad of 1 according to Table 41 to make TLV length even */ packOctet(&pad, buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset); offset = offset + 1; } /* return length*/ return offset; } void unpackClockIdentity( Octet *buf, ClockIdentity *c, PtpClock *ptpClock) { int i; for(i = 0; i < CLOCK_IDENTITY_LENGTH; i++) { unpackOctet((buf+i),&((*c)[i]), ptpClock); } } void packClockIdentity( ClockIdentity *c, Octet *buf) { int i; for(i = 0; i < CLOCK_IDENTITY_LENGTH; i++) { packOctet(&((*c)[i]),(buf+i)); } } void freeClockIdentity( ClockIdentity *c) { /* nothing to free */ } void unpackClockQuality( Octet *buf, ClockQuality *c, PtpClock *ptpClock) { int offset = 0; ClockQuality* data = c; #define OPERATE( name, size, type) \ unpack##type (buf + offset, &data->name, ptpClock); \ offset = offset + size; #include "../def/derivedData/clockQuality.def" } void packClockQuality( ClockQuality *c, Octet *buf) { int offset = 0; ClockQuality *data = c; #define OPERATE( name, size, type) \ pack##type (&data->name, buf + offset); \ offset = offset + size; #include "../def/derivedData/clockQuality.def" } void freeClockQuality( ClockQuality *c) { /* nothing to free */ } void unpackTimeInterval( Octet *buf, TimeInterval *t, PtpClock *ptpClock) { int offset = 0; TimeInterval* data = t; #define OPERATE( name, size, type) \ unpack##type (buf + offset, &data->name, ptpClock); \ offset = offset + size; #include "../def/derivedData/timeInterval.def" } void packTimeInterval( TimeInterval *t, Octet *buf) { int offset = 0; TimeInterval *data = t; #define OPERATE( name, size, type) \ pack##type (&data->name, buf + offset); \ offset = offset + size; #include "../def/derivedData/timeInterval.def" } void freeTimeInterval( TimeInterval *t) { /* nothing to free */ } void unpackTimestamp( Octet *buf, Timestamp *t, PtpClock *ptpClock) { int offset = 0; Timestamp* data = t; #define OPERATE( name, size, type) \ unpack##type (buf + offset, &data->name, ptpClock); \ offset = offset + size; #include "../def/derivedData/timestamp.def" } void packTimestamp( Timestamp *t, Octet *buf) { int offset = 0; Timestamp *data = t; #define OPERATE( name, size, type) \ pack##type (&data->name, buf + offset); \ offset = offset + size; #include "../def/derivedData/timestamp.def" } void freeTimestamp( Timestamp *t) { /* nothing to free */ } void unpackPortIdentity( Octet *buf, PortIdentity *p, PtpClock *ptpClock) { int offset = 0; PortIdentity* data = p; #define OPERATE( name, size, type) \ unpack##type (buf + offset, &data->name, ptpClock); \ offset = offset + size; #include "../def/derivedData/portIdentity.def" } void packPortIdentity( PortIdentity *p, Octet *buf) { int offset = 0; PortIdentity *data = p; #define OPERATE( name, size, type) \ pack##type (&data->name, buf + offset); \ offset = offset + size; #include "../def/derivedData/portIdentity.def" } void freePortIdentity( PortIdentity *p) { /* nothing to free */ } void unpackPortAddress( Octet *buf, PortAddress *p, PtpClock *ptpClock) { unpackEnumeration16( buf, &p->networkProtocol, ptpClock); unpackUInteger16( buf+2, &p->addressLength, ptpClock); if(p->addressLength) { XMALLOC(p->addressField, p->addressLength); memcpy( p->addressField, buf+4, p->addressLength); } else { p->addressField = NULL; } } void packPortAddress(PortAddress *p, Octet *buf) { packEnumeration16(&p->networkProtocol, buf); packUInteger16(&p->addressLength, buf+2); if(p->addressLength) { memcpy( buf+4, p->addressField, p->addressLength); } } void freePortAddress(PortAddress *p) { if(p->addressField) { free(p->addressField); p->addressField = NULL; } } void unpackPTPText( Octet *buf, PTPText *s, PtpClock *ptpClock) { unpackUInteger8( buf, &s->lengthField, ptpClock); if(s->lengthField) { XMALLOC(s->textField, s->lengthField); memcpy( s->textField, buf+1, s->lengthField); } else { s->textField = NULL; } } void packPTPText(PTPText *s, Octet *buf) { packUInteger8(&s->lengthField, buf); if(s->lengthField) { memcpy( buf+1, s->textField, s->lengthField); } } void freePTPText(PTPText *s) { if(s->textField) { free(s->textField); s->textField = NULL; } } void unpackPhysicalAddress( Octet *buf, PhysicalAddress *p, PtpClock *ptpClock) { unpackUInteger16( buf, &p->addressLength, ptpClock); if(p->addressLength) { XMALLOC(p->addressField, p->addressLength); memcpy( p->addressField, buf+2, p->addressLength); } else { p->addressField = NULL; } } void packPhysicalAddress(PhysicalAddress *p, Octet *buf) { packUInteger16(&p->addressLength, buf); if(p->addressLength) { memcpy( buf+2, p->addressField, p->addressLength); } } void freePhysicalAddress(PhysicalAddress *p) { if(p->addressField) { free(p->addressField); p->addressField = NULL; } } void copyClockIdentity( ClockIdentity dest, ClockIdentity src) { memcpy(dest, src, CLOCK_IDENTITY_LENGTH); } void copyPortIdentity( PortIdentity *dest, PortIdentity *src) { copyClockIdentity(dest->clockIdentity, src->clockIdentity); dest->portNumber = src->portNumber; } void unpackMsgHeader(Octet *buf, MsgHeader *header, PtpClock *ptpClock) { int offset = 0; MsgHeader* data = header; #define OPERATE( name, size, type) \ unpack##type (buf + offset, &data->name, ptpClock); \ offset = offset + size; #include "../def/message/header.def" } void packMsgHeader(MsgHeader *h, Octet *buf) { int offset = 0; /* set uninitalized bytes to zero */ h->reserved0 = 0; h->reserved1 = 0; h->reserved2 = 0; #define OPERATE( name, size, type ) \ pack##type( &h->name, buf + offset ); \ offset = offset + size; #include "../def/message/header.def" } void unpackManagementTLV(Octet *buf, MsgManagement *m, PtpClock* ptpClock) { int offset = 0; XMALLOC(m->tlv, sizeof(ManagementTLV)); /* read the management TLV */ #define OPERATE( name, size, type ) \ unpack##type( buf + MANAGEMENT_LENGTH + offset, &m->tlv->name, ptpClock ); \ offset = offset + size; #include "../def/managementTLV/managementTLV.def" } void packManagementTLV(ManagementTLV *tlv, Octet *buf) { int offset = 0; #define OPERATE( name, size, type ) \ pack##type( &tlv->name, buf + MANAGEMENT_LENGTH + offset ); \ offset = offset + size; #include "../def/managementTLV/managementTLV.def" } void freeManagementTLV(MsgManagement *m) { /* cleanup outgoing managementTLV */ if(m->tlv) { if(m->tlv->dataField) { if(m->tlv->tlvType == TLV_MANAGEMENT) { freeMMTLV(m->tlv); } else if(m->tlv->tlvType == TLV_MANAGEMENT_ERROR_STATUS) { freeMMErrorStatusTLV(m->tlv); } free(m->tlv->dataField); m->tlv->dataField = NULL; } free(m->tlv); m->tlv = NULL; } } void packMsgManagement(MsgManagement *m, Octet *buf) { int offset = 0; MsgManagement *data = m; /* set unitialized bytes to zero */ m->reserved0 = 0; m->reserved1 = 0; #define OPERATE( name, size, type) \ pack##type (&data->name, buf + offset); \ offset = offset + size; #include "../def/message/management.def" } void unpackMsgManagement(Octet *buf, MsgManagement *m, PtpClock *ptpClock) { int offset = 0; MsgManagement* data = m; #define OPERATE( name, size, type) \ unpack##type (buf + offset, &data->name, ptpClock); \ offset = offset + size; #include "../def/message/management.def" #ifdef PTPD_DBG msgManagement_display(data); #endif /* PTPD_DBG */ } /*Unpack Header from IN buffer to msgTmpHeader field */ void msgUnpackHeader(Octet * buf, MsgHeader * header) { header->transportSpecific = (*(Nibble *) (buf + 0)) >> 4; header->messageType = (*(Enumeration4 *) (buf + 0)) & 0x0F; header->versionPTP = (*(UInteger4 *) (buf + 1)) & 0x0F; /* force reserved bit to zero if not */ header->messageLength = flip16(*(UInteger16 *) (buf + 2)); header->domainNumber = (*(UInteger8 *) (buf + 4)); header->flagField0 = (*(Octet *) (buf + 6)); header->flagField1 = (*(Octet *) (buf + 7)); memcpy(&header->correctionField.msb, (buf + 8), 4); memcpy(&header->correctionField.lsb, (buf + 12), 4); header->correctionField.msb = flip32(header->correctionField.msb); header->correctionField.lsb = flip32(header->correctionField.lsb); copyClockIdentity(header->sourcePortIdentity.clockIdentity, (buf + 20)); header->sourcePortIdentity.portNumber = flip16(*(UInteger16 *) (buf + 28)); header->sequenceId = flip16(*(UInteger16 *) (buf + 30)); header->controlField = (*(UInteger8 *) (buf + 32)); header->logMessageInterval = (*(Integer8 *) (buf + 33)); #ifdef PTPD_DBG msgHeader_display(header); #endif /* PTPD_DBG */ } /*Pack header message into OUT buffer of ptpClock*/ void msgPackHeader(Octet * buf, PtpClock * ptpClock) { Nibble transport = 0x80; /* (spec annex D) */ *(UInteger8 *) (buf + 0) = transport; *(UInteger4 *) (buf + 1) = ptpClock->versionNumber; *(UInteger8 *) (buf + 4) = ptpClock->domainNumber; /* 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); copyClockIdentity((buf + 20), ptpClock->portIdentity.clockIdentity); *(UInteger16 *) (buf + 28) = flip16(ptpClock->portIdentity.portNumber); *(UInteger8 *) (buf + 33) = 0x7F; /* Default value(spec Table 24) */ } /*Pack SYNC message into OUT buffer of ptpClock*/ void msgPackSync(Octet * buf, Timestamp * originTimestamp, PtpClock * ptpClock) { msgPackHeader(buf, ptpClock); /* 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; /* Table 23 */ *(Integer8 *) (buf + 33) = ptpClock->logSyncInterval; memset((buf + 8), 0, 8); /* Sync message */ *(UInteger16 *) (buf + 34) = flip16(originTimestamp->secondsField.msb); *(UInteger32 *) (buf + 36) = flip32(originTimestamp->secondsField.lsb); *(UInteger32 *) (buf + 40) = flip32(originTimestamp->nanosecondsField); } /*Unpack Sync message from IN buffer */ void msgUnpackSync(Octet * buf, MsgSync * sync) { sync->originTimestamp.secondsField.msb = flip16(*(UInteger16 *) (buf + 34)); sync->originTimestamp.secondsField.lsb = flip32(*(UInteger32 *) (buf + 36)); sync->originTimestamp.nanosecondsField = flip32(*(UInteger32 *) (buf + 40)); #ifdef PTPD_DBG msgSync_display(sync); #endif /* PTPD_DBG */ } /*Pack Announce message into OUT buffer of ptpClock*/ void msgPackAnnounce(Octet * buf, PtpClock * ptpClock) { msgPackHeader(buf, ptpClock); /* 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 + 30) = flip16(ptpClock->sentAnnounceSequenceId); *(UInteger8 *) (buf + 32) = 0x05; /* Table 23 */ *(Integer8 *) (buf + 33) = ptpClock->logAnnounceInterval; /* Announce message */ memset((buf + 34), 0, 10); *(Integer16 *) (buf + 44) = flip16(ptpClock->currentUtcOffset); *(UInteger8 *) (buf + 47) = ptpClock->grandmasterPriority1; *(UInteger8 *) (buf + 48) = ptpClock->clockQuality.clockClass; *(Enumeration8 *) (buf + 49) = ptpClock->clockQuality.clockAccuracy; *(UInteger16 *) (buf + 50) = flip16(ptpClock->clockQuality.offsetScaledLogVariance); *(UInteger8 *) (buf + 52) = ptpClock->grandmasterPriority2; copyClockIdentity((buf + 53), ptpClock->grandmasterIdentity); *(UInteger16 *) (buf + 61) = flip16(ptpClock->stepsRemoved); *(Enumeration8 *) (buf + 63) = ptpClock->timeSource; } /*Unpack Announce message from IN buffer of ptpClock to msgtmp.Announce*/ void msgUnpackAnnounce(Octet * buf, MsgAnnounce * announce) { announce->originTimestamp.secondsField.msb = flip16(*(UInteger16 *) (buf + 34)); announce->originTimestamp.secondsField.lsb = flip32(*(UInteger32 *) (buf + 36)); announce->originTimestamp.nanosecondsField = flip32(*(UInteger32 *) (buf + 40)); announce->currentUtcOffset = flip16(*(UInteger16 *) (buf + 44)); announce->grandmasterPriority1 = *(UInteger8 *) (buf + 47); announce->grandmasterClockQuality.clockClass = *(UInteger8 *) (buf + 48); announce->grandmasterClockQuality.clockAccuracy = *(Enumeration8 *) (buf + 49); announce->grandmasterClockQuality.offsetScaledLogVariance = flip16(*(UInteger16 *) (buf + 50)); announce->grandmasterPriority2 = *(UInteger8 *) (buf + 52); copyClockIdentity(announce->grandmasterIdentity, (buf + 53)); announce->stepsRemoved = flip16(*(UInteger16 *) (buf + 61)); announce->timeSource = *(Enumeration8 *) (buf + 63); #ifdef PTPD_DBG msgAnnounce_display(announce); #endif /* PTPD_DBG */ } /*pack Follow_up message into OUT buffer of ptpClock*/ void msgPackFollowUp(Octet * buf, Timestamp * preciseOriginTimestamp, PtpClock * ptpClock) { msgPackHeader(buf, ptpClock); /* 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); /* sentSyncSequenceId has already been incremented in "issueSync" */ *(UInteger8 *) (buf + 32) = 0x02; /* Table 23 */ *(Integer8 *) (buf + 33) = ptpClock->logSyncInterval; /* Follow_up message */ *(UInteger16 *) (buf + 34) = flip16(preciseOriginTimestamp->secondsField.msb); *(UInteger32 *) (buf + 36) = flip32(preciseOriginTimestamp->secondsField.lsb); *(UInteger32 *) (buf + 40) = flip32(preciseOriginTimestamp->nanosecondsField); } /*Unpack Follow_up message from IN buffer of ptpClock to msgtmp.follow*/ void msgUnpackFollowUp(Octet * buf, MsgFollowUp * follow) { follow->preciseOriginTimestamp.secondsField.msb = flip16(*(UInteger16 *) (buf + 34)); follow->preciseOriginTimestamp.secondsField.lsb = flip32(*(UInteger32 *) (buf + 36)); follow->preciseOriginTimestamp.nanosecondsField = flip32(*(UInteger32 *) (buf + 40)); #ifdef PTPD_DBG msgFollowUp_display(follow); #endif /* PTPD_DBG */ } /*pack PdelayReq message into OUT buffer of ptpClock*/ void msgPackPDelayReq(Octet * buf, Timestamp * originTimestamp, PtpClock * ptpClock) { msgPackHeader(buf, ptpClock); /* 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); *(UInteger8 *) (buf + 32) = 0x05; /* Table 23 */ *(Integer8 *) (buf + 33) = 0x7F; /* Table 24 */ memset((buf + 8), 0, 8); /* Pdelay_req message */ *(UInteger16 *) (buf + 34) = flip16(originTimestamp->secondsField.msb); *(UInteger32 *) (buf + 36) = flip32(originTimestamp->secondsField.lsb); *(UInteger32 *) (buf + 40) = flip32(originTimestamp->nanosecondsField); memset((buf + 44), 0, 10); /* RAZ reserved octets */ } /*pack delayReq message into OUT buffer of ptpClock*/ void msgPackDelayReq(Octet * buf, Timestamp * originTimestamp, PtpClock * ptpClock) { msgPackHeader(buf, ptpClock); /* 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); #ifdef PTP_EXPERIMENTAL if(rtOpts.do_hybrid_mode) *(char *)(buf + 6) |= PTP_UNICAST; #endif *(UInteger16 *) (buf + 30) = flip16(ptpClock->sentDelayReqSequenceId); *(UInteger8 *) (buf + 32) = 0x01; /* Table 23 */ *(Integer8 *) (buf + 33) = 0x7F; /* Table 24 */ memset((buf + 8), 0, 8); /* Pdelay_req message */ *(UInteger16 *) (buf + 34) = flip16(originTimestamp->secondsField.msb); *(UInteger32 *) (buf + 36) = flip32(originTimestamp->secondsField.lsb); *(UInteger32 *) (buf + 40) = flip32(originTimestamp->nanosecondsField); } /*pack delayResp message into OUT buffer of ptpClock*/ void msgPackDelayResp(Octet * buf, MsgHeader * header, Timestamp * receiveTimestamp, PtpClock * ptpClock) { msgPackHeader(buf, ptpClock); /* 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 */ *(Integer32 *) (buf + 8) = flip32(header->correctionField.msb); *(Integer32 *) (buf + 12) = flip32(header->correctionField.lsb); *(UInteger16 *) (buf + 30) = flip16(header->sequenceId); *(UInteger8 *) (buf + 32) = 0x03; /* Table 23 */ *(Integer8 *) (buf + 33) = ptpClock->logMinDelayReqInterval; /* Table 24 */ /* Pdelay_resp message */ *(UInteger16 *) (buf + 34) = flip16(receiveTimestamp->secondsField.msb); *(UInteger32 *) (buf + 36) = flip32(receiveTimestamp->secondsField.lsb); *(UInteger32 *) (buf + 40) = flip32(receiveTimestamp->nanosecondsField); copyClockIdentity((buf + 44), header->sourcePortIdentity.clockIdentity); *(UInteger16 *) (buf + 52) = flip16(header->sourcePortIdentity.portNumber); } /*pack PdelayResp message into OUT buffer of ptpClock*/ void msgPackPDelayResp(Octet * buf, MsgHeader * header, Timestamp * requestReceiptTimestamp, PtpClock * ptpClock) { msgPackHeader(buf, ptpClock); /* 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; memset((buf + 8), 0, 8); *(UInteger16 *) (buf + 30) = flip16(header->sequenceId); *(UInteger8 *) (buf + 32) = 0x05; /* Table 23 */ *(Integer8 *) (buf + 33) = 0x7F; /* Table 24 */ /* Pdelay_resp message */ *(UInteger16 *) (buf + 34) = flip16(requestReceiptTimestamp->secondsField.msb); *(UInteger32 *) (buf + 36) = flip32(requestReceiptTimestamp->secondsField.lsb); *(UInteger32 *) (buf + 40) = flip32(requestReceiptTimestamp->nanosecondsField); copyClockIdentity((buf + 44), header->sourcePortIdentity.clockIdentity); *(UInteger16 *) (buf + 52) = flip16(header->sourcePortIdentity.portNumber); } /*Unpack delayReq message from IN buffer of ptpClock to msgtmp.req*/ void msgUnpackDelayReq(Octet * buf, MsgDelayReq * delayreq) { delayreq->originTimestamp.secondsField.msb = flip16(*(UInteger16 *) (buf + 34)); delayreq->originTimestamp.secondsField.lsb = flip32(*(UInteger32 *) (buf + 36)); delayreq->originTimestamp.nanosecondsField = flip32(*(UInteger32 *) (buf + 40)); #ifdef PTPD_DBG msgDelayReq_display(delayreq); #endif /* PTPD_DBG */ } /*Unpack PdelayReq message from IN buffer of ptpClock to msgtmp.req*/ void msgUnpackPDelayReq(Octet * buf, MsgPDelayReq * pdelayreq) { pdelayreq->originTimestamp.secondsField.msb = flip16(*(UInteger16 *) (buf + 34)); pdelayreq->originTimestamp.secondsField.lsb = flip32(*(UInteger32 *) (buf + 36)); pdelayreq->originTimestamp.nanosecondsField = flip32(*(UInteger32 *) (buf + 40)); #ifdef PTPD_DBG msgPDelayReq_display(pdelayreq); #endif /* PTPD_DBG */ } /*Unpack delayResp message from IN buffer of ptpClock to msgtmp.presp*/ void msgUnpackDelayResp(Octet * buf, MsgDelayResp * resp) { resp->receiveTimestamp.secondsField.msb = flip16(*(UInteger16 *) (buf + 34)); resp->receiveTimestamp.secondsField.lsb = flip32(*(UInteger32 *) (buf + 36)); resp->receiveTimestamp.nanosecondsField = flip32(*(UInteger32 *) (buf + 40)); copyClockIdentity(resp->requestingPortIdentity.clockIdentity, (buf + 44)); resp->requestingPortIdentity.portNumber = flip16(*(UInteger16 *) (buf + 52)); #ifdef PTPD_DBG msgDelayResp_display(resp); #endif /* PTPD_DBG */ } /*Unpack PdelayResp message from IN buffer of ptpClock to msgtmp.presp*/ void msgUnpackPDelayResp(Octet * buf, MsgPDelayResp * presp) { presp->requestReceiptTimestamp.secondsField.msb = flip16(*(UInteger16 *) (buf + 34)); presp->requestReceiptTimestamp.secondsField.lsb = flip32(*(UInteger32 *) (buf + 36)); presp->requestReceiptTimestamp.nanosecondsField = flip32(*(UInteger32 *) (buf + 40)); copyClockIdentity(presp->requestingPortIdentity.clockIdentity, (buf + 44)); presp->requestingPortIdentity.portNumber = flip16(*(UInteger16 *) (buf + 52)); #ifdef PTPD_DBG msgPDelayResp_display(presp); #endif /* PTPD_DBG */ } /*pack PdelayRespfollowup message into OUT buffer of ptpClock*/ void msgPackPDelayRespFollowUp(Octet * buf, MsgHeader * header, Timestamp * responseOriginTimestamp, PtpClock * ptpClock) { msgPackHeader(buf, ptpClock); /* 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); *(UInteger8 *) (buf + 32) = 0x05; /* Table 23 */ *(Integer8 *) (buf + 33) = 0x7F; /* Table 24 */ /* Copy correctionField of PdelayReqMessage */ *(Integer32 *) (buf + 8) = flip32(header->correctionField.msb); *(Integer32 *) (buf + 12) = flip32(header->correctionField.lsb); /* Pdelay_resp_follow_up message */ *(UInteger16 *) (buf + 34) = flip16(responseOriginTimestamp->secondsField.msb); *(UInteger32 *) (buf + 36) = flip32(responseOriginTimestamp->secondsField.lsb); *(UInteger32 *) (buf + 40) = flip32(responseOriginTimestamp->nanosecondsField); copyClockIdentity((buf + 44), header->sourcePortIdentity.clockIdentity); *(UInteger16 *) (buf + 52) = flip16(header->sourcePortIdentity.portNumber); } /*Unpack PdelayResp message from IN buffer of ptpClock to msgtmp.presp*/ void msgUnpackPDelayRespFollowUp(Octet * buf, MsgPDelayRespFollowUp * prespfollow) { prespfollow->responseOriginTimestamp.secondsField.msb = flip16(*(UInteger16 *) (buf + 34)); prespfollow->responseOriginTimestamp.secondsField.lsb = flip32(*(UInteger32 *) (buf + 36)); prespfollow->responseOriginTimestamp.nanosecondsField = flip32(*(UInteger32 *) (buf + 40)); copyClockIdentity(prespfollow->requestingPortIdentity.clockIdentity, (buf + 44)); prespfollow->requestingPortIdentity.portNumber = flip16(*(UInteger16 *) (buf + 52)); #ifdef PTPD_DBG msgPDelayRespFollowUp_display(prespfollow); #endif /* PTPD_DBG */ } /* Pack Management message into OUT buffer */ void msgPackManagementTLV(Octet *buf, MsgManagement *outgoing, PtpClock *ptpClock) { DBGV("packing ManagementTLV message \n"); UInteger16 lengthField = 0; switch(outgoing->tlv->managementId) { case MM_NULL_MANAGEMENT: case MM_SAVE_IN_NON_VOLATILE_STORAGE: case MM_RESET_NON_VOLATILE_STORAGE: case MM_ENABLE_PORT: case MM_DISABLE_PORT: lengthField = 0; break; case MM_CLOCK_DESCRIPTION: lengthField = packMMClockDescription(outgoing, buf); #ifdef PTPD_DBG mMClockDescription_display( (MMClockDescription*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_USER_DESCRIPTION: lengthField = packMMUserDescription(outgoing, buf); #ifdef PTPD_DBG mMUserDescription_display( (MMUserDescription*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_INITIALIZE: lengthField = packMMInitialize(outgoing, buf); #ifdef PTPD_DBG mMInitialize_display( (MMInitialize*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_DEFAULT_DATA_SET: lengthField = packMMDefaultDataSet(outgoing, buf); #ifdef PTPD_DBG mMDefaultDataSet_display( (MMDefaultDataSet*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_CURRENT_DATA_SET: lengthField = packMMCurrentDataSet(outgoing, buf); #ifdef PTPD_DBG mMCurrentDataSet_display( (MMCurrentDataSet*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_PARENT_DATA_SET: lengthField = packMMParentDataSet(outgoing, buf); #ifdef PTPD_DBG mMParentDataSet_display( (MMParentDataSet*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_TIME_PROPERTIES_DATA_SET: lengthField = packMMTimePropertiesDataSet(outgoing, buf); #ifdef PTPD_DBG mMTimePropertiesDataSet_display( (MMTimePropertiesDataSet*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_PORT_DATA_SET: lengthField = packMMPortDataSet(outgoing, buf); #ifdef PTPD_DBG mMPortDataSet_display( (MMPortDataSet*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_PRIORITY1: lengthField = packMMPriority1(outgoing, buf); #ifdef PTPD_DBG mMPriority1_display( (MMPriority1*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_PRIORITY2: lengthField = packMMPriority2(outgoing, buf); #ifdef PTPD_DBG mMPriority2_display( (MMPriority2*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_DOMAIN: lengthField = packMMDomain(outgoing, buf); #ifdef PTPD_DBG mMDomain_display( (MMDomain*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_SLAVE_ONLY: lengthField = packMMSlaveOnly(outgoing, buf); #ifdef PTPD_DBG mMSlaveOnly_display( (MMSlaveOnly*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_LOG_ANNOUNCE_INTERVAL: lengthField = packMMLogAnnounceInterval(outgoing, buf); #ifdef PTPD_DBG mMLogAnnounceInterval_display( (MMLogAnnounceInterval*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_ANNOUNCE_RECEIPT_TIMEOUT: lengthField = packMMAnnounceReceiptTimeout(outgoing, buf); #ifdef PTPD_DBG mMAnnounceReceiptTimeout_display( (MMAnnounceReceiptTimeout*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_LOG_SYNC_INTERVAL: lengthField = packMMLogSyncInterval(outgoing, buf); #ifdef PTPD_DBG mMLogSyncInterval_display( (MMLogSyncInterval*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_VERSION_NUMBER: lengthField = packMMVersionNumber(outgoing, buf); #ifdef PTPD_DBG mMVersionNumber_display( (MMVersionNumber*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_TIME: lengthField = packMMTime(outgoing, buf); #ifdef PTPD_DBG mMTime_display( (MMTime*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_CLOCK_ACCURACY: lengthField = packMMClockAccuracy(outgoing, buf); #ifdef PTPD_DBG mMClockAccuracy_display( (MMClockAccuracy*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_UTC_PROPERTIES: lengthField = packMMUtcProperties(outgoing, buf); #ifdef PTPD_DBG mMUtcProperties_display( (MMUtcProperties*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_TRACEABILITY_PROPERTIES: lengthField = packMMTraceabilityProperties(outgoing, buf); #ifdef PTPD_DBG mMTraceabilityProperties_display( (MMTraceabilityProperties*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_DELAY_MECHANISM: lengthField = packMMDelayMechanism(outgoing, buf); #ifdef PTPD_DBG mMDelayMechanism_display( (MMDelayMechanism*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; case MM_LOG_MIN_PDELAY_REQ_INTERVAL: lengthField = packMMLogMinPdelayReqInterval(outgoing, buf); #ifdef PTPD_DBG mMLogMinPdelayReqInterval_display( (MMLogMinPdelayReqInterval*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ break; default: DBGV("packing management msg: unsupported id \n"); } /* set lengthField */ outgoing->tlv->lengthField = lengthField; packManagementTLV((ManagementTLV*)outgoing->tlv, buf); } /* Pack ManagementErrorStatusTLV message into OUT buffer */ void msgPackManagementErrorStatusTLV(Octet *buf, MsgManagement *outgoing, PtpClock *ptpClock) { DBGV("packing ManagementErrorStatusTLV message \n"); UInteger16 lengthField = 0; lengthField = packMMErrorStatus(outgoing, buf); #ifdef PTPD_DBG mMErrorStatus_display((MMErrorStatus*)outgoing->tlv->dataField, ptpClock); #endif /* PTPD_DBG */ /* set lengthField */ outgoing->tlv->lengthField = lengthField; packManagementTLV((ManagementTLV*)outgoing->tlv, buf); } void freeMMTLV(ManagementTLV* tlv) { DBGV("cleanup managementTLV data\n"); switch(tlv->managementId) { case MM_CLOCK_DESCRIPTION: DBGV("cleanup clock description \n"); freeMMClockDescription((MMClockDescription*)tlv->dataField); break; case MM_USER_DESCRIPTION: DBGV("cleanup user description \n"); freeMMUserDescription((MMUserDescription*)tlv->dataField); break; case MM_NULL_MANAGEMENT: case MM_SAVE_IN_NON_VOLATILE_STORAGE: case MM_RESET_NON_VOLATILE_STORAGE: case MM_INITIALIZE: case MM_DEFAULT_DATA_SET: case MM_CURRENT_DATA_SET: case MM_PARENT_DATA_SET: case MM_TIME_PROPERTIES_DATA_SET: case MM_PORT_DATA_SET: case MM_PRIORITY1: case MM_PRIORITY2: case MM_DOMAIN: case MM_SLAVE_ONLY: case MM_LOG_ANNOUNCE_INTERVAL: case MM_ANNOUNCE_RECEIPT_TIMEOUT: case MM_LOG_SYNC_INTERVAL: case MM_VERSION_NUMBER: case MM_ENABLE_PORT: case MM_DISABLE_PORT: case MM_TIME: case MM_CLOCK_ACCURACY: case MM_UTC_PROPERTIES: case MM_TRACEABILITY_PROPERTIES: case MM_DELAY_MECHANISM: case MM_LOG_MIN_PDELAY_REQ_INTERVAL: default: DBGV("no managementTLV data to cleanup \n"); } } void freeMMErrorStatusTLV(ManagementTLV *tlv) { DBGV("cleanup managementErrorStatusTLV data \n"); freeMMErrorStatus((MMErrorStatus*)tlv->dataField); } void msgPackManagement(Octet *buf, MsgManagement *outgoing, PtpClock *ptpClock) { DBGV("packing management message \n"); packMsgManagement(outgoing, buf); } /*Unpack Management message from IN buffer of ptpClock to msgtmp.manage*/ void msgUnpackManagement(Octet *buf, MsgManagement * manage, MsgHeader * header, PtpClock *ptpClock) { unpackMsgManagement(buf, manage, ptpClock); if ( manage->header.messageLength > MANAGEMENT_LENGTH ) { unpackManagementTLV(buf, manage, ptpClock); /* at this point, we know what managementTLV we have, so return and * let someone else handle the data */ manage->tlv->dataField = NULL; } else /* no TLV attached to this message */ { manage->tlv = NULL; } } /** * Dump the most recent packet in the daemon * * @param ptpClock The central clock structure */ void msgDump(PtpClock *ptpClock) { #if defined(freebsd) static int dumped = 0; #endif /* FreeBSD */ msgDebugHeader(&ptpClock->msgTmpHeader); switch (ptpClock->msgTmpHeader.messageType) { case SYNC: msgDebugSync(&ptpClock->msgTmp.sync); break; case ANNOUNCE: msgDebugAnnounce(&ptpClock->msgTmp.announce); break; case FOLLOW_UP: msgDebugFollowUp(&ptpClock->msgTmp.follow); break; case DELAY_REQ: msgDebugDelayReq(&ptpClock->msgTmp.req); break; case DELAY_RESP: msgDebugDelayResp(&ptpClock->msgTmp.resp); break; case MANAGEMENT: msgDebugManagement(&ptpClock->msgTmp.manage); break; default: NOTIFY("msgDump:unrecognized message\n"); break; } #if defined(freebsd) /* Only dump the first time, after that just do a message. */ if (dumped != 0) return; dumped++; NOTIFY("msgDump: core file created.\n"); switch(rfork(RFFDG|RFPROC|RFNOWAIT)) { case -1: NOTIFY("could not fork to core dump! errno: %s", strerror(errno)); break; case 0: abort(); /* Generate a core dump */ default: /* This default intentionally left blank. */ break; } #endif /* FreeBSD */ } /** * Dump a PTP message header * * @param header a pre-filled msg header structure */ void msgDebugHeader(MsgHeader *header) { NOTIFY("msgDebugHeader: messageType %d\n", header->messageType); NOTIFY("msgDebugHeader: versionPTP %d\n", header->versionPTP); NOTIFY("msgDebugHeader: messageLength %d\n", header->messageLength); NOTIFY("msgDebugHeader: domainNumber %d\n", header->domainNumber); NOTIFY("msgDebugHeader: flags %02hhx %02hhx\n", header->flagField0, header->flagField1); NOTIFY("msgDebugHeader: correctionfield %d\n", header->correctionField); NOTIFY("msgDebugHeader: sourcePortIdentity.clockIdentity " "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx%02hhx:%02hhx\n", header->sourcePortIdentity.clockIdentity[0], header->sourcePortIdentity.clockIdentity[1], header->sourcePortIdentity.clockIdentity[2], header->sourcePortIdentity.clockIdentity[3], header->sourcePortIdentity.clockIdentity[4], header->sourcePortIdentity.clockIdentity[5], header->sourcePortIdentity.clockIdentity[6], header->sourcePortIdentity.clockIdentity[7]); NOTIFY("msgDebugHeader: sourcePortIdentity.portNumber %d\n", header->sourcePortIdentity.portNumber); NOTIFY("msgDebugHeader: sequenceId %d\n", header->sequenceId); NOTIFY("msgDebugHeader: controlField %d\n", header->controlField); NOTIFY("msgDebugHeader: logMessageIntervale %d\n", header->logMessageInterval); } /** * Dump the contents of a sync packet * * @param sync A pre-filled MsgSync structure */ void msgDebugSync(MsgSync *sync) { NOTIFY("msgDebugSync: originTimestamp.seconds %u\n", sync->originTimestamp.secondsField); NOTIFY("msgDebugSync: originTimestamp.nanoseconds %d\n", sync->originTimestamp.nanosecondsField); } /** * Dump the contents of a announce packet * * @param sync A pre-filled MsgAnnounce structure */ void msgDebugAnnounce(MsgAnnounce *announce) { NOTIFY("msgDebugAnnounce: originTimestamp.seconds %u\n", announce->originTimestamp.secondsField); NOTIFY("msgDebugAnnounce: originTimestamp.nanoseconds %d\n", announce->originTimestamp.nanosecondsField); NOTIFY("msgDebugAnnounce: currentUTCOffset %d\n", announce->currentUtcOffset); NOTIFY("msgDebugAnnounce: grandmasterPriority1 %d\n", announce->grandmasterPriority1); NOTIFY("msgDebugAnnounce: grandmasterClockQuality.clockClass %d\n", announce->grandmasterClockQuality.clockClass); NOTIFY("msgDebugAnnounce: grandmasterClockQuality.clockAccuracy %d\n", announce->grandmasterClockQuality.clockAccuracy); NOTIFY("msgDebugAnnounce: " "grandmasterClockQuality.offsetScaledLogVariance %d\n", announce->grandmasterClockQuality.offsetScaledLogVariance); NOTIFY("msgDebugAnnounce: grandmasterPriority2 %d\n", announce->grandmasterPriority2); NOTIFY("msgDebugAnnounce: grandmasterClockIdentity " "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx%02hhx:%02hhx\n", announce->grandmasterIdentity[0], announce->grandmasterIdentity[1], announce->grandmasterIdentity[2], announce->grandmasterIdentity[3], announce->grandmasterIdentity[4], announce->grandmasterIdentity[5], announce->grandmasterIdentity[6], announce->grandmasterIdentity[7]); NOTIFY("msgDebugAnnounce: stepsRemoved %d\n", announce->stepsRemoved); NOTIFY("msgDebugAnnounce: timeSource %d\n", announce->timeSource); } /** * NOT IMPLEMENTED * * @param req */ void msgDebugDelayReq(MsgDelayReq *req) {} /** * Dump the contents of a followup packet * * @param follow A pre-fille MsgFollowUp structure */ void msgDebugFollowUp(MsgFollowUp *follow) { NOTIFY("msgDebugFollowUp: preciseOriginTimestamp.seconds %u\n", follow->preciseOriginTimestamp.secondsField); NOTIFY("msgDebugFollowUp: preciseOriginTimestamp.nanoseconds %d\n", follow->preciseOriginTimestamp.nanosecondsField); } /** * Dump the contents of a delay response packet * * @param resp a pre-filled MsgDelayResp structure */ void msgDebugDelayResp(MsgDelayResp *resp) { NOTIFY("msgDebugDelayResp: delayReceiptTimestamp.seconds %u\n", resp->receiveTimestamp.secondsField); NOTIFY("msgDebugDelayResp: delayReceiptTimestamp.nanoseconds %d\n", resp->receiveTimestamp.nanosecondsField); NOTIFY("msgDebugDelayResp: requestingPortIdentity.clockIdentity " "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx%02hhx:%02hhx\n", resp->requestingPortIdentity.clockIdentity[0], resp->requestingPortIdentity.clockIdentity[1], resp->requestingPortIdentity.clockIdentity[2], resp->requestingPortIdentity.clockIdentity[3], resp->requestingPortIdentity.clockIdentity[4], resp->requestingPortIdentity.clockIdentity[5], resp->requestingPortIdentity.clockIdentity[6], resp->requestingPortIdentity.clockIdentity[7]); NOTIFY("msgDebugDelayResp: requestingPortIdentity.portNumber %d\n", resp->requestingPortIdentity.portNumber); } /** * Dump the contents of management packet * * @param manage a pre-filled MsgManagement structure */ void msgDebugManagement(MsgManagement *manage) { NOTIFY("msgDebugDelayManage: targetPortIdentity.clockIdentity " "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", manage->targetPortIdentity.clockIdentity[0], manage->targetPortIdentity.clockIdentity[1], manage->targetPortIdentity.clockIdentity[2], manage->targetPortIdentity.clockIdentity[3], manage->targetPortIdentity.clockIdentity[4], manage->targetPortIdentity.clockIdentity[5], manage->targetPortIdentity.clockIdentity[6], manage->targetPortIdentity.clockIdentity[7]); NOTIFY("msgDebugDelayManage: targetPortIdentity.portNumber %d\n", manage->targetPortIdentity.portNumber); NOTIFY("msgDebugManagement: startingBoundaryHops %d\n", manage->startingBoundaryHops); NOTIFY("msgDebugManagement: boundaryHops %d\n", manage->boundaryHops); NOTIFY("msgDebugManagement: actionField %d\n", manage->actionField); NOTIFY("msgDebugManagement: tvl %s\n", manage->tlv); } ptpd-2.2.2/src/dep/datatypes_dep.h0000600000175000017500000000421311770426153016006 0ustar ernieernie#ifndef DATATYPES_DEP_H_ #define DATATYPES_DEP_H_ /** *\file * \brief Implementation specific datatype */ /* FIXME: shouldn't uint32_t and friends be used here? */ typedef enum {FALSE=0, TRUE} Boolean; typedef char Octet; typedef signed char Integer8; typedef signed short Integer16; typedef signed int Integer32; typedef unsigned char UInteger8; typedef unsigned short UInteger16; typedef unsigned int UInteger32; typedef unsigned short Enumeration16; typedef unsigned char Enumeration8; typedef unsigned char Enumeration4; typedef unsigned char Enumeration4Upper; typedef unsigned char Enumeration4Lower; typedef unsigned char UInteger4; typedef unsigned char UInteger4Upper; typedef unsigned char UInteger4Lower; typedef unsigned char Nibble; typedef unsigned char NibbleUpper; typedef unsigned char NibbleLower; /** * \brief Implementation specific of UInteger48 type */ typedef struct { unsigned int lsb; /* FIXME: shouldn't uint32_t and uint16_t be used here? */ unsigned short msb; } UInteger48; /** * \brief Implementation specific of Integer64 type */ typedef struct { unsigned int lsb; /* FIXME: shouldn't uint32_t and int32_t be used here? */ int msb; } Integer64; /** * \brief Struct used to average the offset from master * * The FIR filtering of the offset from master input is a simple, two-sample average */ typedef struct { Integer32 nsec_prev, y; } offset_from_master_filter; /** * \brief Struct used to average the one way delay * * It is a variable cutoff/delay low-pass, infinite impulse response (IIR) filter. * * The one-way delay filter has the difference equation: s*y[n] - (s-1)*y[n-1] = x[n]/2 + x[n-1]/2, where increasing the stiffness (s) lowers the cutoff and increases the delay. */ typedef struct { Integer32 nsec_prev, y; Integer32 s_exp; } one_way_delay_filter; /** * \brief Struct used to store network datas */ typedef struct { Integer32 eventSock, generalSock, multicastAddr, peerMulticastAddr,unicastAddr; /* used by IGMP refresh */ struct in_addr interfaceAddr; #ifdef PTP_EXPERIMENTAL /* used for Hybrid mode */ Integer32 lastRecvAddr; #endif } NetPath; #endif /*DATATYPES_DEP_H_*/ ptpd-2.2.2/src/dep/constants_dep.h0000600000175000017500000000514411770426153016030 0ustar ernieernie /* constants_dep.h */ #ifndef CONSTANTS_DEP_H #define CONSTANTS_DEP_H /** *\file * \brief Plateform-dependent constants definition * * This header defines all includes and constants which are plateform-dependent * * ptpdv2 is only implemented for linux, NetBSD and FreeBSD */ /* platform dependent */ #if !defined(linux) && !defined(__NetBSD__) && !defined(__FreeBSD__) && \ !defined(__APPLE__) #error Not ported to this architecture, please update. #endif #ifdef linux #include #include #include #define IFACE_NAME_LENGTH IF_NAMESIZE #define NET_ADDRESS_LENGTH INET_ADDRSTRLEN #define IFCONF_LENGTH 10 #include #if __BYTE_ORDER == __LITTLE_ENDIAN #define PTPD_LSBF #elif __BYTE_ORDER == __BIG_ENDIAN #define PTPD_MSBF #endif #endif /* linux */ #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__) # include # include # include # include # include # include # if defined(__FreeBSD__) || defined(__APPLE__) # include # include # else # include # endif # include # define IFACE_NAME_LENGTH IF_NAMESIZE # define NET_ADDRESS_LENGTH INET_ADDRSTRLEN # define IFCONF_LENGTH 10 # define adjtimex ntp_adjtime # include # if BYTE_ORDER == LITTLE_ENDIAN # define PTPD_LSBF # elif BYTE_ORDER == BIG_ENDIAN # define PTPD_MSBF # endif #endif #define CLOCK_IDENTITY_LENGTH 8 #define ADJ_FREQ_MAX 512000 /* UDP/IPv4 dependent */ #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001UL #endif #define SUBDOMAIN_ADDRESS_LENGTH 4 #define PORT_ADDRESS_LENGTH 2 #define PTP_UUID_LENGTH 6 #define CLOCK_IDENTITY_LENGTH 8 #define FLAG_FIELD_LENGTH 2 #define PACKET_SIZE 300 //ptpdv1 value kept because of use of TLV... #define PTP_EVENT_PORT 319 #define PTP_GENERAL_PORT 320 #define DEFAULT_PTP_DOMAIN_ADDRESS "224.0.1.129" #define PEER_PTP_DOMAIN_ADDRESS "224.0.0.107" /* used for -I option */ #define ALTERNATE_PTP_DOMAIN1_ADDRESS "224.0.1.130" #define ALTERNATE_PTP_DOMAIN2_ADDRESS "224.0.1.131" #define ALTERNATE_PTP_DOMAIN3_ADDRESS "224.0.1.132" #define MM_STARTING_BOUNDARY_HOPS 0x7fff /* others */ /* bigger screen size constants */ #define SCREEN_BUFSZ 228 #define SCREEN_MAXSZ 180 /* default size for string buffers */ #define BUF_SIZE 1000 #define NANOSECONDS_MAX 999999999 // limit operator messages to once every X seconds #define OPERATOR_MESSAGES_INTERVAL 300.0 #define MAXTIMESTR 32 #endif /*CONSTANTS_DEP_H_*/ ptpd-2.2.2/src/dep/ptpd_dep.h0000600000175000017500000003233511770426153014765 0ustar ernieernie/** * @file ptpd_dep.h * * @brief External definitions for inclusion elsewhere. * * */ #ifndef PTPD_DEP_H_ #define PTPD_DEP_H_ #ifdef RUNTIME_DEBUG #undef PTPD_DBGV #define PTPD_DBGV #endif #ifdef DBG_SIGUSR2_CHANGE_DOMAIN #ifdef DBG_SIGUSR2_CHANGE_DEBUG #error "Cannot compile with both DBG_SIGUSR2_CHANGE_DOMAIN and DBG_SIGUSR2_CHANGE_DEBUG" #endif #endif /** \name System messages*/ /**\{*/ // Syslog ordering. We define extra debug levels above LOG_DEBUG for internal use - but message() doesn't pass these to SysLog // extended from #define LOG_DEBUG1 7 #define LOG_DEBUG2 8 #define LOG_DEBUG3 9 #define LOG_DEBUGV 9 #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 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 NOTICE(x, ...) message(LOG_NOTICE, x, ##__VA_ARGS__) #define INFO(x, ...) message(LOG_INFO, x, ##__VA_ARGS__) #include /* list of per-module defines: ./dep/sys.c:#define PRINT_MAC_ADDRESSES ./dep/timer.c:#define US_TIMER_INTERVAL 125000 */ #define USE_BINDTODEVICE // enable this line to show debug numbers in nanoseconds instead of microseconds // #define DEBUG_IN_NS #define DBG_UNIT_US (1000) #define DBG_UNIT_NS (1) #ifdef DEBUG_IN_NS #define DBG_UNIT DBG_UNIT_NS #else #define DBG_UNIT DBG_UNIT_US #endif /** \}*/ /** \name Debug messages*/ /**\{*/ #ifdef PTPD_DBGV #undef PTPD_DBG #undef PTPD_DBG2 #define PTPD_DBG #define PTPD_DBG2 #define DBGV(x, ...) message(LOG_DEBUGV, x, ##__VA_ARGS__) #else #define DBGV(x, ...) #endif /* * new debug level DBG2: * this is above DBG(), but below DBGV() (to avoid changing hundreds of lines) */ #ifdef PTPD_DBG2 #undef PTPD_DBG #define PTPD_DBG #define DBG2(x, ...) message(LOG_DEBUG2, x, ##__VA_ARGS__) #else #define DBG2(x, ...) #endif #ifdef PTPD_DBG #define DBG(x, ...) message(LOG_DEBUG, x, ##__VA_ARGS__) #else #define DBG(x, ...) #endif /** \}*/ /** \name Endian corrections*/ /**\{*/ #if defined(PTPD_MSBF) #define shift8(x,y) ( (x) << ((3-y)<<3) ) #define shift16(x,y) ( (x) << ((1-y)<<4) ) #elif defined(PTPD_LSBF) #define shift8(x,y) ( (x) << ((y)<<3) ) #define shift16(x,y) ( (x) << ((y)<<4) ) #endif #define flip16(x) htons(x) #define flip32(x) htonl(x) /* i don't know any target platforms that do not have htons and htonl, but here are generic funtions just in case */ /* #if defined(PTPD_MSBF) #define flip16(x) (x) #define flip32(x) (x) #elif defined(PTPD_LSBF) static inline Integer16 flip16(Integer16 x) { return (((x) >> 8) & 0x00ff) | (((x) << 8) & 0xff00); } static inline Integer32 flip32(x) { return (((x) >> 24) & 0x000000ff) | (((x) >> 8 ) & 0x0000ff00) | (((x) << 8 ) & 0x00ff0000) | (((x) << 24) & 0xff000000); } #endif */ /** \}*/ /** \name Bit array manipulations*/ /**\{*/ #define getFlag(x,y) !!( *(UInteger8*)((x)+((y)<8?1:0)) & (1<<((y)<8?(y):(y)-8)) ) #define setFlag(x,y) ( *(UInteger8*)((x)+((y)<8?1:0)) |= 1<<((y)<8?(y):(y)-8) ) #define clearFlag(x,y) ( *(UInteger8*)((x)+((y)<8?1:0)) &= ~(1<<((y)<8?(y):(y)-8)) ) /** \}*/ /** \name msg.c *-Pack and unpack PTP messages */ /**\{*/ void msgUnpackHeader(Octet * buf,MsgHeader*); void msgUnpackAnnounce (Octet * buf,MsgAnnounce*); void msgUnpackSync(Octet * buf,MsgSync*); void msgUnpackFollowUp(Octet * buf,MsgFollowUp*); void msgUnpackDelayReq(Octet * buf, MsgDelayReq * delayreq); void msgUnpackDelayResp(Octet * buf,MsgDelayResp *); void msgUnpackPDelayReq(Octet * buf,MsgPDelayReq*); void msgUnpackPDelayResp(Octet * buf,MsgPDelayResp*); void msgUnpackPDelayRespFollowUp(Octet * buf,MsgPDelayRespFollowUp*); void msgUnpackManagement(Octet * buf,MsgManagement*, MsgHeader*, PtpClock *ptpClock); void msgPackHeader(Octet * buf,PtpClock*); void msgPackAnnounce(Octet * buf,PtpClock*); void msgPackSync(Octet * buf,Timestamp*,PtpClock*); void msgPackFollowUp(Octet * buf,Timestamp*,PtpClock*); void msgPackDelayReq(Octet * buf,Timestamp *,PtpClock *); void msgPackDelayResp(Octet * buf,MsgHeader *,Timestamp *,PtpClock *); void msgPackPDelayReq(Octet * buf,Timestamp*,PtpClock*); void msgPackPDelayResp(Octet * buf,MsgHeader*,Timestamp*,PtpClock*); void msgPackPDelayRespFollowUp(Octet * buf,MsgHeader*,Timestamp*,PtpClock*); void msgPackManagement(Octet * buf,MsgManagement*,PtpClock*); void msgPackManagementRespAck(Octet *,MsgManagement*,PtpClock*); void msgPackManagementTLV(Octet *,MsgManagement*, PtpClock*); void msgPackManagementErrorStatusTLV(Octet *,MsgManagement*,PtpClock*); void freeMMErrorStatusTLV(ManagementTLV*); void freeMMTLV(ManagementTLV*); void msgDump(PtpClock *ptpClock); void msgDebugHeader(MsgHeader *header); void msgDebugSync(MsgSync *sync); void msgDebugAnnounce(MsgAnnounce *announce); void msgDebugDelayReq(MsgDelayReq *req); void msgDebugFollowUp(MsgFollowUp *follow); void msgDebugDelayResp(MsgDelayResp *resp); void msgDebugManagement(MsgManagement *manage); void copyClockIdentity( ClockIdentity dest, ClockIdentity src); void copyPortIdentity( PortIdentity * dest, PortIdentity * src); void unpackMsgManagement(Octet *, MsgManagement*, PtpClock*); void packMsgManagement(MsgManagement*, Octet *); void unpackManagementTLV(Octet*, MsgManagement*, PtpClock*); void packManagementTLV(ManagementTLV*, Octet*); void freeManagementTLV(MsgManagement*); void unpackMMClockDescription( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMClockDescription( MsgManagement*, Octet*); void freeMMClockDescription( MMClockDescription*); void unpackMMUserDescription( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMUserDescription( MsgManagement*, Octet*); void freeMMUserDescription( MMUserDescription*); void unpackMMErrorStatus( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMErrorStatus( MsgManagement*, Octet*); void freeMMErrorStatus( MMErrorStatus*); void unpackMMInitialize( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMInitialize( MsgManagement*, Octet*); void unpackMMDefaultDataSet( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMDefaultDataSet( MsgManagement*, Octet*); void unpackMMCurrentDataSet( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMCurrentDataSet( MsgManagement*, Octet*); void unpackMMParentDataSet( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMParentDataSet( MsgManagement*, Octet*); void unpackMMTimePropertiesDataSet( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMTimePropertiesDataSet( MsgManagement*, Octet*); void unpackMMPortDataSet( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMPortDataSet( MsgManagement*, Octet*); void unpackMMPriority1( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMPriority1( MsgManagement*, Octet*); void unpackMMPriority2( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMPriority2( MsgManagement*, Octet*); void unpackMMDomain( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMDomain( MsgManagement*, Octet*); void unpackMMSlaveOnly( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMSlaveOnly( MsgManagement*, Octet* ); void unpackMMLogAnnounceInterval( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMLogAnnounceInterval( MsgManagement*, Octet*); void unpackMMAnnounceReceiptTimeout( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMAnnounceReceiptTimeout( MsgManagement*, Octet*); void unpackMMLogSyncInterval( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMLogSyncInterval( MsgManagement*, Octet*); void unpackMMVersionNumber( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMVersionNumber( MsgManagement*, Octet*); void unpackMMTime( Octet* buf, MsgManagement*, PtpClock * ); UInteger16 packMMTime( MsgManagement*, Octet*); void unpackMMClockAccuracy( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMClockAccuracy( MsgManagement*, Octet*); void unpackMMUtcProperties( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMUtcProperties( MsgManagement*, Octet*); void unpackMMTraceabilityProperties( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMTraceabilityProperties( MsgManagement*, Octet*); void unpackMMDelayMechanism( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMDelayMechanism( MsgManagement*, Octet*); void unpackMMLogMinPdelayReqInterval( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMLogMinPdelayReqInterval( MsgManagement*, Octet*); void unpackMMErrorStatus( Octet* buf, MsgManagement*, PtpClock* ); UInteger16 packMMErrorStatus( MsgManagement*, Octet*); void unpackPortAddress( Octet* buf, PortAddress*, PtpClock*); void packPortAddress( PortAddress*, Octet*); void freePortAddress( PortAddress*); void unpackPTPText( Octet* buf, PTPText*, PtpClock*); void packPTPText( PTPText*, Octet*); void freePTPText( PTPText*); void unpackPhysicalAddress( Octet* buf, PhysicalAddress*, PtpClock*); void packPhysicalAddress( PhysicalAddress*, Octet*); void freePhysicalAddress( PhysicalAddress*); void unpackClockIdentity( Octet* buf, ClockIdentity *c, PtpClock*); void packClockIdentity( ClockIdentity *c, Octet* buf); void freeClockIdentity( ClockIdentity *c); void unpackClockQuality( Octet* buf, ClockQuality *c, PtpClock*); void packClockQuality( ClockQuality *c, Octet* buf); void freeClockQuality( ClockQuality *c); void unpackTimeInterval( Octet* buf, TimeInterval *t, PtpClock*); void packTimeInterval( TimeInterval *t, Octet* buf); void freeTimeInterval( TimeInterval *t); void unpackPortIdentity( Octet* buf, PortIdentity *p, PtpClock*); void packPortIdentity( PortIdentity *p, Octet* buf); void freePortIdentity( PortIdentity *p); void unpackTimestamp( Octet* buf, Timestamp *t, PtpClock*); void packTimestamp( Timestamp *t, Octet* buf); void freeTimestamp( Timestamp *t); UInteger16 msgPackManagementResponse(Octet * buf,MsgHeader*,MsgManagement*,PtpClock*); /** \}*/ /** \name net.c (Unix API dependent) * -Init network stuff, send and receive datas*/ /**\{*/ 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*); Boolean netRefreshIGMP(NetPath *, RunTimeOpts *, PtpClock *); /** \}*/ /** \name servo.c * -Clock servo*/ /**\{*/ void initClock(RunTimeOpts*,PtpClock*); void updatePeerDelay (one_way_delay_filter*, RunTimeOpts*,PtpClock*,TimeInternal*,Boolean); void updateDelay (one_way_delay_filter*, RunTimeOpts*, PtpClock*,TimeInternal*); void updateOffset(TimeInternal*,TimeInternal*, offset_from_master_filter*,RunTimeOpts*,PtpClock*,TimeInternal*); void updateClock(RunTimeOpts*,PtpClock*); void servo_perform_clock_step(RunTimeOpts * rtOpts, PtpClock * ptpClock); /** \}*/ /** \name startup.c (Unix API dependent) * -Handle with runtime options*/ /**\{*/ int logToFile(RunTimeOpts * rtOpts); int recordToFile(RunTimeOpts * rtOpts); PtpClock * ptpdStartup(int,char**,Integer16*,RunTimeOpts*); void ptpdShutdown(PtpClock * ptpClock); void check_signals(RunTimeOpts * rtOpts, PtpClock * ptpClock); void enable_runtime_debug(void ); void disable_runtime_debug(void ); #define D_ON do { enable_runtime_debug(); } while (0); #define D_OFF do { disable_runtime_debug( ); } while (0); /** \}*/ /** \name sys.c (Unix API dependent) * -Manage timing system API*/ /**\{*/ /* new debug methods to debug time variables */ char *time2st(const TimeInternal * p); void DBG_time(const char *name, const TimeInternal p); void message(int priority, const char *format, ...); void displayStats(RunTimeOpts *rtOpts, PtpClock *ptpClock); void increaseMaxDelayThreshold(); void decreaseMaxDelayThreshold(); Boolean nanoSleep(TimeInternal*); void getTime(TimeInternal*); void setTime(TimeInternal*); double getRand(void); Boolean adjFreq(Integer32); void recordSync(RunTimeOpts * rtOpts, UInteger16 sequenceId, TimeInternal * time); #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); #if defined(MOD_TAI) && NTP_API == 4 void setKernelUtcOffset(int utc_offset); #endif /* MOD_TAI */ #endif /* apple */ /** \}*/ /** \name timer.c (Unix API dependent) * -Handle with timers*/ /**\{*/ void initTimer(void); void timerUpdate(IntervalTimer*); void timerStop(UInteger16,IntervalTimer*); //void timerStart(UInteger16,UInteger16,IntervalTimer*); /* R135 patch: we went back to floating point periods (for less than 1s )*/ void timerStart(UInteger16 index, float interval, IntervalTimer * itimer); /* Version with randomized backoff */ void timerStart_random(UInteger16 index, float interval, IntervalTimer * itimer); Boolean timerExpired(UInteger16,IntervalTimer*); /** \}*/ /*Test functions*/ void reset_operator_messages(RunTimeOpts * rtOpts, PtpClock * ptpClock); #endif /*PTPD_DEP_H_*/ ptpd-2.2.2/src/dep/timer.c0000600000175000017500000001575111770426153014304 0ustar ernieernie/*- * Copyright (c) 2011-2012 George V. Neville-Neil, * Steven Kreuzer, * Martin Burnicki, * Jan Breuer, * Gael Mace, * Alexandre Van Kempen, * Inaqui Delgado, * Rick Ratzel, * National Instruments. * Copyright (c) 2009-2010 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. */ /** * @file timer.c * @date Wed Jun 23 09:41:26 2010 * * @brief The timers which run the state machine. * * Timers in the PTP daemon are run off of the signal system. */ #include "../ptpd.h" #define US_TIMER_INTERVAL (62500) volatile unsigned int elapsed; /* * original code calls sigalarm every fixed 1ms. This highly pollutes the debug_log, and causes more interrupted instructions * This was later modified to have a fixed granularity of 1s. * * Currently this has a configured granularity, and timerStart() guarantees that clocks expire ASAP when the granularity is too small. * Timers must now be explicitelly canceled with timerStop (instead of timerStart(0.0)) */ void catch_alarm(int sig) { elapsed++; /* be sure to NOT call DBG in asynchronous handlers! */ } void initTimer(void) { struct itimerval itimer; DBG("initTimer\n"); signal(SIGALRM, SIG_IGN); elapsed = 0; itimer.it_value.tv_sec = itimer.it_interval.tv_sec = 0; itimer.it_value.tv_usec = itimer.it_interval.tv_usec = US_TIMER_INTERVAL; signal(SIGALRM, catch_alarm); setitimer(ITIMER_REAL, &itimer, 0); } void timerUpdate(IntervalTimer * itimer) { int i, delta; /* * latch how many ticks we got since we were last called * remember that catch_alarm() is totally asynchronous to this timerUpdate() */ delta = elapsed; elapsed = 0; if (delta <= 0) return; /* * if time actually passed, then decrease every timer left * the one(s) that went to zero or negative are: * a) rearmed at the original time (ignoring the time that may have passed ahead) * b) have their expiration latched until timerExpired() is called */ for (i = 0; i < TIMER_ARRAY_SIZE; ++i) { if ((itimer[i].interval) > 0 && ((itimer[i].left) -= delta) <= 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 ); } } } void timerStop(UInteger16 index, IntervalTimer * itimer) { if (index >= TIMER_ARRAY_SIZE) return; itimer[index].interval = 0; DBG2("timerStop: Stopping timer %d. (New interval: %d; New left: %d)\n", index, itimer[index].left , itimer[index].interval); } void timerStart(UInteger16 index, float interval, IntervalTimer * itimer) { if (index >= TIMER_ARRAY_SIZE) return; itimer[index].expire = FALSE; /* * US_TIMER_INTERVAL defines the minimum interval between sigalarms. * timerStart has a float parameter for the interval, which is casted to integer. * very small amounts are forced to expire ASAP by setting the interval to 1 */ itimer[index].left = (int)((interval * 1E6) / US_TIMER_INTERVAL); if(itimer[index].left == 0){ /* * the interval is too small, raise it to 1 to make sure it expires ASAP * Timer cancelation is done explicitelly with stopTimer() */ itimer[index].left = 1; static int operator_warned_interval_too_small = 0; if(!operator_warned_interval_too_small){ operator_warned_interval_too_small = 1; /* * using random uniform timers it is pratically guarantted that we hit the possible minimum timer. * This is because of the current timer model based on periodic sigalarm, irrespective if the next * event is close or far away in time. * * A solution would be to recode this whole module with a calendar queue, while keeping the same API: * E.g.: http://www.isi.edu/nsnam/ns/doc/node35.html * * Having events that expire immediatly (ie, delayreq invocations using random timers) can lead to * messages appearing in unexpected ordering, so the protocol implementation must check more conditions * and not assume a certain ususal ordering */ 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); } /* * This function arms the timer with a uniform range, as requested by page 105 of the standard (for sending delayReqs.) * actual time will be U(0, interval * 2.0); * * 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); timerStart(index, new_value, itimer); } Boolean timerExpired(UInteger16 index, IntervalTimer * itimer) { timerUpdate(itimer); if (index >= TIMER_ARRAY_SIZE) return FALSE; if (!itimer[index].expire) return FALSE; 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); return TRUE; } ptpd-2.2.2/src/dep/servo.c0000600000175000017500000004442111770426153014316 0ustar ernieernie/*- * Copyright (c) 2011-2012 George V. Neville-Neil, * Steven Kreuzer, * Martin Burnicki, * Jan Breuer, * Gael Mace, * Alexandre Van Kempen, * Inaqui Delgado, * Rick Ratzel, * National Instruments. * Copyright (c) 2009-2010 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. */ /** * @file servo.c * @date Tue Jul 20 16:19:19 2010 * * @brief Code which implements the clock servo in software. * * */ #include "../ptpd.h" void reset_operator_messages(RunTimeOpts * rtOpts, PtpClock * ptpClock) { ptpClock->warned_operator_slow_slewing = 0; ptpClock->warned_operator_fast_slewing = 0; //ptpClock->seen_servo_stable_first_time = FALSE; } void initClock(RunTimeOpts * rtOpts, PtpClock * ptpClock) { DBG("initClock\n"); /* clear vars */ ptpClock->owd_filt.s_exp = 0; /* clears one-way delay filter */ #if !defined(__APPLE__) if (!rtOpts->noAdjust) adjFreq(0); ptpClock->observed_drift = 0; #endif /* apple */ /* clean more original filter variables */ clearTime(&ptpClock->offsetFromMaster); clearTime(&ptpClock->meanPathDelay); clearTime(&ptpClock->delaySM); clearTime(&ptpClock->delayMS); 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->char_last_msg='I'; reset_operator_messages(rtOpts, ptpClock); #ifdef PTP_EXPERIMENTAL /* For Hybrid mode */ ptpClock->MasterAddr = 0; #endif } 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; ptpClock->char_last_msg='D'; { //perform basic checks, using local variables only TimeInternal slave_to_master_delay; /* calc 'slave_to_master_delay' */ subTime(&slave_to_master_delay, &ptpClock->delay_req_receive_time, &ptpClock->delay_req_send_time); if (rtOpts->maxDelay && /* If maxDelay is 0 then it's OFF */ rtOpts->offset_first_updated) { if ((slave_to_master_delay.nanoseconds < 0) && (abs(slave_to_master_delay.nanoseconds) > rtOpts->maxDelay)) { INFO("updateDelay aborted, delay (sec: %d ns: %d) " "is negative\n", slave_to_master_delay.seconds, slave_to_master_delay.nanoseconds); INFO("send (sec: %d ns: %d)\n ", ptpClock->delay_req_send_time.seconds, ptpClock->delay_req_send_time.nanoseconds); INFO("recv (sec: %d n s: %d)\n", ptpClock->delay_req_receive_time.seconds, ptpClock->delay_req_receive_time.nanoseconds); ptpClock->discardedPacketCount++; goto autotune; } if (slave_to_master_delay.seconds && rtOpts->maxDelay) { INFO("updateDelay aborted, delay greater than 1" " second.\n"); if (rtOpts->displayPackets) msgDump(ptpClock); ptpClock->discardedPacketCount++; goto autotune; } 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); if (rtOpts->displayPackets) msgDump(ptpClock); ptpClock->discardedPacketCount++; goto autotune; } } } /* * The packet has passed basic checks, so we'll: * - update the global delaySM variable * - calculate a new filtered MPD */ if (rtOpts->offset_first_updated) { Integer16 s; ptpClock->discardedPacketCount--; /* * calc 'slave_to_master_delay' (Master to Slave delay is * already computed in updateOffset ) */ DBG("==> UpdateDelay(): %s\n", dump_TimeInternal2("Req_RECV:", &ptpClock->delay_req_receive_time, "Req_SENT:", &ptpClock->delay_req_send_time)); subTime(&ptpClock->delaySM, &ptpClock->delay_req_receive_time, &ptpClock->delay_req_send_time); /* update 'one_way_delay' */ addTime(&ptpClock->meanPathDelay, &ptpClock->delaySM, &ptpClock->delayMS); /* Substract correctionField */ subTime(&ptpClock->meanPathDelay, &ptpClock->meanPathDelay, correctionField); /* Compute one-way delay */ div2Time(&ptpClock->meanPathDelay); if (ptpClock->meanPathDelay.seconds) { DBG(" update delay: cannot filter with large OFM, clearing filter \n"); INFO("Servo: Ignoring delayResp because of large OFM\n"); owd_filt->s_exp = owd_filt->nsec_prev = 0; ptpClock->meanPathDelay = prev_meanPathDelay; /* revert back to previous value */ goto display; } if(ptpClock->meanPathDelay.nanoseconds < 0){ DBG(" updatedelay: found negative value for OWD, so ignoring this value\n"); ptpClock->meanPathDelay = prev_meanPathDelay; /* revert back to previous value */ goto display; } /* avoid overflowing filter */ s = rtOpts->s; while (abs(owd_filt->y) >> (31 - s)) --s; /* crank down filter cutoff by increasing 's_exp' */ if (owd_filt->s_exp < 1) owd_filt->s_exp = 1; else if (owd_filt->s_exp < 1 << s) ++owd_filt->s_exp; else if (owd_filt->s_exp > 1 << s) owd_filt->s_exp = 1 << s; /* filter 'meanPathDelay' */ owd_filt->y = (owd_filt->s_exp - 1) * owd_filt->y / owd_filt->s_exp + (ptpClock->meanPathDelay.nanoseconds / 2 + owd_filt->nsec_prev / 2) / owd_filt->s_exp; owd_filt->nsec_prev = ptpClock->meanPathDelay.nanoseconds; ptpClock->meanPathDelay.nanoseconds = owd_filt->y; DBGV("delay filter %d, %d\n", owd_filt->y, owd_filt->s_exp); } else { INFO("Ignoring delayResp because we didn't receive any sync yet\n"); } autotune: if (rtOpts->maxDelayAutoTune) { if (ptpClock->discardedPacketCount >= rtOpts->discardedPacketThreshold) { ptpClock->discardedPacketCount = 0; increaseMaxDelayThreshold(); goto display; } if (ptpClock->discardedPacketCount < (rtOpts->discardedPacketThreshold * -1)) { ptpClock->discardedPacketCount = 0; decreaseMaxDelayThreshold(); } } display: displayStats(rtOpts, ptpClock); } void updatePeerDelay(one_way_delay_filter * owd_filt, RunTimeOpts * rtOpts, PtpClock * ptpClock, TimeInternal * correctionField, Boolean twoStep) { Integer16 s; DBGV("updateDelay\n"); if (twoStep) { /* calc 'slave_to_master_delay' */ subTime(&ptpClock->pdelayMS, &ptpClock->pdelay_resp_receive_time, &ptpClock->pdelay_resp_send_time); subTime(&ptpClock->pdelaySM, &ptpClock->pdelay_req_receive_time, &ptpClock->pdelay_req_send_time); /* update 'one_way_delay' */ addTime(&ptpClock->peerMeanPathDelay, &ptpClock->pdelayMS, &ptpClock->pdelaySM); /* Substract correctionField */ subTime(&ptpClock->peerMeanPathDelay, &ptpClock->peerMeanPathDelay, correctionField); /* Compute one-way delay */ div2Time(&ptpClock->peerMeanPathDelay); } else { /* One step clock */ subTime(&ptpClock->peerMeanPathDelay, &ptpClock->pdelay_resp_receive_time, &ptpClock->pdelay_req_send_time); /* Substract correctionField */ subTime(&ptpClock->peerMeanPathDelay, &ptpClock->peerMeanPathDelay, correctionField); /* Compute one-way delay */ div2Time(&ptpClock->peerMeanPathDelay); } if (ptpClock->peerMeanPathDelay.seconds) { /* cannot filter with secs, clear filter */ owd_filt->s_exp = owd_filt->nsec_prev = 0; return; } /* avoid overflowing filter */ s = rtOpts->s; while (abs(owd_filt->y) >> (31 - s)) --s; /* crank down filter cutoff by increasing 's_exp' */ if (owd_filt->s_exp < 1) owd_filt->s_exp = 1; else if (owd_filt->s_exp < 1 << s) ++owd_filt->s_exp; else if (owd_filt->s_exp > 1 << s) owd_filt->s_exp = 1 << s; /* filter 'meanPathDelay' */ owd_filt->y = (owd_filt->s_exp - 1) * owd_filt->y / owd_filt->s_exp + (ptpClock->peerMeanPathDelay.nanoseconds / 2 + owd_filt->nsec_prev / 2) / owd_filt->s_exp; owd_filt->nsec_prev = ptpClock->peerMeanPathDelay.nanoseconds; ptpClock->peerMeanPathDelay.nanoseconds = owd_filt->y; DBGV("delay filter %d, %d\n", owd_filt->y, owd_filt->s_exp); } void updateOffset(TimeInternal * send_time, TimeInternal * recv_time, offset_from_master_filter * ofm_filt, RunTimeOpts * rtOpts, PtpClock * ptpClock, TimeInternal * correctionField) { DBGV("UTCOffset: %d | leap 59: %d | leap61: %d\n", ptpClock->currentUtcOffset,ptpClock->leap59,ptpClock->leap61); /* updates paused, leap second pending - do nothing */ if(ptpClock->leapSecondInProgress) return; DBGV("==> updateOffset\n"); //perform basic checks, using only local variables TimeInternal master_to_slave_delay; /* 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.\n"); /* 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); /* msgDump(ptpClock); */ return; } } // used for stats feedback ptpClock->char_last_msg='S'; ptpClock->last_packet_was_sync = TRUE; /* * The packet has passed basic checks, so we'll: * - update the global delayMS variable * - calculate a new filtered OFM */ subTime(&ptpClock->delayMS, recv_time, send_time); /* Used just for End to End mode. */ /* Take care about correctionField */ subTime(&ptpClock->delayMS, &ptpClock->delayMS, correctionField); /* update 'offsetFromMaster' */ if (ptpClock->delayMechanism == P2P) { subTime(&ptpClock->offsetFromMaster, &ptpClock->delayMS, &ptpClock->peerMeanPathDelay); } else if (ptpClock->delayMechanism == E2E) { /* (End to End mode) */ subTime(&ptpClock->offsetFromMaster, &ptpClock->delayMS, &ptpClock->meanPathDelay); } if (ptpClock->offsetFromMaster.seconds) { /* cannot filter with secs, clear filter */ ofm_filt->nsec_prev = 0; rtOpts->offset_first_updated = TRUE; return; } /* filter 'offsetFromMaster' */ ofm_filt->y = ptpClock->offsetFromMaster.nanoseconds / 2 + ofm_filt->nsec_prev / 2; ofm_filt->nsec_prev = ptpClock->offsetFromMaster.nanoseconds; ptpClock->offsetFromMaster.nanoseconds = ofm_filt->y; DBGV("offset filter %d\n", ofm_filt->y); /* * Offset must have been computed at least one time before * computing end to end delay */ rtOpts->offset_first_updated = TRUE; } void servo_perform_clock_step(RunTimeOpts * rtOpts, PtpClock * ptpClock) { 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"); 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) { if(ptpClock->warned_operator_fast_slewing == 0){ if ((adj >= ADJ_FREQ_MAX) || ((adj <= -ADJ_FREQ_MAX))){ ptpClock->warned_operator_fast_slewing = 1; NOTICE("Servo: Going to slew the clock with the maximum frequency adjustment\n"); } } } void warn_operator_slow_slewing(RunTimeOpts * rtOpts, PtpClock * ptpClock ) { if(ptpClock->warned_operator_slow_slewing == 0){ ptpClock->warned_operator_slow_slewing = 1; ptpClock->warned_operator_fast_slewing = 1; /* 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", ptpClock->offsetFromMaster.seconds, estimated ); } } /* * this is a wrapper around adjFreq to abstract extra operations */ void adjFreq_wrapper(RunTimeOpts * rtOpts, PtpClock * ptpClock, Integer32 adj) { if (rtOpts->noAdjust){ DBGV("adjFreq2: noAdjust on, returning\n"); return; } // call original adjtime DBG2(" adjFreq2: call adjfreq to %d us \n", adj / DBG_UNIT); adjFreq(adj); warn_operator_fast_slewing(rtOpts, ptpClock, adj); } void updateClock(RunTimeOpts * rtOpts, PtpClock * ptpClock) { Integer32 adj; //TimeInternal timeTmp; /* updates paused, leap second pending - do nothing */ if(ptpClock->leapSecondInProgress) return; DBGV("==> updateClock\n"); if (rtOpts->maxReset) { /* If maxReset is 0 then it's OFF */ if (ptpClock->offsetFromMaster.seconds && rtOpts->maxReset) { INFO("updateClock aborted, offset greater than 1" " second."); if (rtOpts->displayPackets) msgDump(ptpClock); goto display; } if (ptpClock->offsetFromMaster.nanoseconds > rtOpts->maxReset) { INFO("updateClock aborted, offset %d greater than " "administratively set maximum %d\n", ptpClock->offsetFromMaster.nanoseconds, rtOpts->maxReset); if (rtOpts->displayPackets) msgDump(ptpClock); goto display; } } if (ptpClock->offsetFromMaster.seconds) { /* if secs, reset clock or set freq adjustment to max */ /* if offset from master seconds is non-zero, then this is a "big jump: in time. Check Run Time options to see if we will reset the clock or set frequency adjustment to max to adjust the time */ /* * noAdjust = cannot do any change to clock * noResetClock = if can change the clock, can we also step it? */ if (!rtOpts->noAdjust) { /* */ if (!rtOpts->noResetClock) { servo_perform_clock_step(rtOpts, ptpClock); } 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; warn_operator_slow_slewing(rtOpts, ptpClock); adjFreq_wrapper(rtOpts, ptpClock, -adj); /* its not clear how the APPLE case works for large jumps */ #endif /* __APPLE__ */ } } } else { /* these variables contain the actual ai and ap to be used below */ Integer32 ap = rtOpts->ap; Integer32 ai = rtOpts->ai; /* the PI controller */ /* Offset from master is less than one second. Use the the PI controller * to adjust the time */ /* no negative or zero attenuation */ if (ap < 1) ap = 1; if (ai < 1) ai = 1; /* the accumulator for the I component */ // original PI servo ptpClock->observed_drift += ptpClock->offsetFromMaster.nanoseconds / ai; // ADJ_FREQ_MAX: 512 000 /* 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 + ptpClock->observed_drift; DBG(" Observed_drift with AI component: %d\n", ptpClock->observed_drift ); DBG(" After PI: Adj: %d Drift: %d 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); #else adjFreq_wrapper(rtOpts, ptpClock, -adj); #endif /* __APPLE__ */ } display: displayStats(rtOpts, ptpClock); DBGV("\n--Offset Correction-- \n"); DBGV("Raw offset from master: %10ds %11dns\n", ptpClock->delayMS.seconds, ptpClock->delayMS.nanoseconds); DBGV("\n--Offset and Delay filtered-- \n"); if (ptpClock->delayMechanism == P2P) { DBGV("one-way delay averaged (P2P): %10ds %11dns\n", ptpClock->peerMeanPathDelay.seconds, ptpClock->peerMeanPathDelay.nanoseconds); } else if (ptpClock->delayMechanism == E2E) { DBGV("one-way delay averaged (E2E): %10ds %11dns\n", ptpClock->meanPathDelay.seconds, ptpClock->meanPathDelay.nanoseconds); } DBGV("offset from master: %10ds %11dns\n", ptpClock->offsetFromMaster.seconds, ptpClock->offsetFromMaster.nanoseconds); DBGV("observed drift: %10d\n", ptpClock->observed_drift); } ptpd-2.2.2/src/dep/sys.c0000600000175000017500000004647111770426153014005 0ustar ernieernie/*- * Copyright (c) 2011-2012 George V. Neville-Neil, * Steven Kreuzer, * Martin Burnicki, * Jan Breuer, * Gael Mace, * Alexandre Van Kempen, * Inaqui Delgado, * Rick Ratzel, * National Instruments. * Copyright (c) 2009-2010 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. */ /** * @file sys.c * @date Tue Jul 20 16:19:46 2010 * * @brief Code to call kernel time routines and also display server statistics. * * */ #include "../ptpd.h" #if defined(linux) # include #elif defined( __FreeBSD__ ) # include #elif defined( __NetBSD__ ) # include #elif defined( __OpenBSD__ ) # include // force build error #endif /* only C99 has the round function built-in */ double round (double __x); /* returns a static char * for the representation of time, for debug purposes DO NOT call this twice in the same printf! */ char *dump_TimeInternal(const TimeInternal * p) { static char buf[100]; snprint_TimeInternal(buf, 100, p); return buf; } /* displays 2 timestamps and their strings in sequence, and the difference between then DO NOT call this twice in the same printf! */ char *dump_TimeInternal2(const char *st1, const TimeInternal * p1, const char *st2, const TimeInternal * p2) { static char buf[BUF_SIZE]; int n = 0; /* display Timestamps */ if (st1) { n += snprintf(buf + n, BUF_SIZE - n, "%s ", st1); } n += snprint_TimeInternal(buf + n, BUF_SIZE - n, p1); n += snprintf(buf + n, BUF_SIZE - n, " "); if (st2) { n += snprintf(buf + n, BUF_SIZE - n, "%s ", st2); } n += snprint_TimeInternal(buf + n, BUF_SIZE - n, p2); n += snprintf(buf + n, BUF_SIZE - n, " "); /* display difference */ TimeInternal r; subTime(&r, p1, p2); n += snprintf(buf + n, BUF_SIZE - n, " (diff: "); n += snprint_TimeInternal(buf + n, BUF_SIZE - n, &r); n += snprintf(buf + n, BUF_SIZE - n, ") "); return buf; } int snprint_TimeInternal(char *s, int max_len, const TimeInternal * p) { int len = 0; /* 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)? '-':' '); len += snprintf(&s[len], max_len - len, "%d.%09d", abs(p->seconds), abs(p->nanoseconds)); return len; } /* debug aid: convert a time variable into a static char */ char *time2st(const TimeInternal * p) { static char buf[1000]; snprint_TimeInternal(buf, sizeof(buf), p); return buf; } void DBG_time(const char *name, const TimeInternal p) { DBG(" %s: %s\n", name, time2st(&p)); } char * translatePortState(PtpClock *ptpClock) { char *s; switch(ptpClock->portState) { case PTP_INITIALIZING: s = "init"; break; case PTP_FAULTY: s = "flt"; break; case PTP_LISTENING: /* seperate init-reset from real resets */ if(ptpClock->reset_count == 1){ s = "lstn_init"; } else { s = "lstn_reset"; } break; case PTP_PASSIVE: s = "pass"; break; case PTP_UNCALIBRATED: s = "uncl"; break; case PTP_SLAVE: s = "slv"; break; case PTP_PRE_MASTER: s = "pmst"; break; case PTP_MASTER: s = "mst"; break; case PTP_DISABLED: s = "dsbl"; break; default: s = "?"; break; } return s; } int snprint_ClockIdentity(char *s, int max_len, const ClockIdentity id) { int len = 0; int i; for (i = 0; ;) { len += snprintf(&s[len], max_len - len, "%02x", (unsigned char) id[i]); if (++i >= CLOCK_IDENTITY_LENGTH) break; } return len; } /* show the mac address in an easy way */ int snprint_ClockIdentity_mac(char *s, int max_len, const ClockIdentity id) { int len = 0; int i; 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 (++i >= CLOCK_IDENTITY_LENGTH) break; /* print a separator after each byte except the last one */ len += snprintf(&s[len], max_len - len, "%s", ":"); } else { i++; } } return len; } /* * wrapper that caches the latest value of ether_ntohost * this function will NOT check the last accces time of /etc/ethers, * so it only have different output on a failover or at restart * */ int ether_ntohost_cache(char *hostname, struct ether_addr *addr) { static int valid = 0; static struct ether_addr prev_addr; static char buf[BUF_SIZE]; #if defined(linux) || defined(__NetBSD__) if (memcmp(addr->ether_addr_octet, &prev_addr, sizeof(struct ether_addr )) != 0) { valid = 0; } #else // e.g. defined(__FreeBSD__) if (memcmp(addr->octet, &prev_addr, sizeof(struct ether_addr )) != 0) { valid = 0; } #endif if (!valid) { if(ether_ntohost(buf, addr)){ sprintf(buf, "%s", "unknown"); } /* clean possible commas from the string */ while (strchr(buf, ',') != NULL) { *(strchr(buf, ',')) = '_'; } prev_addr = *addr; } valid = 1; strcpy(hostname, buf); return 0; } /* Show the hostname configured in /etc/ethers */ int snprint_ClockIdentity_ntohost(char *s, int max_len, const ClockIdentity id) { int len = 0; int i,j; char buf[100]; struct ether_addr e; /* extract mac address */ 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__) e.ether_addr_octet[j] = (uint8_t) id[i]; #else // e.g. defined(__FreeBSD__) e.octet[j] = (uint8_t) id[i]; #endif j++; } } /* convert and print hostname */ ether_ntohost_cache(buf, &e); len += snprintf(&s[len], max_len - len, "(%s)", buf); return len; } int snprint_PortIdentity(char *s, int max_len, const PortIdentity *id) { int len = 0; #ifdef PRINT_MAC_ADDRESSES len += snprint_ClockIdentity_mac(&s[len], max_len - len, id->clockIdentity); #else len += snprint_ClockIdentity(&s[len], max_len - len, id->clockIdentity); #endif len += snprint_ClockIdentity_ntohost(&s[len], max_len - len, id->clockIdentity); len += snprintf(&s[len], max_len - len, "/%02x", (unsigned) id->portNumber); return len; } /* * Prints a message, randing from critical to debug. * 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 message(int priority, const char * format, ...) { extern RunTimeOpts rtOpts; va_list ap; va_start(ap, format); #ifdef RUNTIME_DEBUG if ((priority >= LOG_DEBUG) && (priority > rtOpts.debug_level)) { return; } #endif if (rtOpts.useSysLog) { static Boolean logOpened; #ifdef RUNTIME_DEBUG /* * Syslog only has 8 message levels (3 bits) * important: messages will only appear if "*.debug /var/log/debug" is on /etc/rsyslog.conf */ if(priority > LOG_DEBUG){ priority = LOG_DEBUG; } #endif if (!logOpened) { openlog(PTPD_PROGNAME, 0, LOG_DAEMON); logOpened = TRUE; } vsyslog(priority, format, ap); /* Also warn operator during startup only */ if (rtOpts.syslog_startup_messages_also_to_stdout && (priority <= LOG_WARNING) ){ va_start(ap, format); vfprintf(stderr, format, ap); } } else { char time_str[MAXTIMESTR]; struct timeval now; extern char *translatePortState(PtpClock *ptpClock); extern PtpClock *G_ptpClock; fprintf(stderr, " (ptpd %-9s ", priority == LOG_EMERG ? "emergency)" : priority == LOG_ALERT ? "alert)" : priority == LOG_CRIT ? "critical)" : priority == LOG_ERR ? "error)" : priority == LOG_WARNING ? "warning)" : priority == LOG_NOTICE ? "notice)" : priority == LOG_INFO ? "info)" : priority == LOG_DEBUG ? "debug1)" : priority == LOG_DEBUG2 ? "debug2)" : priority == LOG_DEBUGV ? "debug3)" : "unk)"); /* * select debug tagged with timestamps. This will slow down PTP itself if you send a lot of messages! * it also can cause problems in nested debug statements (which are solved by turning the signal * handling synchronous, and not calling this function inside assycnhonous signal processing) */ gettimeofday(&now, 0); strftime(time_str, MAXTIMESTR, "%X", localtime(&now.tv_sec)); fprintf(stderr, "%s.%06d ", time_str, (int)now.tv_usec ); fprintf(stderr, " (%s) ", G_ptpClock ? translatePortState(G_ptpClock) : "___"); vfprintf(stderr, format, ap); } va_end(ap); } void increaseMaxDelayThreshold() { extern RunTimeOpts rtOpts; NOTIFY("Increasing maxDelay threshold from %i to %i\n", rtOpts.maxDelay, rtOpts.maxDelay << 1); rtOpts.maxDelay <<= 1; } void decreaseMaxDelayThreshold() { extern RunTimeOpts rtOpts; if ((rtOpts.maxDelay >> 1) < rtOpts.origMaxDelay) return; NOTIFY("Decreasing maxDelay threshold from %i to %i\n", rtOpts.maxDelay, rtOpts.maxDelay >> 1); rtOpts.maxDelay >>= 1; } void displayStats(RunTimeOpts * rtOpts, PtpClock * ptpClock) { static int start = 1; static char sbuf[SCREEN_BUFSZ]; int len = 0; TimeInternal now; time_t time_s; static TimeInternal prev_now; char time_str[MAXTIMESTR]; if (!rtOpts->displayStats) { return; } if (start) { start = 0; printf("# Timestamp, State, Clock ID, One Way Delay, " "Offset From Master, Slave to Master, " "Master to Slave, Drift, Discarded Packet Count, Last packet Received\n"); fflush(stdout); } memset(sbuf, ' ', sizeof(sbuf)); getTime(&now); /* * print one log entry per X seconds, to reduce disk usage. * This only happens to SLAVE SYNC statistics lines, which are the bulk of the log. * All other lines are printed, including delayreqs. */ if ((ptpClock->portState == PTP_SLAVE) && (rtOpts->log_seconds_between_message)) { if(ptpClock->last_packet_was_sync){ ptpClock->last_packet_was_sync = FALSE; if((now.seconds - prev_now.seconds) < rtOpts->log_seconds_between_message){ //leave early and do not print the log message to save disk space DBGV("Skipped printing of Sync message because of option -V\n"); return; } prev_now = now; } } ptpClock->last_packet_was_sync = FALSE; time_s = now.seconds; strftime(time_str, MAXTIMESTR, "%Y-%m-%d %X", localtime(&time_s)); len += snprintf(sbuf + len, sizeof(sbuf) - len, "%s.%06d, %s, ", time_str, (int)now.nanoseconds/1000, /* Timestamp */ translatePortState(ptpClock)); /* State */ if (ptpClock->portState == PTP_SLAVE) { len += snprint_PortIdentity(sbuf + len, sizeof(sbuf) - len, &ptpClock->parentPortIdentity); /* Clock ID */ /* * 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); } len += snprintf(sbuf + len, sizeof(sbuf) - len, ", "); if(rtOpts->delayMechanism == E2E) { len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len, &ptpClock->meanPathDelay); } else { len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len, &ptpClock->peerMeanPathDelay); } len += snprintf(sbuf + len, sizeof(sbuf) - len, ", "); len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len, &ptpClock->offsetFromMaster); /* print MS and SM with sign */ len += snprintf(sbuf + len, sizeof(sbuf) - len, ", "); len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len, &(ptpClock->delaySM)); len += snprintf(sbuf + len, sizeof(sbuf) - len, ", "); len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len, &(ptpClock->delayMS)); len += sprintf(sbuf + len, ", %d, %i, %c", ptpClock->observed_drift, ptpClock->discardedPacketCount, ptpClock->char_last_msg); } else { if ((ptpClock->portState == PTP_MASTER) || (ptpClock->portState == PTP_PASSIVE)) { 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); } } /* add final \n in normal status lines */ len += snprintf(sbuf + len, sizeof(sbuf) - len, "\n"); #if 0 /* NOTE: Do we want this? */ if (rtOpts->nonDaemon) { /* in -C mode, adding an extra \n makes stats more clear intermixed with debug comments */ len += snprintf(sbuf + len, sizeof(sbuf) - len, "\n"); } #endif write(1, sbuf, len); } void recordSync(RunTimeOpts * rtOpts, UInteger16 sequenceId, TimeInternal * time) { if (rtOpts->recordFP) fprintf(rtOpts->recordFP, "%d %llu\n", sequenceId, ((time->seconds * 1000000000ULL) + time->nanoseconds) ); } Boolean nanoSleep(TimeInternal * t) { struct timespec ts, tr; ts.tv_sec = t->seconds; ts.tv_nsec = t->nanoseconds; if (nanosleep(&ts, &tr) < 0) { t->seconds = tr.tv_sec; t->nanoseconds = tr.tv_nsec; return FALSE; } return TRUE; } void getTime(TimeInternal * time) { #if defined(linux) || defined(__APPLE__) struct timeval tv; gettimeofday(&tv, 0); time->seconds = tv.tv_sec; time->nanoseconds = tv.tv_usec * 1000; #else struct timespec tp; if (clock_gettime(CLOCK_REALTIME, &tp) < 0) { PERROR("clock_gettime() failed, exiting."); exit(0); } time->seconds = tp.tv_sec; 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); } /* returns a double beween 0.0 and 1.0 */ double getRand(void) { return ((rand() * 1.0) / RAND_MAX); } /* * 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) { struct timex t; memset(&t, 0, sizeof(t)); if (adj > ADJ_FREQ_MAX){ adj = ADJ_FREQ_MAX; } else if (adj < -ADJ_FREQ_MAX){ adj = -ADJ_FREQ_MAX; } t.modes = MOD_FREQUENCY; t.freq = adj * ((1 << 16) / 1000); /* 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; DBG2(" adj is %d; t freq is %d (float: %f Integer32: %d)\n", adj, t.freq, f, t1); return !adjtimex(&t); } void setTimexFlags(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 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); } return( tmx.status ); } Boolean checkTimexFlags(int flags) { int tflags = getTimexFlags(); if (tflags == -1) return FALSE; return ((tflags & flags) == flags); } /* * TODO: track NTP API changes - NTP API version check * is required - the method of setting the TAI offset * may change with next API versions */ #if defined(MOD_TAI) && NTP_API == 4 void setKernelUtcOffset(int utc_offset) { struct timex tmx; int ret; memset(&tmx, 0, sizeof(tmx)); tmx.modes = MOD_TAI; tmx.constant = utc_offset; DBG2("Kernel NTP API supports TAI offset. " "Setting TAI offset to %d", utc_offset); ret = adjtimex(&tmx); if (ret < 0) { PERROR("Could not set kernel TAI offset: %s", strerror(errno)); } } #endif /* MOD_TAI */ #else void adjTime(Integer32 nanoseconds) { struct timeval t; t.tv_sec = 0; t.tv_usec = nanoseconds / 1000; if (adjtime(&t, NULL) < 0) PERROR("failed to ajdtime"); } #endif /* __APPLE__ */ #if 0 && defined (linux) /* NOTE: This is actually not used */ long get_current_tickrate(void) { struct timex t; t.modes = 0; adjtimex(&t); DBG2(" (Current tick rate is %d)\n", t.tick ); return (t.tick); } #endif /* defined(linux) */ ptpd-2.2.2/src/dep/net.c0000600000175000017500000007210511770426153013746 0ustar ernieernie/*- * Copyright (c) 2011-2012 George V. Neville-Neil, * Steven Kreuzer, * Martin Burnicki, * Jan Breuer, * Gael Mace, * Alexandre Van Kempen, * Inaqui Delgado, * Rick Ratzel, * National Instruments. * Copyright (c) 2009-2010 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. */ /** * @file net.c * @date Tue Jul 20 16:17:49 2010 * * @brief Functions to interact with the network sockets and NIC driver. * * */ #include "../ptpd.h" /* choose kernel-level nanoseconds or microseconds resolution on the client-side */ #if !defined(SO_TIMESTAMPNS) && !defined(SO_TIMESTAMP) && !defined(SO_BINTIME) #error kernel-level timestamps not detected #endif /** * shutdown the IPv4 multicast for specific address * * @param netPath * @param multicastAddr * * @return TRUE if successful */ static Boolean netShutdownMulticastIPv4(NetPath * netPath, Integer32 multicastAddr) { struct ip_mreq imr; /* Close General Multicast */ imr.imr_multiaddr.s_addr = multicastAddr; imr.imr_interface.s_addr = netPath->interfaceAddr.s_addr; setsockopt(netPath->eventSock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(struct ip_mreq)); setsockopt(netPath->generalSock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(struct ip_mreq)); return TRUE; } /** * shutdown the multicast (both General and Peer) * * @param netPath * * @return TRUE if successful */ static Boolean netShutdownMulticast(NetPath * netPath) { /* Close General Multicast */ netShutdownMulticastIPv4(netPath, netPath->multicastAddr); netPath->multicastAddr = 0; /* Close Peer Multicast */ netShutdownMulticastIPv4(netPath, netPath->peerMulticastAddr); netPath->peerMulticastAddr = 0; return TRUE; } /* shut down the UDP stuff */ Boolean netShutdown(NetPath * netPath) { netShutdownMulticast(netPath); netPath->unicastAddr = 0; /* Close sockets */ if (netPath->eventSock > 0) close(netPath->eventSock); netPath->eventSock = -1; if (netPath->generalSock > 0) close(netPath->generalSock); netPath->generalSock = -1; return TRUE; } Boolean chooseMcastGroup(RunTimeOpts * rtOpts, struct in_addr *netAddr) { char *addrStr; #ifdef PTP_EXPERIMENTAL switch(rtOpts->mcast_group_Number){ case 0: addrStr = DEFAULT_PTP_DOMAIN_ADDRESS; break; case 1: addrStr = ALTERNATE_PTP_DOMAIN1_ADDRESS; break; case 2: addrStr = ALTERNATE_PTP_DOMAIN2_ADDRESS; break; case 3: addrStr = ALTERNATE_PTP_DOMAIN3_ADDRESS; break; default: ERROR("Unk group %d\n", rtOpts->mcast_group_Number); exit(3); break; } #else addrStr = DEFAULT_PTP_DOMAIN_ADDRESS; #endif if (!inet_aton(addrStr, netAddr)) { ERROR("failed to encode multicast address: %s\n", addrStr); return FALSE; } return TRUE; } /*Test if network layer is OK for PTP*/ UInteger8 lookupCommunicationTechnology(UInteger8 communicationTechnology) { #if defined(linux) switch (communicationTechnology) { case ARPHRD_ETHER: case ARPHRD_EETHER: case ARPHRD_IEEE802: return PTP_ETHER; default: break; } #endif /* defined(linux) */ return PTP_DEFAULT; } /* Find the local network interface */ UInteger32 findIface(Octet * ifaceName, UInteger8 * communicationTechnology, Octet * uuid, NetPath * netPath) { #if defined(linux) /* 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; memset(data.ifc_buf, 0, data.ifc_len); 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); 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"); /* 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 (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; } } } 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; #else /* usually *BSD */ 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 (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 (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); return ((struct sockaddr_in *)ifv4->ifa_addr)->sin_addr.s_addr; #endif } /** * Init the multcast for specific IPv4 address * * @param netPath * @param multicastAddr * * @return TRUE if successful */ static Boolean netInitMulticastIPv4(NetPath * netPath, Integer32 multicastAddr) { struct ip_mreq imr; /* multicast send only on specified interface */ imr.imr_multiaddr.s_addr = multicastAddr; imr.imr_interface.s_addr = netPath->interfaceAddr.s_addr; if (setsockopt(netPath->eventSock, IPPROTO_IP, IP_MULTICAST_IF, &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0 || setsockopt(netPath->generalSock, IPPROTO_IP, IP_MULTICAST_IF, &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0) { PERROR("failed to enable multi-cast on the interface"); return FALSE; } /* join multicast group (for receiving) on specified interface */ if (setsockopt(netPath->eventSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(struct ip_mreq)) < 0 || setsockopt(netPath->generalSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(struct ip_mreq)) < 0) { PERROR("failed to join the multi-cast group"); return FALSE; } return TRUE; } /** * Init the multcast (both General and Peer) * * @param netPath * @param rtOpts * * @return TRUE if successful */ Boolean netInitMulticast(NetPath * netPath, RunTimeOpts * rtOpts) { struct in_addr netAddr; char addrStr[NET_ADDRESS_LENGTH]; /* Init General multicast IP address */ if(!chooseMcastGroup(rtOpts, &netAddr)){ return FALSE; } netPath->multicastAddr = netAddr.s_addr; if(!netInitMulticastIPv4(netPath, netPath->multicastAddr)) { return FALSE; } /* End of General multicast Ip address init */ /* Init Peer multicast IP address */ memcpy(addrStr, PEER_PTP_DOMAIN_ADDRESS, NET_ADDRESS_LENGTH); if (!inet_aton(addrStr, &netAddr)) { ERROR("failed to encode multi-cast address: %s\n", addrStr); return FALSE; } netPath->peerMulticastAddr = netAddr.s_addr; if(!netInitMulticastIPv4(netPath, netPath->peerMulticastAddr)) { return FALSE; } /* End of Peer multicast Ip address init */ return TRUE; } /** * Initialize timestamping of packets * * @param netPath * * @return TRUE if successful */ Boolean netInitTimestamping(NetPath * netPath) { int val = 1; Boolean result = TRUE; #if defined(SO_TIMESTAMPNS) /* Linux, Apple */ DBG("netInitTimestamping: trying to use SO_TIMESTAMPNS\n"); if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_TIMESTAMPNS, &val, sizeof(int)) < 0 || setsockopt(netPath->generalSock, SOL_SOCKET, SO_TIMESTAMPNS, &val, sizeof(int)) < 0) { PERROR("netInitTimestamping: failed to enable SO_TIMESTAMPNS"); result = FALSE; } #elif defined(SO_BINTIME) /* FreeBSD */ DBG("netInitTimestamping: trying to use SO_BINTIME\n"); if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_BINTIME, &val, sizeof(int)) < 0 || setsockopt(netPath->generalSock, SOL_SOCKET, SO_BINTIME, &val, sizeof(int)) < 0) { PERROR("netInitTimestamping: failed to enable SO_BINTIME"); result = FALSE; } #else result = FALSE; #endif /* fallback method */ #if defined(SO_TIMESTAMP) /* Linux, Apple, FreeBSD */ if (!result) { DBG("netInitTimestamping: trying to use SO_TIMESTAMP\n"); if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_TIMESTAMP, &val, sizeof(int)) < 0 || setsockopt(netPath->generalSock, SOL_SOCKET, SO_TIMESTAMP, &val, sizeof(int)) < 0) { PERROR("netInitTimestamping: failed to enable SO_TIMESTAMP"); result = FALSE; } result = TRUE; } #endif return result; } /** * start all of the UDP stuff * must specify 'subdomainName', and optionally 'ifaceName', * if not then pass ifaceName == "" * on socket options, see the 'socket(7)' and 'ip' man pages * * @param netPath * @param rtOpts * @param ptpClock * * @return TRUE if successful */ Boolean netInit(NetPath * netPath, RunTimeOpts * rtOpts, PtpClock * ptpClock) { int temp; struct in_addr interfaceAddr, netAddr; struct sockaddr_in addr; DBG("netInit\n"); /* 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; /* 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; } #ifdef USE_BINDTODEVICE #ifdef linux /* * The following code makes sure that the data is only received on the specified interface. * Without this option, it's possible to receive PTP from another interface, and confuse the protocol. * Calling bind() with the IP address of the device instead of INADDR_ANY does not work. * * More info: * http://developerweb.net/viewtopic.php?id=6471 * http://stackoverflow.com/questions/1207746/problems-with-so-bindtodevice-linux-socket-option */ #ifdef PTP_EXPERIMENTAL if ( !rtOpts->do_hybrid_mode ) #endif if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_BINDTODEVICE, rtOpts->ifaceName, strlen(rtOpts->ifaceName)) < 0 || setsockopt(netPath->generalSock, SOL_SOCKET, SO_BINDTODEVICE, rtOpts->ifaceName, strlen(rtOpts->ifaceName)) < 0){ PERROR("failed to call SO_BINDTODEVICE on the interface"); return FALSE; } #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; } /* init UDP Multicast on both Default and Pear addresses */ if (!netInitMulticast(netPath, rtOpts)) { return FALSE; } /* 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; } /* enable loopback */ temp = 1; 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; } /* make timestamps available through recvmsg() */ if (!netInitTimestamping(netPath)) { ERROR("failed to enable receive time stamps"); return FALSE; } return TRUE; } /*Check if data have been received*/ int netSelect(TimeInternal * timeout, NetPath * netPath) { int ret, nfds; fd_set readfds; struct timeval tv, *tv_ptr; if (timeout < 0) return FALSE; 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 (netPath->eventSock > netPath->generalSock) nfds = netPath->eventSock; else nfds = netPath->generalSock; ret = select(nfds + 1, &readfds, 0, 0, tv_ptr) > 0; if (ret < 0) { if (errno == EAGAIN || errno == EINTR) return 0; } return ret; } /** * store received data from network to "buf" , get and store the * SO_TIMESTAMP value in "time" for an event message * * @note Should this function be merged with netRecvGeneral(), below? * Jan Breuer: I think that netRecvGeneral should be simplified. Timestamp returned by this * function is never used. According to this, netInitTimestamping can be also simplified * to initialize timestamping only on eventSock. * * @param buf * @param time * @param netPath * * @return */ ssize_t netRecvEvent(Octet * buf, TimeInternal * time, NetPath * netPath) { ssize_t ret; struct msghdr msg; struct iovec vec[1]; struct sockaddr_in from_addr; union { struct cmsghdr cm; char control[CMSG_SPACE(sizeof(struct timeval))]; } cmsg_un; struct cmsghdr *cmsg; #if defined(SO_TIMESTAMPNS) struct timespec * ts; #elif defined(SO_BINTIME) struct bintime * bt; struct timespec ts; #endif #if defined(SO_TIMESTAMP) struct timeval * tv; #endif Boolean timestampValid = FALSE; vec[0].iov_base = buf; vec[0].iov_len = PACKET_SIZE; memset(&msg, 0, sizeof(msg)); memset(&from_addr, 0, sizeof(from_addr)); memset(buf, 0, PACKET_SIZE); memset(&cmsg_un, 0, sizeof(cmsg_un)); msg.msg_name = (caddr_t)&from_addr; msg.msg_namelen = sizeof(from_addr); msg.msg_iov = vec; msg.msg_iovlen = 1; msg.msg_control = cmsg_un.control; msg.msg_controllen = sizeof(cmsg_un.control); msg.msg_flags = 0; ret = recvmsg(netPath->eventSock, &msg, MSG_DONTWAIT); if (ret <= 0) { if (errno == EAGAIN || errno == EINTR) return 0; return ret; } if (msg.msg_flags & MSG_TRUNC) { ERROR("received truncated message\n"); return 0; } /* get time stamp of packet */ if (!time) { ERROR("null receive time stamp argument\n"); return 0; } if (msg.msg_flags & MSG_CTRUNC) { ERROR("received truncated ancillary data\n"); return 0; } #ifdef PTP_EXPERIMENTAL netPath->lastRecvAddr = from_addr.sin_addr.s_addr; #endif if (msg.msg_controllen <= 0) { ERROR("received short ancillary data (%ld/%ld)\n", (long)msg.msg_controllen, (long)sizeof(cmsg_un.control)); return 0; } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET) { #if defined(SO_TIMESTAMPNS) if(cmsg->cmsg_type == SCM_TIMESTAMPNS) { ts = (struct timespec *)CMSG_DATA(cmsg); time->seconds = ts->tv_sec; time->nanoseconds = ts->tv_nsec; timestampValid = TRUE; DBGV("kernel NANO recv time stamp %us %dns\n", time->seconds, time->nanoseconds); break; } #elif defined(SO_BINTIME) if(cmsg->cmsg_type == SCM_BINTIME) { bt = (struct bintime *)CMSG_DATA(cmsg); bintime2timespec(bt, &ts); time->seconds = ts.tv_sec; time->nanoseconds = ts.tv_nsec; timestampValid = TRUE; DBGV("kernel NANO recv time stamp %us %dns\n", time->seconds, time->nanoseconds); break; } #endif #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; timestampValid = TRUE; DBGV("kernel MICRO recv time stamp %us %dns\n", time->seconds, time->nanoseconds); } #endif } } if (!timestampValid) { /* * do not try to get by with recording the time here, better * to fail because the time recorded could be well after the * message receive, which would put a big spike in the * offset signal sent to the clock servo */ DBG("netRecvEvent: no receive time stamp\n"); return 0; } return ret; } /** * * store received data from network to "buf" get and store the * SO_TIMESTAMP value in "time" for a general message * * @param buf * @param time * @param netPath * * @return */ ssize_t netRecvGeneral(Octet * buf, TimeInternal * time, NetPath * netPath) { ssize_t ret; struct msghdr msg; struct iovec vec[1]; struct sockaddr_in from_addr; union { struct cmsghdr cm; char control[CMSG_SPACE(sizeof(struct timeval))]; } cmsg_un; struct cmsghdr *cmsg; #if defined(SO_TIMESTAMPNS) struct timespec * ts; #elif defined(SO_BINTIME) struct bintime * bt; struct timespec ts; #endif #if defined(SO_TIMESTAMP) struct timeval * tv; #endif Boolean timestampValid = FALSE; vec[0].iov_base = buf; vec[0].iov_len = PACKET_SIZE; memset(&msg, 0, sizeof(msg)); memset(&from_addr, 0, sizeof(from_addr)); memset(buf, 0, PACKET_SIZE); memset(&cmsg_un, 0, sizeof(cmsg_un)); msg.msg_name = (caddr_t)&from_addr; msg.msg_namelen = sizeof(from_addr); msg.msg_iov = vec; msg.msg_iovlen = 1; msg.msg_control = cmsg_un.control; msg.msg_controllen = sizeof(cmsg_un.control); msg.msg_flags = 0; ret = recvmsg(netPath->generalSock, &msg, MSG_DONTWAIT); if (ret <= 0) { if (errno == EAGAIN || errno == EINTR) return 0; return ret; } if (msg.msg_flags & MSG_TRUNC) { ERROR("received truncated message\n"); return 0; } /* get time stamp of packet */ if (!time) { ERROR("null receive time stamp argument\n"); return 0; } if (msg.msg_flags & MSG_CTRUNC) { ERROR("received truncated ancillary data\n"); return 0; } #ifdef PTP_EXPERIMENTAL netPath->lastRecvAddr = from_addr.sin_addr.s_addr; #endif if (msg.msg_controllen <= 0) { ERROR("received short ancillary data (%ld/%ld)\n", (long)msg.msg_controllen, (long)sizeof(cmsg_un.control)); return 0; } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET) { #if defined(SO_TIMESTAMPNS) if(cmsg->cmsg_type == SCM_TIMESTAMPNS) { ts = (struct timespec *)CMSG_DATA(cmsg); time->seconds = ts->tv_sec; time->nanoseconds = ts->tv_nsec; timestampValid = TRUE; DBGV("kernel NANO recv time stamp %us %dns\n", time->seconds, time->nanoseconds); break; } #elif defined(SO_BINTIME) if(cmsg->cmsg_type == SCM_BINTIME) { bt = (struct bintime *)CMSG_DATA(cmsg); bintime2timespec(bt, &ts); time->seconds = ts.tv_sec; time->nanoseconds = ts.tv_nsec; timestampValid = TRUE; DBGV("kernel NANO recv time stamp %us %dns\n", time->seconds, time->nanoseconds); break; } #endif #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; timestampValid = TRUE; DBGV("kernel MICRO recv time stamp %us %dns\n", time->seconds, time->nanoseconds); } #endif } } if (!timestampValid) { /* * do not try to get by with recording the time here, better * to fail because the time recorded could be well after the * message receive, which would put a big spike in the * offset signal sent to the clock servo */ DBG("netRecvGeneral: no receive time stamp\n"); return 0; } return ret; } // // alt_dst: alternative destination. // if filled, send to this unicast dest; // if zero, do the normal operation (send to unicast with -u, or send to the multcast group) // /// /// TODO: merge these 2 functions into one /// ssize_t netSendEvent(Octet * buf, UInteger16 length, NetPath * netPath, Integer32 alt_dst) { ssize_t ret; struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(PTP_EVENT_PORT); if (netPath->unicastAddr || alt_dst ) { if (netPath->unicastAddr) { addr.sin_addr.s_addr = netPath->unicastAddr; } else { 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"); /* * 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"); } else { addr.sin_addr.s_addr = netPath->multicastAddr; 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; } ssize_t netSendGeneral(Octet * buf, UInteger16 length, NetPath * netPath, Integer32 alt_dst) { ssize_t 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; } 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"); } else { addr.sin_addr.s_addr = netPath->multicastAddr; 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 netSendPeerGeneral(Octet * buf, UInteger16 length, NetPath * netPath) { ssize_t ret; struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(PTP_GENERAL_PORT); 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"); } 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"); } return ret; } ssize_t netSendPeerEvent(Octet * buf, UInteger16 length, NetPath * netPath) { ssize_t ret; struct sockaddr_in addr; 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"); } else { addr.sin_addr.s_addr = netPath->peerMulticastAddr; 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; } /* * refresh IGMP on a timeout */ /* * @return TRUE if successful */ Boolean netRefreshIGMP(NetPath * netPath, RunTimeOpts * rtOpts, PtpClock * ptpClock) { DBG("netRefreshIGMP\n"); netShutdownMulticast(netPath); /* suspend process 100 milliseconds, to make sure the kernel sends the IGMP_leave properly */ usleep(100*1000); if (!netInitMulticast(netPath, rtOpts)) { return FALSE; } INFO("refreshed IGMP multicast memberships\n"); return TRUE; } ptpd-2.2.2/src/ptpd.c0000600000175000017500000001277211770426154013364 0ustar ernieernie/*- * Copyright (c) 2011-2012 George V. Neville-Neil, * Steven Kreuzer, * Martin Burnicki, * Jan Breuer, * Gael Mace, * Alexandre Van Kempen, * Inaqui Delgado, * Rick Ratzel, * National Instruments. * Copyright (c) 2009-2010 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. */ /** * @file ptpd.c * @date Wed Jun 23 10:13:38 2010 * * @brief The main() function for the PTP daemon * * This file contains very little code, as should be obvious, * and only serves to tie together the rest of the daemon. * All of the default options are set here, but command line * arguments are processed in the ptpdStartup() routine called * below. */ #include "ptpd.h" RunTimeOpts rtOpts; /* statically allocated run-time * configuration data */ /* * Global variable with the main PTP port. This is used to show the current state in DBG()/message() * without having to pass the pointer everytime. * * if ptpd is extended to handle multiple ports (eg, to instantiate a Boundary Clock), * then DBG()/message() needs a per-port pointer argument */ PtpClock *G_ptpClock = NULL; int main(int argc, char **argv) { 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; #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; /* 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.maxDelayAutoTune = FALSE; rtOpts.discardedPacketThreshold = 60; rtOpts.logFd = -1; rtOpts.recordFP = NULL; rtOpts.do_log_to_file = FALSE; rtOpts.do_record_quality_file = FALSE; rtOpts.nonDaemon = FALSE; /* * defaults for new options */ rtOpts.slaveOnly = TRUE; rtOpts.ignore_delayreq_interval_master = FALSE; rtOpts.do_IGMP_refresh = TRUE; rtOpts.useSysLog = TRUE; rtOpts.syslog_startup_messages_also_to_stdout = TRUE; /* used to print inital messages both to syslog and screen */ rtOpts.announceReceiptTimeout = DEFAULT_ANNOUNCE_RECEIPT_TIMEOUT; #ifdef RUNTIME_DEBUG rtOpts.debug_level = LOG_INFO; /* by default debug messages as disabled, but INFO messages and below are printed */ #endif rtOpts.ttl = 1; rtOpts.delayMechanism = DEFAULT_DELAY_MECHANISM; rtOpts.noResetClock = DEFAULT_NO_RESET_CLOCK; rtOpts.log_seconds_between_message = 0; 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; /* global variable for message(), please see comment on top of this file */ G_ptpClock = ptpClock; /* do the protocol engine */ protocol(&rtOpts, ptpClock); /* forever loop.. */ ptpdShutdown(ptpClock); NOTIFY("self shutdown, probably due to an error\n"); return 1; } ptpd-2.2.2/src/check_startup.sh0000600000175000017500000000241011770426154015430 0ustar ernieernie#!/bin/bash ## ## use this quick and dirty shell script to manage the flags in dep/startup.c ## ## outputs are: ## - which flags are duplicated ## - which flags are still available to use (set is: a..z, A..Z, ?) ## - the getopt string to put in the beginning of ptpdStartup() ## ## limitations: only the internal help text is parsed. Explanations are assumed to start at position 19. ## TMP_BASE="/tmp/tmp_ptp_startup" FILE="dep/startup.c" TMP1="${TMP_BASE}.1" TMP2="${TMP_BASE}.2" TMP3="${TMP_BASE}.3" L=`cat "$FILE" | awk '/GETOPT_START_OF_OPTIONS/{A=1} /GETOPT_END_OF_OPTIONS/{A=0;} {if(A){print}}' | cut -b1-23 ` A=`echo "$L" | awk -F"\"" '{print $2}' | awk '{print $1}'| awk '/^-/' | awk '{print $1}' | tr -d "-" | sort` B=`echo "?" {a..z} {A..Z} | sed 's/ /\n/g' | sort ` echo "$A" > "$TMP1" echo "$A" | sort -u > "$TMP2" echo "$B" > "$TMP3" C=` echo "$L" | awk -F"\"" '{print $2 $3}' | awk '/^[ ]*-/' | cut -b1-18 ` CC=`echo "$C" | awk 'BEGIN{print ""}; {print $1; if(NF>1){print ":"}}' | paste -s -d "" | sed 's/\-//g'` echo "Possible Duplicated: `cat "$TMP1" | uniq -d | paste -s `" echo "Available flags: `diff -d "$TMP2" "$TMP3" | grep ">" | awk '{print $2}'| paste -s`" echo "GetOpt String: const char *getopt_string = \"$CC\";" exit 0 ptpd-2.2.2/src/Makefile0000600000175000017500000000513011770426154013677 0ustar ernieernie# Makefile for ptpd2 # # Compile time defines: # -DRUNTIME_DEBUG # if defined: all debug messages are included, and are either selected at startup (-B) # and runtime (SIGUSR2) (implies -DPTPD_DBGV) using syslog, # add "*.debug /var/log/debug" to /etc/rsyslog.conf to see debug messages # if undefined: individual defines select which messages will be included and presented (old behaviour) # # -DPTPD_DBG basic debug messages # -DPTPD_DBG2 adds basic protocol messages, +ignored messages reasons # -DPTPD_DBGV adds all debug messages # # -DPTPD_NO_DAEMON forces option -c # # -PTP_EXPERIMENTAL Allows non-standard compliant experimental options: # -U: Hybrid mode # -I: choose multicast group # # Mutually exclusive defines (useful for testing): # -DDBG_SIGUSR2_CHANGE_DOMAIN: SIGUSR2 cycles the PTP domain number # -DDBG_SIGUSR2_CHANGE_DEBUG: SIGUSR2 cycles the current debug level (if RUNTIME_DEBUG is defined) # ####### VERSION = 2.2.2 RM = rm -f SYMLINK = ln -s # start with CFLAGS += ..., so additional CFLAGs can be specified e.g. on the make command line CFLAGS += -Wall -g -fPIC CFLAGS += -DRUNTIME_DEBUG #CFLAGS += -DPTPD_DBG #CFLAGS += -DPTPD_DBG2 #CFLAGS += -DPTPD_DBGV #CFLAGS += -DPTPD_NO_DAEMON #CFLAGS += -DDBG_SIGUSR2_CHANGE_DOMAIN CFLAGS += -DDBG_SIGUSR2_CHANGE_DEBUG CFLAGS += -DPTP_EXPERIMENTAL LDFLAGS = -lm -lrt STATICLIBFLAGS = rcs SHAREDLIBFLAGS = -shared -Wl,-soname PROG = ptpd2 STATICLIB = libptpd2.a SHAREDLIB = libptpd2.so SHAREDLIBVER = $(SHAREDLIB).$(VERSION) SRCS = ptpd.c arith.c bmc.c protocol.c display.c management.c \ dep/msg.c dep/net.c dep/servo.c dep/startup.c dep/sys.c dep/timer.c OBJS = $(SRCS:.c=.o) HDRS = ptpd.h constants.h datatypes.h \ dep/ptpd_dep.h dep/constants_dep.h dep/datatypes_dep.h CSCOPE = cscope GTAGS = gtags DOXYGEN = doxygen TAGFILES = GPATH GRTAGS GSYMS GTAGS cscope.in.out cscope.out cscope.po.out .c.o: $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< $(PROG): $(OBJS) $(CC) -o $@ $(OBJS) $(LDFLAGS) $(STATICLIB): $(OBJS) $(AR) $(STATICLIBFLAGS) $(STATICLIB) $(OBJS) $(SHAREDLIB): $(OBJS) $(CC) $(SHAREDLIBFLAGS),$(SHAREDLIB) -o $(SHAREDLIB) $(OBJS) # create shared lib symlink with ptpd version extension $(SYMLINK) $(SHAREDLIB) $(SHAREDLIBVER) all: $(PROG) $(STATICLIB) $(SHAREDLIB) $(OBJS): $(HDRS) tags: $(CSCOPE) -R -q -b $(GTAGS) $(DOXYGEN) Doxyfile clean: $(RM) $(PROG) $(STATICLIB) $(SHAREDLIB) $(SHAREDLIBVER) $(OBJS) $(TAGFILES) make.out realclean: clean $(RM) $(TAGFILES) ptpd-2.2.2/src/management.c0000600000175000017500000012334711770426154014532 0ustar ernieernie/*- * Copyright (c) 2011-2012 George V. Neville-Neil, * Steven Kreuzer, * Martin Burnicki, * Jan Breuer, * Gael Mace, * Alexandre Van Kempen, * Inaqui Delgado, * Rick Ratzel, * National Instruments. * Copyright (c) 2009-2010 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. */ /** * @file management.c * @date Wed Jul 27 13:07:30 CDT 2011 * * @brief Routines to handle incoming management messages * * */ #include "ptpd.h" /**\brief Initialize outgoing management message fields*/ void initOutgoingMsgManagement(MsgManagement* incoming, MsgManagement* outgoing, PtpClock *ptpClock) { /* set header fields */ outgoing->header.transportSpecific = 0x0; outgoing->header.messageType = MANAGEMENT; outgoing->header.versionPTP = ptpClock->versionNumber; outgoing->header.domainNumber = ptpClock->domainNumber; /* set header flagField to zero for management messages, Spec 13.3.2.6 */ outgoing->header.flagField0 = 0x00; outgoing->header.flagField1 = 0x00; outgoing->header.correctionField.msb = 0; outgoing->header.correctionField.lsb = 0; copyPortIdentity(&outgoing->header.sourcePortIdentity, &ptpClock->portIdentity); outgoing->header.sequenceId = incoming->header.sequenceId; outgoing->header.controlField = 0x0; /* deprecrated for ptp version 2 */ outgoing->header.logMessageInterval = 0x7F; /* set management message fields */ copyPortIdentity( &outgoing->targetPortIdentity, &incoming->header.sourcePortIdentity ); outgoing->startingBoundaryHops = incoming->startingBoundaryHops - incoming->boundaryHops; outgoing->boundaryHops = outgoing->startingBoundaryHops; outgoing->actionField = 0; /* set default action, avoid uninitialized value */ /* init managementTLV */ XMALLOC(outgoing->tlv, sizeof(ManagementTLV)); outgoing->tlv->dataField = NULL; } /**\brief Handle incoming NULL_MANAGEMENT message*/ void handleMMNullManagement(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received NULL_MANAGEMENT message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_NULL_MANAGEMENT; switch(incoming->actionField) { case GET: case SET: DBGV(" GET or SET mgmt msg\n"); break; case COMMAND: DBGV(" COMMAND mgmt msg\n"); break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_NULL_MANAGEMENT, NOT_SUPPORTED); } } /**\brief Handle incoming CLOCK_DESCRIPTION management message*/ void handleMMClockDescription(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received CLOCK_DESCRIPTION management message \n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_CLOCK_DESCRIPTION; MMClockDescription *data = NULL; switch(incoming->actionField) { case GET: DBGV(" GET action \n"); /* Table 38 */ outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof( MMClockDescription)); data = (MMClockDescription*)outgoing->tlv->dataField; memset(data, 0, sizeof( MMClockDescription)); /* GET actions */ /* this is an ordnary node, clockType field bit 0 is one */ data->clockType0 = 0x80; data->clockType1 = 0x00; /* physical layer protocol */ data->physicalLayerProtocol.lengthField = sizeof(PROTOCOL) - 1; XMALLOC(data->physicalLayerProtocol.textField, data->physicalLayerProtocol.lengthField); memcpy(data->physicalLayerProtocol.textField, &PROTOCOL, data->physicalLayerProtocol.lengthField); /* physical address */ data->physicalAddress.addressLength = PTP_UUID_LENGTH; XMALLOC(data->physicalAddress.addressField, PTP_UUID_LENGTH); memcpy(data->physicalAddress.addressField, ptpClock->port_uuid_field, PTP_UUID_LENGTH); /* protocol address */ data->protocolAddress.addressLength = 4; data->protocolAddress.networkProtocol = 1; XMALLOC(data->protocolAddress.addressField, data->protocolAddress.addressLength); memcpy(data->protocolAddress.addressField, &ptpClock->netPath.interfaceAddr.s_addr, data->protocolAddress.addressLength); /* manufacturerIdentity OUI */ data->manufacturerIdentity0 = MANUFACTURER_ID_OUI0; data->manufacturerIdentity1 = MANUFACTURER_ID_OUI1; data->manufacturerIdentity2 = MANUFACTURER_ID_OUI2; /* reserved */ data->reserved = 0; /* product description */ data->productDescription.lengthField = sizeof(PRODUCT_DESCRIPTION) - 1; XMALLOC(data->productDescription.textField, data->productDescription.lengthField); memcpy(data->productDescription.textField, &PRODUCT_DESCRIPTION, data->productDescription.lengthField); /* revision data */ data->revisionData.lengthField = sizeof(REVISION) - 1; XMALLOC(data->revisionData.textField, data->revisionData.lengthField); memcpy(data->revisionData.textField, &REVISION, data->revisionData.lengthField); /* user description */ data->userDescription.lengthField = strlen(ptpClock->user_description); XMALLOC(data->userDescription.textField, data->userDescription.lengthField); memcpy(data->userDescription.textField, ptpClock->user_description, data->userDescription.lengthField); /* profile identity is zero unless specific profile is implemented */ data->profileIdentity0 = 0; data->profileIdentity1 = 0; data->profileIdentity2 = 0; data->profileIdentity3 = 0; data->profileIdentity4 = 0; data->profileIdentity5 = 0; break; case RESPONSE: DBGV(" RESPONSE action \n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_CLOCK_DESCRIPTION, NOT_SUPPORTED); } } /**\brief Handle incoming SLAVE_ONLY management message type*/ void handleMMSlaveOnly(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received SLAVE_ONLY management message \n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_SLAVE_ONLY; MMSlaveOnly* data = NULL; switch (incoming->actionField) { case SET: DBGV(" SET action \n"); data = (MMSlaveOnly*)incoming->tlv->dataField; /* SET actions */ ptpClock->slaveOnly = data->so; /* intentionally fall through to GET case */ case GET: DBGV(" GET action \n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMSlaveOnly)); data = (MMSlaveOnly*)outgoing->tlv->dataField; /* GET actions */ data->so = ptpClock->slaveOnly; data->reserved = 0x0; break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_SLAVE_ONLY, NOT_SUPPORTED); } } /**\brief Handle incoming USER_DESCRIPTION management message type*/ void handleMMUserDescription(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received USER_DESCRIPTION message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_USER_DESCRIPTION; MMUserDescription* data = NULL; switch(incoming->actionField) { case SET: DBGV(" SET action \n"); data = (MMUserDescription*)incoming->tlv->dataField; UInteger8 userDescriptionLength = data->userDescription.lengthField; if(userDescriptionLength <= USER_DESCRIPTION_MAX) { memset(ptpClock->user_description, 0, sizeof(ptpClock->user_description)); memcpy(ptpClock->user_description, data->userDescription.textField, userDescriptionLength); /* add null-terminator to make use of C string function strlen later */ ptpClock->user_description[userDescriptionLength] = '\0'; } else { WARNING("management user description exceeds specification length \n"); } /* intentionally fall through to GET case */ case GET: DBGV(" GET action \n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof( MMUserDescription)); data = (MMUserDescription*)outgoing->tlv->dataField; memset(data, 0, sizeof(MMUserDescription)); /* GET actions */ data->userDescription.lengthField = strlen(ptpClock->user_description); XMALLOC(data->userDescription.textField, data->userDescription.lengthField); memcpy(data->userDescription.textField, ptpClock->user_description, data->userDescription.lengthField); break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_USER_DESCRIPTION, NOT_SUPPORTED); } } /**\brief Handle incoming SAVE_IN_NON_VOLATILE_STORAGE management message type*/ void handleMMSaveInNonVolatileStorage(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received SAVE_IN_NON_VOLATILE_STORAGE message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_SAVE_IN_NON_VOLATILE_STORAGE; switch( incoming->actionField ) { case COMMAND: /* issue a NOT_SUPPORTED error management message, intentionally fall through */ case ACKNOWLEDGE: /* issue a NOT_SUPPORTED error management message, intentionally fall through */ default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_SAVE_IN_NON_VOLATILE_STORAGE, NOT_SUPPORTED); } } /**\brief Handle incoming RESET_NON_VOLATILE_STORAGE management message type*/ void handleMMResetNonVolatileStorage(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received RESET_NON_VOLATILE_STORAGE message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_RESET_NON_VOLATILE_STORAGE; switch( incoming->actionField ) { case COMMAND: /* issue a NOT_SUPPORTED error management message, intentionally fall through */ case ACKNOWLEDGE: /* issue a NOT_SUPPORTED error management message, intentionally fall through */ default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_RESET_NON_VOLATILE_STORAGE, NOT_SUPPORTED); } } /**\brief Handle incoming INITIALIZE management message type*/ void handleMMInitialize(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received INITIALIZE message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_INITIALIZE; MMInitialize* incomingData = NULL; MMInitialize* outgoingData = NULL; switch( incoming->actionField ) { case COMMAND: DBGV(" COMMAND action\n"); outgoing->actionField = ACKNOWLEDGE; XMALLOC(outgoing->tlv->dataField, sizeof(MMInitialize)); incomingData = (MMInitialize*)incoming->tlv->dataField; outgoingData = (MMInitialize*)outgoing->tlv->dataField; /* Table 45 - INITIALIZATION_KEY enumeration */ switch( incomingData->initializeKey ) { case INITIALIZE_EVENT: /* cause INITIALIZE event */ ptpClock->portState = PTP_INITIALIZING; break; default: /* do nothing, implementation specific */ DBGV("initializeKey != 0, do nothing\n"); } outgoingData->initializeKey = incomingData->initializeKey; break; case ACKNOWLEDGE: DBGV(" ACKNOWLEDGE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_INITIALIZE, NOT_SUPPORTED); } } /**\brief Handle incoming DEFAULT_DATA_SET management message type*/ void handleMMDefaultDataSet(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received DEFAULT_DATA_SET message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_DEFAULT_DATA_SET; MMDefaultDataSet* data = NULL; switch( incoming->actionField ) { case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMDefaultDataSet)); data = (MMDefaultDataSet*)outgoing->tlv->dataField; /* GET actions */ /* get bit and align for slave only */ Octet so = ptpClock->slaveOnly << 1; /* get bit and align by shifting right 1 since TWO_STEP_FLAG is either 0b00 or 0b10 */ Octet tsc = ptpClock->twoStepFlag >> 1; data->so_tsc = so | tsc; data->reserved0 = 0x0; data->numberPorts = ptpClock->numberPorts; data->priority1 = ptpClock->priority1; data->clockQuality.clockAccuracy = ptpClock->clockQuality.clockAccuracy; data->clockQuality.clockClass = ptpClock->clockQuality.clockClass; data->clockQuality.offsetScaledLogVariance = ptpClock->clockQuality.offsetScaledLogVariance; data->priority2 = ptpClock->priority2; /* copy clockIdentity */ copyClockIdentity(data->clockIdentity, ptpClock->clockIdentity); data->domainNumber = ptpClock->domainNumber; data->reserved1 = 0x0; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_DEFAULT_DATA_SET, NOT_SUPPORTED); } } /**\brief Handle incoming CURRENT_DATA_SET management message type*/ void handleMMCurrentDataSet(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received CURRENT_DATA_SET message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_CURRENT_DATA_SET; MMCurrentDataSet *data = NULL; switch( incoming->actionField ) { case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof( MMCurrentDataSet)); data = (MMCurrentDataSet*)outgoing->tlv->dataField; /* GET actions */ data->stepsRemoved = ptpClock->stepsRemoved; TimeInterval oFM; oFM.scaledNanoseconds.lsb = 0; oFM.scaledNanoseconds.msb = 0; /* TODO: call function * internalTime_to_integer64(ptpClock->offsetFromMaster, &oFM.scaledNanoseconds); */ data->offsetFromMaster.scaledNanoseconds.lsb = oFM.scaledNanoseconds.lsb; data->offsetFromMaster.scaledNanoseconds.msb = oFM.scaledNanoseconds.msb; TimeInterval mPD; mPD.scaledNanoseconds.lsb = 0; mPD.scaledNanoseconds.msb = 0; /* TODO: call function * internalTime_to_integer64(ptpClock->meanPathDelay, &mPD.scaledNanoseconds); */ data->meanPathDelay.scaledNanoseconds.lsb = mPD.scaledNanoseconds.lsb; data->meanPathDelay.scaledNanoseconds.msb = mPD.scaledNanoseconds.msb; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_CURRENT_DATA_SET, NOT_SUPPORTED); } } /**\brief Handle incoming PARENT_DATA_SET management message type*/ void handleMMParentDataSet(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received PARENT_DATA_SET message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_PARENT_DATA_SET; MMParentDataSet *data = NULL; switch( incoming->actionField ) { case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMParentDataSet)); data = (MMParentDataSet*)outgoing->tlv->dataField; /* GET actions */ copyPortIdentity(&data->parentPortIdentity, &ptpClock->parentPortIdentity); data->PS = ptpClock->parentStats; data->reserved = 0; data->observedParentOffsetScaledLogVariance = ptpClock->observedParentOffsetScaledLogVariance; data->observedParentClockPhaseChangeRate = ptpClock->observedParentClockPhaseChangeRate; data->grandmasterPriority1 = ptpClock->grandmasterPriority1; data->grandmasterClockQuality.clockAccuracy = ptpClock->grandmasterClockQuality.clockAccuracy; data->grandmasterClockQuality.clockClass = ptpClock->grandmasterClockQuality.clockClass; data->grandmasterClockQuality.offsetScaledLogVariance = ptpClock->grandmasterClockQuality.offsetScaledLogVariance; data->grandmasterPriority2 = ptpClock->grandmasterPriority2; copyClockIdentity(data->grandmasterIdentity, ptpClock->grandmasterIdentity); break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_PARENT_DATA_SET, NOT_SUPPORTED); } } /**\brief Handle incoming PROPERTIES_DATA_SET management message type*/ void handleMMTimePropertiesDataSet(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received TIME_PROPERTIES message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_TIME_PROPERTIES_DATA_SET; MMTimePropertiesDataSet *data = NULL; switch( incoming->actionField ) { case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMTimePropertiesDataSet)); data = (MMTimePropertiesDataSet*)outgoing->tlv->dataField; /* GET actions */ data->currentUtcOffset = ptpClock->currentUtcOffset; Octet ftra = SET_FIELD(ptpClock->frequencyTraceable, FTRA); Octet ttra = SET_FIELD(ptpClock->timeTraceable, TTRA); Octet ptp = SET_FIELD(ptpClock->ptpTimescale, PTPT); Octet utcv = SET_FIELD(ptpClock->currentUtcOffsetValid, UTCV); Octet li59 = SET_FIELD(ptpClock->leap59, LI59); Octet li61 = SET_FIELD(ptpClock->leap61, LI61); data->ftra_ttra_ptp_utcv_li59_li61 = ftra | ttra | ptp | utcv | li59 | li61; data->timeSource = ptpClock->timeSource; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_TIME_PROPERTIES_DATA_SET, NOT_SUPPORTED); } } /**\brief Handle incoming PORT_DATA_SET management message type*/ void handleMMPortDataSet(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received PORT_DATA_SET message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_PORT_DATA_SET; MMPortDataSet *data = NULL; switch( incoming->actionField ) { case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMPortDataSet)); data = (MMPortDataSet*)outgoing->tlv->dataField; copyPortIdentity(&data->portIdentity, &ptpClock->portIdentity); data->portState = ptpClock->portState; data->logMinDelayReqInterval = ptpClock->logMinDelayReqInterval; TimeInterval pMPD; pMPD.scaledNanoseconds.lsb = 0; pMPD.scaledNanoseconds.msb = 0; /* TODO: call function * internalTime_to_integer64(ptpClock->peerMeanPathDelay, &pMPD.scaledNanoseconds); */ data->peerMeanPathDelay.scaledNanoseconds.lsb = pMPD.scaledNanoseconds.lsb; data->peerMeanPathDelay.scaledNanoseconds.msb = pMPD.scaledNanoseconds.msb; data->logAnnounceInterval = ptpClock->logAnnounceInterval; data->announceReceiptTimeout = ptpClock->announceReceiptTimeout; data->logSyncInterval = ptpClock->logSyncInterval; data->delayMechanism = ptpClock->delayMechanism; data->logMinPdelayReqInterval = ptpClock->logMinPdelayReqInterval; data->reserved = 0; data->versionNumber = ptpClock->versionNumber; break; case RESPONSE: DBGV(" RESPONSE action \n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_PORT_DATA_SET, NOT_SUPPORTED); } } /**\brief Handle incoming PRIORITY1 management message type*/ void handleMMPriority1(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received PRIORITY1 message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_PRIORITY1; MMPriority1* data = NULL; switch( incoming->actionField ) { case SET: DBGV(" SET action\n"); data = (MMPriority1*)incoming->tlv->dataField; /* SET actions */ ptpClock->priority1 = data->priority1; /* intentionally fall through to GET case */ case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMPriority1)); data = (MMPriority1*)outgoing->tlv->dataField; /* GET actions */ data->priority1 = ptpClock->priority1; data->reserved = 0x0; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_PRIORITY1, NOT_SUPPORTED); } } /**\brief Handle incoming PRIORITY2 management message type*/ void handleMMPriority2(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received PRIORITY2 message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_PRIORITY2; MMPriority2* data = NULL; switch( incoming->actionField ) { case SET: DBGV(" SET action\n"); data = (MMPriority2*)incoming->tlv->dataField; /* SET actions */ ptpClock->priority2 = data->priority2; /* intentionally fall through to GET case */ case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMPriority2)); data = (MMPriority2*)outgoing->tlv->dataField; /* GET actions */ data->priority2 = ptpClock->priority2; data->reserved = 0x0; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_PRIORITY2, NOT_SUPPORTED); } } /**\brief Handle incoming DOMAIN management message type*/ void handleMMDomain(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received DOMAIN message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_DOMAIN; MMDomain* data = NULL; switch( incoming->actionField ) { case SET: DBGV(" SET action\n"); data = (MMDomain*)incoming->tlv->dataField; /* SET actions */ ptpClock->domainNumber = data->domainNumber; /* intentionally fall through to GET case */ case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMDomain)); data = (MMDomain*)outgoing->tlv->dataField; /* GET actions */ data->domainNumber = ptpClock->domainNumber; data->reserved = 0x0; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_DOMAIN, NOT_SUPPORTED); } } /**\brief Handle incoming LOG_ANNOUNCE_INTERVAL management message type*/ void handleMMLogAnnounceInterval(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received LOG_ANNOUNCE_INTERVAL message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_LOG_ANNOUNCE_INTERVAL; MMLogAnnounceInterval* data = NULL; switch( incoming->actionField ) { case SET: DBGV(" SET action\n"); data = (MMLogAnnounceInterval*)incoming->tlv->dataField; /* SET actions */ ptpClock->logAnnounceInterval = data->logAnnounceInterval; /* intentionally fall through to GET case */ case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMLogAnnounceInterval)); data = (MMLogAnnounceInterval*)outgoing->tlv->dataField; /* GET actions */ data->logAnnounceInterval = ptpClock->logAnnounceInterval; data->reserved = 0x0; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_LOG_ANNOUNCE_INTERVAL, NOT_SUPPORTED); } } /**\brief Handle incoming ANNOUNCE_RECEIPT_TIMEOUT management message type*/ void handleMMAnnounceReceiptTimeout(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received ANNOUNCE_RECEIPT_TIMEOUT message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_ANNOUNCE_RECEIPT_TIMEOUT; MMAnnounceReceiptTimeout* data = NULL; switch( incoming->actionField ) { case SET: DBGV(" SET action\n"); data = (MMAnnounceReceiptTimeout*)incoming->tlv->dataField; /* SET actions */ ptpClock->announceReceiptTimeout = data->announceReceiptTimeout; /* intentionally fall through to GET case */ case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMAnnounceReceiptTimeout)); data = (MMAnnounceReceiptTimeout*)outgoing->tlv->dataField; /* GET actions */ data->announceReceiptTimeout = ptpClock->announceReceiptTimeout; data->reserved = 0x0; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_ANNOUNCE_RECEIPT_TIMEOUT, NOT_SUPPORTED); } } /**\brief Handle incoming LOG_SYNC_INTERVAL management message type*/ void handleMMLogSyncInterval(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received LOG_SYNC_INTERVAL message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_LOG_SYNC_INTERVAL; MMLogSyncInterval* data = NULL; switch( incoming->actionField ) { case SET: DBGV(" SET action\n"); data = (MMLogSyncInterval*)incoming->tlv->dataField; /* SET actions */ ptpClock->logSyncInterval = data->logSyncInterval; /* intentionally fall through to GET case */ case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMLogSyncInterval)); data = (MMLogSyncInterval*)outgoing->tlv->dataField; /* GET actions */ data->logSyncInterval = ptpClock->logSyncInterval; data->reserved = 0x0; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_LOG_SYNC_INTERVAL, NOT_SUPPORTED); } } /**\brief Handle incoming VERSION_NUMBER management message type*/ void handleMMVersionNumber(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received VERSION_NUMBER message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_VERSION_NUMBER; MMVersionNumber* data = NULL; switch( incoming->actionField ) { case SET: DBGV(" SET action\n"); data = (MMVersionNumber*)incoming->tlv->dataField; /* SET actions */ ptpClock->versionNumber = data->versionNumber; /* intentionally fall through to GET case */ case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMVersionNumber)); data = (MMVersionNumber*)outgoing->tlv->dataField; /* GET actions */ data->reserved0 = 0x0; data->versionNumber = ptpClock->versionNumber; data->reserved1 = 0x0; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_VERSION_NUMBER, NOT_SUPPORTED); } } /**\brief Handle incoming ENABLE_PORT management message type*/ void handleMMEnablePort(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received ENABLE_PORT message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_ENABLE_PORT; switch( incoming->actionField ) { case COMMAND: DBGV(" COMMAND action\n"); outgoing->actionField = ACKNOWLEDGE; /* check if port is disabled, if so then initialize */ if(ptpClock->portState == PTP_DISABLED) { ptpClock->portState = PTP_INITIALIZING; } break; case ACKNOWLEDGE: DBGV(" ACKNOWLEDGE action\n"); /* TODO: implementation specific */ default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_ENABLE_PORT, NOT_SUPPORTED); } } /**\brief Handle incoming DISABLE_PORT management message type*/ void handleMMDisablePort(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received DISABLE_PORT message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_DISABLE_PORT; switch( incoming->actionField ) { case COMMAND: DBGV(" COMMAND action\n"); outgoing->actionField = ACKNOWLEDGE; /* disable port */ ptpClock->portState = PTP_DISABLED; break; case ACKNOWLEDGE: DBGV(" ACKNOWLEDGE action\n"); /* TODO: implementation specific */ default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_DISABLE_PORT, NOT_SUPPORTED); } } /**\brief Handle incoming TIME management message type*/ void handleMMTime(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received TIME message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_TIME; MMTime* data = NULL; switch( incoming->actionField ) { case SET: DBGV(" SET action\n"); data = (MMTime*)incoming->tlv->dataField; /* SET actions */ /* TODO: add currentTime */ case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; /* TODO: implement action */ break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_TIME, NOT_SUPPORTED); } } /**\brief Handle incoming CLOCK_ACCURACY management message type*/ void handleMMClockAccuracy(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received CLOCK_ACCURACY message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_CLOCK_ACCURACY; MMClockAccuracy* data = NULL; switch( incoming->actionField ) { case SET: DBGV(" SET action\n"); data = (MMClockAccuracy*)incoming->tlv->dataField; /* SET actions */ ptpClock->clockQuality.clockAccuracy = data->clockAccuracy; /* intentionally fall through to GET case */ case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMClockAccuracy)); data = (MMClockAccuracy*)outgoing->tlv->dataField; /* GET actions */ data->clockAccuracy = ptpClock->clockQuality.clockAccuracy; data->reserved = 0x0; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_CLOCK_ACCURACY, NOT_SUPPORTED); } } /**\brief Handle incoming UTC_PROPERTIES management message type*/ void handleMMUtcProperties(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received UTC_PROPERTIES message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_UTC_PROPERTIES; MMUtcProperties* data = NULL; switch( incoming->actionField ) { case SET: DBGV(" SET action\n"); data = (MMUtcProperties*)incoming->tlv->dataField; /* SET actions */ ptpClock->currentUtcOffset = data->currentUtcOffset; /* set bit */ ptpClock->currentUtcOffsetValid = IS_SET(data->utcv_li59_li61, UTCV); ptpClock->leap59 = IS_SET(data->utcv_li59_li61, LI59); ptpClock->leap61 = IS_SET(data->utcv_li59_li61, LI61); /* intentionally fall through to GET case */ case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMUtcProperties)); data = (MMUtcProperties*)outgoing->tlv->dataField; /* GET actions */ data->currentUtcOffset = ptpClock->currentUtcOffset; Octet utcv = SET_FIELD(ptpClock->currentUtcOffsetValid, UTCV); Octet li59 = SET_FIELD(ptpClock->leap59, LI59); Octet li61 = SET_FIELD(ptpClock->leap61, LI61); data->utcv_li59_li61 = utcv | li59 | li61; data->reserved = 0x0; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_UTC_PROPERTIES, NOT_SUPPORTED); } } /**\brief Handle incoming TRACEABILITY_PROPERTIES management message type*/ void handleMMTraceabilityProperties(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received TRACEABILITY_PROPERTIES message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_TRACEABILITY_PROPERTIES; MMTraceabilityProperties* data = NULL; switch( incoming->actionField ) { case SET: DBGV(" SET action\n"); data = (MMTraceabilityProperties*)incoming->tlv->dataField; /* SET actions */ ptpClock->frequencyTraceable = IS_SET(data->ftra_ttra, FTRA); ptpClock->timeTraceable = IS_SET(data->ftra_ttra, TTRA); /* intentionally fall through to GET case */ case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMTraceabilityProperties)); data = (MMTraceabilityProperties*)outgoing->tlv->dataField; /* GET actions */ Octet ftra = SET_FIELD(ptpClock->frequencyTraceable, FTRA); Octet ttra = SET_FIELD(ptpClock->timeTraceable, TTRA); data->ftra_ttra = ftra | ttra; data->reserved = 0x0; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_TRACEABILITY_PROPERTIES, NOT_SUPPORTED); } } /**\brief Handle incoming DELAY_MECHANISM management message type*/ void handleMMDelayMechanism(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received DELAY_MECHANISM message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_DELAY_MECHANISM; MMDelayMechanism *data = NULL; switch( incoming->actionField ) { case SET: DBGV(" SET action\n"); data = (MMDelayMechanism*)incoming->tlv->dataField; /* SET actions */ ptpClock->delayMechanism = data->delayMechanism; /* intentionally fall through to GET case */ case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMDelayMechanism)); data = (MMDelayMechanism*)outgoing->tlv->dataField; /* GET actions */ data->delayMechanism = ptpClock->delayMechanism; data->reserved = 0x0; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_DELAY_MECHANISM, NOT_SUPPORTED); } } /**\brief Handle incoming LOG_MIN_PDELAY_REQ_INTERVAL management message type*/ void handleMMLogMinPdelayReqInterval(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock) { DBGV("received LOG_MIN_PDELAY_REQ_INTERVAL message\n"); initOutgoingMsgManagement(incoming, outgoing, ptpClock); outgoing->tlv->tlvType = TLV_MANAGEMENT; outgoing->tlv->lengthField = 2; outgoing->tlv->managementId = MM_LOG_MIN_PDELAY_REQ_INTERVAL; MMLogMinPdelayReqInterval* data = NULL; switch( incoming->actionField ) { case SET: DBGV(" SET action\n"); data = (MMLogMinPdelayReqInterval*)incoming->tlv->dataField; /* SET actions */ ptpClock->logMinPdelayReqInterval = data->logMinPdelayReqInterval; /* intentionally fall through to GET case */ case GET: DBGV(" GET action\n"); outgoing->actionField = RESPONSE; XMALLOC(outgoing->tlv->dataField, sizeof(MMLogMinPdelayReqInterval)); data = (MMLogMinPdelayReqInterval*)outgoing->tlv->dataField; /* GET actions */ data->logMinPdelayReqInterval = ptpClock->logMinPdelayReqInterval; data->reserved = 0x0; break; case RESPONSE: DBGV(" RESPONSE action\n"); /* TODO: implementation specific */ break; default: DBGV(" unknown actionType \n"); free(outgoing->tlv); handleErrorManagementMessage(incoming, outgoing, ptpClock, MM_LOG_MIN_PDELAY_REQ_INTERVAL, NOT_SUPPORTED); } } /**\brief Handle incoming ERROR_STATUS management message type*/ void handleMMErrorStatus(MsgManagement *incoming) { DBGV("received MANAGEMENT_ERROR_STATUS message \n"); /* implementation specific */ } /**\brief Handle issuing ERROR_STATUS management message type*/ void handleErrorManagementMessage(MsgManagement *incoming, MsgManagement *outgoing, PtpClock *ptpClock, Enumeration16 mgmtId, Enumeration16 errorId) { /* init management header fields */ initOutgoingMsgManagement(incoming, outgoing, ptpClock); /* init management error status tlv fields */ outgoing->tlv->tlvType = TLV_MANAGEMENT_ERROR_STATUS; outgoing->tlv->lengthField = 2; /* management error status managementId field is the errorId */ outgoing->tlv->managementId = errorId; switch(incoming->actionField) { case GET: case SET: outgoing->actionField = RESPONSE; break; case RESPONSE: outgoing->actionField = ACKNOWLEDGE; break; default: outgoing->actionField = 0; } XMALLOC(outgoing->tlv->dataField, sizeof( MMErrorStatus)); MMErrorStatus *data = (MMErrorStatus*)outgoing->tlv->dataField; /* set managementId */ data->managementId = mgmtId; data->reserved = 0x00; data->displayData.lengthField = 0; data->displayData.textField = NULL; } ptpd-2.2.2/src/ptpd.h0000600000175000017500000002417011770426154013364 0ustar ernieernie/** * @file ptpd.h * @mainpage Ptpd v2 Documentation * @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. */ #ifndef PTPD_H_ #define PTPD_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __APPLE__ #include #endif #include #include #include #include #include #include #include #include #include "constants.h" #include "limits.h" #include "dep/constants_dep.h" #include "dep/datatypes_dep.h" #include "datatypes.h" #include "dep/ptpd_dep.h" /* NOTE: this macro can be refactored into a function */ #define XMALLOC(ptr,size) \ if(!((ptr)=malloc(size))) { \ PERROR("failed to allocate memory"); \ ptpdShutdown(ptpClock); \ exit(1); \ } #define IS_SET(data, bitpos) \ ((data & ( 0x1 << bitpos )) == (0x1 << bitpos)) #define SET_FIELD(data, bitpos) \ data << bitpos #define min(a,b) (((a)<(b))?(a):(b)) #define max(a,b) (((a)>(b))?(a):(b)) /** \name arith.c * -Timing management and arithmetic*/ /**\{*/ /* arith.c */ /** * \brief Convert Integer64 into TimeInternal structure */ void integer64_to_internalTime(Integer64,TimeInternal*); /** * \brief Convert TimeInternal structure to Integer64 */ void internalTime_to_integer64(TimeInternal, Integer64*); /** * \brief Convert TimeInternal into Timestamp structure (defined by the spec) */ void fromInternalTime(TimeInternal*,Timestamp*); /** * \brief Convert Timestamp to TimeInternal structure (defined by the spec) */ 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 * * The nanosecondsField member must always be less than 10⁹ * This function is used after adding or substracting TimeInternal */ void normalizeTime(TimeInternal*); /** * \brief Add two InternalTime structure and normalize */ void addTime(TimeInternal*,const TimeInternal*,const TimeInternal*); /** * \brief Substract two InternalTime structure and normalize */ void subTime(TimeInternal*,const TimeInternal*,const TimeInternal*); /** \}*/ /** * \brief Divied an InternalTime by 2 */ void div2Time(TimeInternal *); /** \name bmc.c * -Best Master Clock Algorithm functions*/ /**\{*/ /* bmc.c */ /** * \brief Compare data set of foreign masters and local data set * \return The recommended state for the port */ UInteger8 bmc(ForeignMasterRecord*,RunTimeOpts*,PtpClock*); /** * \brief When recommended state is Master, copy local data into parent and grandmaster dataset */ void m1(RunTimeOpts *, PtpClock*); /** * \brief When recommended state is Slave, copy dataset of master into parent and grandmaster dataset */ void s1(MsgHeader*,MsgAnnounce*,PtpClock*, RunTimeOpts *); void p1(PtpClock *ptpClock, RunTimeOpts *rtOpts); /** * \brief Initialize datas */ void initData(RunTimeOpts*,PtpClock*); /** \}*/ /** \name protocol.c * -Execute the protocol engine*/ /**\{*/ /** * \brief Protocol engine */ /* protocol.c */ void protocol(RunTimeOpts*,PtpClock*); /** \}*/ /** \name management.c * -Management message support*/ /**\{*/ /* management.c */ /** * \brief Management message support */ void handleMMNullManagement(MsgManagement*, MsgManagement*, PtpClock*); void handleMMClockDescription(MsgManagement*, MsgManagement*, PtpClock*); void handleMMSlaveOnly(MsgManagement*, MsgManagement*, PtpClock*); void handleMMUserDescription(MsgManagement*, MsgManagement*, PtpClock*); void handleMMSaveInNonVolatileStorage(MsgManagement*, MsgManagement*, PtpClock*); void handleMMResetNonVolatileStorage(MsgManagement*, MsgManagement*, PtpClock*); void handleMMInitialize(MsgManagement*, MsgManagement*, PtpClock*); void handleMMDefaultDataSet(MsgManagement*, MsgManagement*, PtpClock*); void handleMMCurrentDataSet(MsgManagement*, MsgManagement*, PtpClock*); void handleMMParentDataSet(MsgManagement*, MsgManagement*, PtpClock*); void handleMMTimePropertiesDataSet(MsgManagement*, MsgManagement*, PtpClock*); void handleMMPortDataSet(MsgManagement*, MsgManagement*, PtpClock*); void handleMMPriority1(MsgManagement*, MsgManagement*, PtpClock*); void handleMMPriority2(MsgManagement*, MsgManagement*, PtpClock*); void handleMMDomain(MsgManagement*, MsgManagement*, PtpClock*); void handleMMLogAnnounceInterval(MsgManagement*, MsgManagement*, PtpClock*); void handleMMAnnounceReceiptTimeout(MsgManagement*, MsgManagement*, PtpClock*); void handleMMLogSyncInterval(MsgManagement*, MsgManagement*, PtpClock*); void handleMMVersionNumber(MsgManagement*, MsgManagement*, PtpClock*); void handleMMEnablePort(MsgManagement*, MsgManagement*, PtpClock*); void handleMMDisablePort(MsgManagement*, MsgManagement*, PtpClock*); void handleMMTime(MsgManagement*, MsgManagement*, PtpClock*); void handleMMClockAccuracy(MsgManagement*, MsgManagement*, PtpClock*); void handleMMUtcProperties(MsgManagement*, MsgManagement*, PtpClock*); void handleMMTraceabilityProperties(MsgManagement*, MsgManagement*, PtpClock*); void handleMMDelayMechanism(MsgManagement*, MsgManagement*, PtpClock*); void handleMMLogMinPdelayReqInterval(MsgManagement*, MsgManagement*, PtpClock*); void handleMMErrorStatus(MsgManagement*); void handleErrorManagementMessage(MsgManagement *incoming, MsgManagement *outgoing, PtpClock *ptpClock, Enumeration16 mgmtId, Enumeration16 errorId); /** \}*/ /* * \brief Packing and Unpacking macros */ #define DECLARE_PACK( type ) void pack##type( void*, void* ); DECLARE_PACK( NibbleUpper ) DECLARE_PACK( Enumeration4Lower ) DECLARE_PACK( UInteger4Lower ) DECLARE_PACK( UInteger16 ) DECLARE_PACK( UInteger8 ) DECLARE_PACK( Octet ) DECLARE_PACK( Integer8 ) DECLARE_PACK( UInteger48 ) DECLARE_PACK( Integer64 ) #define DECLARE_UNPACK( type ) void unpack##type( void*, void*, PtpClock *ptpClock ); DECLARE_UNPACK( Boolean ) DECLARE_UNPACK( Enumeration4Lower ) DECLARE_UNPACK( Octet ) DECLARE_UNPACK( UInteger48 ) DECLARE_UNPACK( Integer64 ) //Diplay functions usefull to debug void displayRunTimeOpts(RunTimeOpts*); void displayDefault (PtpClock*); void displayCurrent (PtpClock*); void displayParent (PtpClock*); void displayGlobal (PtpClock*); void displayPort (PtpClock*); void displayForeignMaster (PtpClock*); void displayOthers (PtpClock*); void displayBuffer (PtpClock*); void displayPtpClock (PtpClock*); void timeInternal_display(TimeInternal*); void clockIdentity_display(ClockIdentity); void netPath_display(NetPath*); void intervalTimer_display(IntervalTimer*); void integer64_display (Integer64*); void timeInterval_display(TimeInterval*); void portIdentity_display(PortIdentity*); void clockQuality_display (ClockQuality*); void PTPText_display(PTPText*, PtpClock*); void iFaceName_display(Octet*); void unicast_display(Octet*); void msgHeader_display(MsgHeader*); void msgAnnounce_display(MsgAnnounce*); void msgSync_display(MsgSync *sync); void msgFollowUp_display(MsgFollowUp*); void msgPDelayReq_display(MsgPDelayReq*); void msgDelayReq_display(MsgDelayReq * req); void msgDelayResp_display(MsgDelayResp * resp); void msgPDelayResp_display(MsgPDelayResp * presp); void msgPDelayRespFollowUp_display(MsgPDelayRespFollowUp * prespfollow); void msgManagement_display(MsgManagement * manage); void mMSlaveOnly_display(MMSlaveOnly*, PtpClock*); void mMClockDescription_display(MMClockDescription*, PtpClock*); void mMUserDescription_display(MMUserDescription*, PtpClock*); void mMInitialize_display(MMInitialize*, PtpClock*); void mMDefaultDataSet_display(MMDefaultDataSet*, PtpClock*); void mMCurrentDataSet_display(MMCurrentDataSet*, PtpClock*); void mMParentDataSet_display(MMParentDataSet*, PtpClock*); void mMTimePropertiesDataSet_display(MMTimePropertiesDataSet*, PtpClock*); void mMPortDataSet_display(MMPortDataSet*, PtpClock*); void mMPriority1_display(MMPriority1*, PtpClock*); void mMPriority2_display(MMPriority2*, PtpClock*); void mMDomain_display(MMDomain*, PtpClock*); void mMLogAnnounceInterval_display(MMLogAnnounceInterval*, PtpClock*); void mMAnnounceReceiptTimeout_display(MMAnnounceReceiptTimeout*, PtpClock*); void mMLogSyncInterval_display(MMLogSyncInterval*, PtpClock*); void mMVersionNumber_display(MMVersionNumber*, PtpClock*); void mMTime_display(MMTime*, PtpClock*); void mMClockAccuracy_display(MMClockAccuracy*, PtpClock*); void mMUtcProperties_display(MMUtcProperties*, PtpClock*); void mMTraceabilityProperties_display(MMTraceabilityProperties*, PtpClock*); void mMDelayMechanism_display(MMDelayMechanism*, PtpClock*); void mMLogMinPdelayReqInterval_display(MMLogMinPdelayReqInterval*, PtpClock*); void mMErrorStatus_display(MMErrorStatus*, PtpClock*); void clearTime(TimeInternal *time); int isTimeInternalNegative(const TimeInternal * p); char *dump_TimeInternal(const TimeInternal * p); char *dump_TimeInternal2(const char *st1, const TimeInternal * p1, const char *st2, const TimeInternal * p2); int snprint_TimeInternal(char *s, int max_len, const TimeInternal * p); void nano_to_Time(TimeInternal *time, int nano); int gtTime(TimeInternal *x, TimeInternal *b); void absTime(TimeInternal *time); int is_Time_close(TimeInternal *x, TimeInternal *b, int nanos); int isTimeInternalNegative(const TimeInternal * p); int check_timestamp_is_fresh2(TimeInternal * timeA, TimeInternal * timeB); int check_timestamp_is_fresh(TimeInternal * timeA); void toState(UInteger8,RunTimeOpts*,PtpClock*); /* helper functions for leap second handling */ float secondsToMidnight(void); float getPauseAfterMidnight(Integer8 announceInterval); #endif /*PTPD_H_*/ ptpd-2.2.2/src/ptpd2.80000600000175000017500000000712211770426154013364 0ustar ernieernie.\" -*- nroff -*" .TH ptpd2 8 "January, 2012" "version 2.2.0" "Precision Time Protocol daemon" .SH NAME ptpd2 \- Precision Time Protocol daemon (1588-2008) .SH SYNOPSIS .B ptpd2 [?] [-B] [-c] [-C] [-S] [-f FILE] [-R FILE] [-d] [-D] [-P] [-x] [-O NUMBER] [-M NUMBER] [-t] [-T ttl] [-a NUMBER,NUMBER] [-w NUMBER] [-b NAME] [-u ADDRESS] [-I group] [-U] [-e] [-h] [-l NUMBER,NUMBER] [-o NUMBER] [-i NUMBER] [-n NUMBER] [-y NUMBER] [-m NUMBER] [-V NUMBER] [-g] [-v NUMBER] [-r NUMBER] [-N NUMBER] [-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. .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 to work properly with other IEEE 1588 implementations. The source code for PTPd is freely available under a BSD-style license. Thanks to contributions from users, PTPd is becoming an increasingly portable, interoperable, and stable IEEE 1588 implementation. .PP For more information, see http://ptpd.sourceforge.net/ .SH OPTIONS .TP .B \-? display a short help text .TP .B \-B enable debugging if it has been previously compiled in .TP .B \-c run in command line (non-daemon) mode .TP .B \-C run in command line mode and display statistics and logs .TP .B \-S do not log information to syslog .TP .B \-f FILE send output to FILE .TP .B \-R FILE record a quality FILE .TP .B \-D display stats in .csv format .TP .B \-x do not reset the clock if off by more than one second .TP .B \-M NUMBER do not accept delay values of more than NUMBER nanoseconds .TP .B \-O NUMBER do not reset the clock if offset is more than NUMBER nanoseconds .TP .B \-P display packets received for debugging purposes .TP .B \-t do not adjust the system clock .TP .B \-T set multicast TTL for packets. Defaults to 1. .TP .B \-a NUMBER,NUMBER specify clock servo P and I attenuations .TP .B \-w NUMBER specify one way delay filter stiffness .TP .B \-b NAME bind PTP to network interface NAME .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 .B \-h run in End to End mode .TP .B \-l NUMBER,NUMBER specify inbound, outbound latency in nsec .TP .B \-o NUMBER specify current UTC offset .TP .B \-i NUMBER specify PTP domain number .TP .B \-n NUMBER specify announce interval in 2^NUMBER sec .TP .B \-y NUMBER specify sync interval in 2^NUMBER sec .TP .B \-m NUMBER specify max number of foreign master records .TP .B \-N NUMBER announce receipt timeout .TP .B \-V limit displaying statistics by setting the seconds between log messages .TP .B \-g run as slave only .TP .B \-v NUMBER specify system clock allen variance .TP .B \-r NUMBER specify system clock accuracy .TP .B \-s NUMBER specify system clock class .TP .B \-p NUMBER specify priority1 attribute .TP .B \-q NUMBER specify priority2 attribute .TP .B \-G run as master with connection to NTP .TP .B \-W run as master without NTP .TP .B \-Y NUMBER set an initial delay request value .TP .B \-L enable running multiple ptpd2 daemons .TP .B \-j turn off IGMP refresh messages .SH AUTHORS Gael Mace & Alexandre Van Kempen. .PP Steven Kreuzer .PP George Neville-Neil .PP This manual page was written by Gael Mace for any Linux environment project. ptpd-2.2.2/src/display.c0000600000175000017500000005272111770426154014060 0ustar ernieernie/*- * Copyright (c) 2011-2012 George V. Neville-Neil, * Steven Kreuzer, * Martin Burnicki, * Jan Breuer, * Gael Mace, * Alexandre Van Kempen, * Inaqui Delgado, * Rick Ratzel, * National Instruments. * Copyright (c) 2009-2010 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. */ /** * @file display.c * @date Thu Aug 12 09:06:21 2010 * * @brief General routines for displaying internal data. * * */ #include "ptpd.h" /**\brief Display an Integer64 type*/ void integer64_display(Integer64 * bigint) { DBGV("Integer 64 : \n"); DBGV("LSB : %u\n", bigint->lsb); DBGV("MSB : %d\n", bigint->msb); } /**\brief Display an UInteger48 type*/ void uInteger48_display(UInteger48 * bigint) { DBGV("Integer 48 : \n"); DBGV("LSB : %u\n", bigint->lsb); DBGV("MSB : %u\n", bigint->msb); } /** \brief Display a TimeInternal Structure*/ void timeInternal_display(TimeInternal * timeInternal) { DBGV("seconds : %d \n", timeInternal->seconds); DBGV("nanoseconds %d \n", timeInternal->nanoseconds); } /** \brief Display a Timestamp Structure*/ void timestamp_display(Timestamp * timestamp) { uInteger48_display(×tamp->secondsField); DBGV("nanoseconds %u \n", timestamp->nanosecondsField); } /**\brief Display a Clockidentity Structure*/ void clockIdentity_display(ClockIdentity clockIdentity) { DBGV( "ClockIdentity : %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", clockIdentity[0], clockIdentity[1], clockIdentity[2], clockIdentity[3], clockIdentity[4], clockIdentity[5], clockIdentity[6], clockIdentity[7] ); } /**\brief Display MAC address*/ void clockUUID_display(Octet * sourceUuid) { DBGV( "sourceUuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", sourceUuid[0], sourceUuid[1], sourceUuid[2], sourceUuid[3], sourceUuid[4], sourceUuid[5] ); } /**\brief Display Network info*/ void netPath_display(NetPath * net) { struct in_addr addr; DBGV("eventSock : %d \n", net->eventSock); DBGV("generalSock : %d \n", net->generalSock); addr.s_addr = net->multicastAddr; DBGV("multicastAdress : %s \n", inet_ntoa(addr)); addr.s_addr = net->peerMulticastAddr; DBGV("peerMulticastAddress : %s \n", inet_ntoa(addr)); addr.s_addr = net->unicastAddr; DBGV("unicastAddress : %s \n", inet_ntoa(addr)); } /**\brief Display a IntervalTimer Structure*/ void intervalTimer_display(IntervalTimer * ptimer) { DBGV("interval : %d \n", ptimer->interval); DBGV("left : %d \n", ptimer->left); DBGV("expire : %d \n", ptimer->expire); } /**\brief Display a TimeInterval Structure*/ void timeInterval_display(TimeInterval * timeInterval) { integer64_display(&timeInterval->scaledNanoseconds); } /**\brief Display a Portidentity Structure*/ void portIdentity_display(PortIdentity * portIdentity) { clockIdentity_display(portIdentity->clockIdentity); DBGV("port number : %d \n", portIdentity->portNumber); } /**\brief Display a Clockquality Structure*/ void clockQuality_display(ClockQuality * clockQuality) { DBGV("clockClass : %d \n", clockQuality->clockClass); DBGV("clockAccuracy : %d \n", clockQuality->clockAccuracy); DBGV("offsetScaledLogVariance : %d \n", clockQuality->offsetScaledLogVariance); } /**\brief Display PTPText Structure*/ void PTPText_display(PTPText *p, PtpClock *ptpClock) { /* Allocate new memory to append null-terminator to the text field. * This allows printing the textField as a string */ Octet *str; XMALLOC(str, p->lengthField + 1); memcpy(str, p->textField, p->lengthField); str[p->lengthField] = '\0'; DBGV(" lengthField : %d \n", p->lengthField); DBGV(" textField : %s \n", str); free(str); } /**\brief Display the Network Interface Name*/ void iFaceName_display(Octet * iFaceName) { int i; DBGV("iFaceName : "); for (i = 0; i < IFACE_NAME_LENGTH; i++) { DBGV("%c", iFaceName[i]); } DBGV("\n"); } /**\brief Display an Unicast Adress*/ void unicast_display(Octet * unicast) { int i; DBGV("Unicast adress : "); for (i = 0; i < NET_ADDRESS_LENGTH; i++) { DBGV("%c", unicast[i]); } DBGV("\n"); } /**\brief Display Sync message*/ void msgSync_display(MsgSync * sync) { DBGV("Message Sync : \n"); timestamp_display(&sync->originTimestamp); DBGV("\n"); } /**\brief Display Header message*/ void msgHeader_display(MsgHeader * header) { 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->flagField0, header->flagField1); DBGV("CorrectionField : \n"); integer64_display(&header->correctionField); DBGV("SourcePortIdentity : \n"); portIdentity_display(&header->sourcePortIdentity); DBGV("sequenceId : %d\n", header->sequenceId); DBGV("controlField : %d\n", header->controlField); DBGV("logMessageInterval : %d\n", header->logMessageInterval); DBGV("\n"); } /**\brief Display Announce message*/ void msgAnnounce_display(MsgAnnounce * announce) { DBGV("Announce Message : \n"); DBGV("\n"); DBGV("originTimestamp : \n"); DBGV("secondField : \n"); timestamp_display(&announce->originTimestamp); DBGV("currentUtcOffset : %d \n", announce->currentUtcOffset); DBGV("grandMasterPriority1 : %d \n", announce->grandmasterPriority1); DBGV("grandMasterClockQuality : \n"); clockQuality_display(&announce->grandmasterClockQuality); DBGV("grandMasterPriority2 : %d \n", announce->grandmasterPriority2); DBGV("grandMasterIdentity : \n"); clockIdentity_display(announce->grandmasterIdentity); DBGV("stepsRemoved : %d \n", announce->stepsRemoved); DBGV("timeSource : %d \n", announce->timeSource); DBGV("\n"); } /**\brief Display Follow_UP message*/ void msgFollowUp_display(MsgFollowUp * follow) { timestamp_display(&follow->preciseOriginTimestamp); } /**\brief Display DelayReq message*/ void msgDelayReq_display(MsgDelayReq * req) { timestamp_display(&req->originTimestamp); } /**\brief Display DelayResp message*/ void msgDelayResp_display(MsgDelayResp * resp) { timestamp_display(&resp->receiveTimestamp); portIdentity_display(&resp->requestingPortIdentity); } /**\brief Display Pdelay_Req message*/ void msgPDelayReq_display(MsgPDelayReq * preq) { timestamp_display(&preq->originTimestamp); } /**\brief Display Pdelay_Resp message*/ void msgPDelayResp_display(MsgPDelayResp * presp) { timestamp_display(&presp->requestReceiptTimestamp); portIdentity_display(&presp->requestingPortIdentity); } /**\brief Display Pdelay_Resp Follow Up message*/ void msgPDelayRespFollowUp_display(MsgPDelayRespFollowUp * prespfollow) { timestamp_display(&prespfollow->responseOriginTimestamp); portIdentity_display(&prespfollow->requestingPortIdentity); } /**\brief Display Management message*/ void msgManagement_display(MsgManagement * manage) { DBGV("Management Message : \n"); DBGV("\n"); DBGV("targetPortIdentity : \n"); portIdentity_display(&manage->targetPortIdentity); DBGV("startingBoundaryHops : %d \n", manage->startingBoundaryHops); DBGV("boundaryHops : %d \n", manage->boundaryHops); DBGV("actionField : %d\n", manage->actionField); } /**\brief Display ManagementTLV Slave Only message*/ void mMSlaveOnly_display(MMSlaveOnly *slaveOnly, PtpClock *ptpClock) { DBGV("Slave Only ManagementTLV message \n"); DBGV("SO : %d \n", slaveOnly->so); } /**\brief Display ManagementTLV Clock Description message*/ void mMClockDescription_display(MMClockDescription *clockDescription, PtpClock *ptpClock) { DBGV("Clock Description ManagementTLV message \n"); DBGV("clockType0 : %d \n", clockDescription->clockType0); DBGV("clockType1 : %d \n", clockDescription->clockType1); DBGV("physicalLayerProtocol : \n"); PTPText_display(&clockDescription->physicalLayerProtocol, ptpClock); DBGV("physicalAddressLength : %d \n", clockDescription->physicalAddress.addressLength); if(clockDescription->physicalAddress.addressField) { DBGV("physicalAddressField : \n"); clockUUID_display(clockDescription->physicalAddress.addressField); } DBGV("protocolAddressNetworkProtocol : %d \n", clockDescription->protocolAddress.networkProtocol); DBGV("protocolAddressLength : %d \n", clockDescription->protocolAddress.addressLength); if(clockDescription->protocolAddress.addressField) { DBGV("protocolAddressField : %d.%d.%d.%d \n", (UInteger8)clockDescription->protocolAddress.addressField[0], (UInteger8)clockDescription->protocolAddress.addressField[1], (UInteger8)clockDescription->protocolAddress.addressField[2], (UInteger8)clockDescription->protocolAddress.addressField[3]); } DBGV("manufacturerIdentity0 : %d \n", clockDescription->manufacturerIdentity0); DBGV("manufacturerIdentity1 : %d \n", clockDescription->manufacturerIdentity1); DBGV("manufacturerIdentity2 : %d \n", clockDescription->manufacturerIdentity2); DBGV("productDescription : \n"); PTPText_display(&clockDescription->productDescription, ptpClock); DBGV("revisionData : \n"); PTPText_display(&clockDescription->revisionData, ptpClock); DBGV("userDescription : \n"); PTPText_display(&clockDescription->userDescription, ptpClock); DBGV("profileIdentity0 : %d \n", clockDescription->profileIdentity0); DBGV("profileIdentity1 : %d \n", clockDescription->profileIdentity1); DBGV("profileIdentity2 : %d \n", clockDescription->profileIdentity2); DBGV("profileIdentity3 : %d \n", clockDescription->profileIdentity3); DBGV("profileIdentity4 : %d \n", clockDescription->profileIdentity4); DBGV("profileIdentity5 : %d \n", clockDescription->profileIdentity5); } void mMUserDescription_display(MMUserDescription* userDescription, PtpClock *ptpClock) { /* TODO: implement me */ } void mMInitialize_display(MMInitialize* initialize, PtpClock *ptpClock) { /* TODO: implement me */ } void mMDefaultDataSet_display(MMDefaultDataSet* defaultDataSet, PtpClock *ptpClock) { /* TODO: implement me */ } void mMCurrentDataSet_display(MMCurrentDataSet* currentDataSet, PtpClock *ptpClock) { /* TODO: implement me */ } void mMParentDataSet_display(MMParentDataSet* parentDataSet, PtpClock *ptpClock) { /* TODO: implement me */ } void mMTimePropertiesDataSet_display(MMTimePropertiesDataSet* timePropertiesDataSet, PtpClock *ptpClock) { /* TODO: implement me */ } void mMPortDataSet_display(MMPortDataSet* portDataSet, PtpClock *ptpClock) { /* TODO: implement me */ } void mMPriority1_display(MMPriority1* priority1, PtpClock *ptpClock) { /* TODO: implement me */ } void mMPriority2_display(MMPriority2* priority2, PtpClock *ptpClock) { /* TODO: implement me */ } void mMDomain_display(MMDomain* domain, PtpClock *ptpClock) { /* TODO: implement me */ } void mMLogAnnounceInterval_display(MMLogAnnounceInterval* logAnnounceInterval, PtpClock *ptpClock) { /* TODO: implement me */ } void mMAnnounceReceiptTimeout_display(MMAnnounceReceiptTimeout* announceReceiptTimeout, PtpClock *ptpClock) { /* TODO: implement me */ } void mMLogSyncInterval_display(MMLogSyncInterval* logSyncInterval, PtpClock *ptpClock) { /* TODO: implement me */ } void mMVersionNumber_display(MMVersionNumber* versionNumber, PtpClock *ptpClock) { /* TODO: implement me */ } void mMTime_display(MMTime* time, PtpClock *ptpClock) { /* TODO: implement me */ } void mMClockAccuracy_display(MMClockAccuracy* clockAccuracy, PtpClock *ptpClock) { /* TODO: implement me */ } void mMUtcProperties_display(MMUtcProperties* utcProperties, PtpClock *ptpClock) { /* TODO: implement me */ } void mMTraceabilityProperties_display(MMTraceabilityProperties* traceabilityProperties, PtpClock *ptpClock) { /* TODO: implement me */ } void mMDelayMechanism_display(MMDelayMechanism* delayMechanism, PtpClock *ptpClock) { /* TODO: implement me */ } void mMLogMinPdelayReqInterval_display(MMLogMinPdelayReqInterval* logMinPdelayReqInterval, PtpClock *ptpClock) { /* TODO: implement me */ } void mMErrorStatus_display(MMErrorStatus* errorStatus, PtpClock *ptpClock) { /* TODO: implement me */ } /**\brief Display runTimeOptions structure*/ void displayRunTimeOpts(RunTimeOpts * rtOpts) { DBGV("---Run time Options Display-- \n"); DBGV("\n"); DBGV("announceInterval : %d \n", rtOpts->announceInterval); DBGV("syncInterval : %d \n", rtOpts->syncInterval); clockQuality_display(&(rtOpts->clockQuality)); DBGV("priority1 : %d \n", rtOpts->priority1); DBGV("priority2 : %d \n", rtOpts->priority2); DBGV("domainNumber : %d \n", rtOpts->domainNumber); DBGV("slaveOnly : %d \n", rtOpts->slaveOnly); DBGV("currentUtcOffset : %d \n", rtOpts->currentUtcOffset); unicast_display(rtOpts->unicastAddress); DBGV("noAdjust : %d \n", rtOpts->noAdjust); DBGV("displayStats : %d \n", rtOpts->displayStats); iFaceName_display(rtOpts->ifaceName); DBGV("ap : %d \n", rtOpts->ap); DBGV("aI : %d \n", rtOpts->ai); DBGV("s : %d \n", rtOpts->s); DBGV("inbound latency : \n"); timeInternal_display(&(rtOpts->inboundLatency)); DBGV("outbound latency : \n"); timeInternal_display(&(rtOpts->outboundLatency)); DBGV("max_foreign_records : %d \n", rtOpts->max_foreign_records); DBGV("ethernet mode : %d \n", rtOpts->ethernet_mode); DBGV("\n"); } /**\brief Display Default data set of a PtpClock*/ void displayDefault(PtpClock * ptpClock) { DBGV("---Ptp Clock Default Data Set-- \n"); DBGV("\n"); DBGV("twoStepFlag : %d \n", ptpClock->twoStepFlag); clockIdentity_display(ptpClock->clockIdentity); DBGV("numberPorts : %d \n", ptpClock->numberPorts); clockQuality_display(&(ptpClock->clockQuality)); DBGV("priority1 : %d \n", ptpClock->priority1); DBGV("priority2 : %d \n", ptpClock->priority2); DBGV("domainNumber : %d \n", ptpClock->domainNumber); DBGV("slaveOnly : %d \n", ptpClock->slaveOnly); DBGV("\n"); } /**\brief Display Current data set of a PtpClock*/ void displayCurrent(PtpClock * ptpClock) { DBGV("---Ptp Clock Current Data Set-- \n"); DBGV("\n"); DBGV("stepsremoved : %d \n", ptpClock->stepsRemoved); DBGV("Offset from master : \n"); timeInternal_display(&ptpClock->offsetFromMaster); DBGV("Mean path delay : \n"); timeInternal_display(&ptpClock->meanPathDelay); DBGV("\n"); } /**\brief Display Parent data set of a PtpClock*/ void displayParent(PtpClock * ptpClock) { DBGV("---Ptp Clock Parent Data Set-- \n"); DBGV("\n"); portIdentity_display(&(ptpClock->parentPortIdentity)); DBGV("parentStats : %d \n", ptpClock->parentStats); DBGV("observedParentOffsetScaledLogVariance : %d \n", ptpClock->observedParentOffsetScaledLogVariance); DBGV("observedParentClockPhaseChangeRate : %d \n", ptpClock->observedParentClockPhaseChangeRate); DBGV("--GrandMaster--\n"); clockIdentity_display(ptpClock->grandmasterIdentity); clockQuality_display(&ptpClock->grandmasterClockQuality); DBGV("grandmasterpriority1 : %d \n", ptpClock->grandmasterPriority1); DBGV("grandmasterpriority2 : %d \n", ptpClock->grandmasterPriority2); DBGV("\n"); } /**\brief Display Global data set of a PtpClock*/ void displayGlobal(PtpClock * ptpClock) { DBGV("---Ptp Clock Global Time Data Set-- \n"); DBGV("\n"); DBGV("currentUtcOffset : %d \n", ptpClock->currentUtcOffset); DBGV("currentUtcOffsetValid : %d \n", ptpClock->currentUtcOffsetValid); DBGV("leap59 : %d \n", ptpClock->leap59); DBGV("leap61 : %d \n", ptpClock->leap61); DBGV("timeTraceable : %d \n", ptpClock->timeTraceable); DBGV("frequencyTraceable : %d \n", ptpClock->frequencyTraceable); DBGV("ptpTimescale : %d \n", ptpClock->ptpTimescale); DBGV("timeSource : %d \n", ptpClock->timeSource); DBGV("\n"); } /**\brief Display Port data set of a PtpClock*/ void displayPort(PtpClock * ptpClock) { DBGV("---Ptp Clock Port Data Set-- \n"); DBGV("\n"); portIdentity_display(&ptpClock->portIdentity); DBGV("port state : %d \n", ptpClock->portState); DBGV("logMinDelayReqInterval : %d \n", ptpClock->logMinDelayReqInterval); DBGV("peerMeanPathDelay : \n"); timeInternal_display(&ptpClock->peerMeanPathDelay); DBGV("logAnnounceInterval : %d \n", ptpClock->logAnnounceInterval); DBGV("announceReceiptTimeout : %d \n", ptpClock->announceReceiptTimeout); DBGV("logSyncInterval : %d \n", ptpClock->logSyncInterval); DBGV("delayMechanism : %d \n", ptpClock->delayMechanism); DBGV("logMinPdelayReqInterval : %d \n", ptpClock->logMinPdelayReqInterval); DBGV("versionNumber : %d \n", ptpClock->versionNumber); DBGV("\n"); } /**\brief Display ForeignMaster data set of a PtpClock*/ void displayForeignMaster(PtpClock * ptpClock) { ForeignMasterRecord *foreign; int i; if (ptpClock->number_foreign_records > 0) { DBGV("---Ptp Clock Foreign Data Set-- \n"); DBGV("\n"); DBGV("There is %d Foreign master Recorded \n", ptpClock->number_foreign_records); foreign = ptpClock->foreign; for (i = 0; i < ptpClock->number_foreign_records; i++) { portIdentity_display(&foreign->foreignMasterPortIdentity); DBGV("number of Announce message received : %d \n", foreign->foreignMasterAnnounceMessages); msgHeader_display(&foreign->header); msgAnnounce_display(&foreign->announce); foreign++; } } else { DBGV("No Foreign masters recorded \n"); } DBGV("\n"); } /**\brief Display other data set of a PtpClock*/ void displayOthers(PtpClock * ptpClock) { int i; /* Usefull to display name of timers */ #ifdef PTPD_DBGV char timer[][26] = { "PDELAYREQ_INTERVAL_TIMER", "SYNC_INTERVAL_TIMER", "ANNOUNCE_RECEIPT_TIMER", "ANNOUNCE_INTERVAL_TIMER" }; #endif DBGV("---Ptp Others Data Set-- \n"); DBGV("\n"); /*DBGV("master_to_slave_delay : \n"); timeInternal_display(&ptpClock->master_to_slave_delay); DBGV("\n"); DBGV("slave_to_master_delay : \n"); timeInternal_display(&ptpClock->slave_to_master_delay); */ DBGV("\n"); DBGV("delay_req_receive_time : \n"); timeInternal_display(&ptpClock->pdelay_req_receive_time); DBGV("\n"); DBGV("delay_req_send_time : \n"); timeInternal_display(&ptpClock->pdelay_req_send_time); DBGV("\n"); DBGV("delay_resp_receive_time : \n"); timeInternal_display(&ptpClock->pdelay_resp_receive_time); DBGV("\n"); DBGV("delay_resp_send_time : \n"); timeInternal_display(&ptpClock->pdelay_resp_send_time); DBGV("\n"); DBGV("sync_receive_time : \n"); timeInternal_display(&ptpClock->sync_receive_time); DBGV("\n"); //DBGV("R : %f \n", ptpClock->R); DBGV("sentPdelayReq : %d \n", ptpClock->sentPDelayReq); DBGV("sentPDelayReqSequenceId : %d \n", ptpClock->sentPDelayReqSequenceId); DBGV("waitingForFollow : %d \n", ptpClock->waitingForFollow); DBGV("\n"); DBGV("Offset from master filter : \n"); DBGV("nsec_prev : %d \n", ptpClock->ofm_filt.nsec_prev); DBGV("y : %d \n", ptpClock->ofm_filt.y); DBGV("\n"); DBGV("One way delay filter : \n"); DBGV("nsec_prev : %d \n", ptpClock->owd_filt.nsec_prev); 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("message activity %d \n", ptpClock->message_activity); DBGV("\n"); for (i = 0; i < TIMER_ARRAY_SIZE; i++) { DBGV("%s : \n", timer[i]); intervalTimer_display(&ptpClock->itimer[i]); DBGV("\n"); } netPath_display(&ptpClock->netPath); DBGV("mCommunication technology %d \n", ptpClock->port_communication_technology); clockUUID_display(ptpClock->port_uuid_field); DBGV("\n"); } /**\brief Display Buffer in & out of a PtpClock*/ void displayBuffer(PtpClock * ptpClock) { int i; int j; j = 0; DBGV("PtpClock Buffer Out \n"); DBGV("\n"); for (i = 0; i < PACKET_SIZE; i++) { DBGV(":%02hhx", ptpClock->msgObuf[i]); j++; if (j == 8) { DBGV(" "); } if (j == 16) { DBGV("\n"); j = 0; } } DBGV("\n"); j = 0; DBGV("\n"); DBGV("PtpClock Buffer In \n"); DBGV("\n"); for (i = 0; i < PACKET_SIZE; i++) { DBGV(":%02hhx", ptpClock->msgIbuf[i]); j++; if (j == 8) { DBGV(" "); } if (j == 16) { DBGV("\n"); j = 0; } } DBGV("\n"); DBGV("\n"); } /**\brief Display All data set of a PtpClock*/ void displayPtpClock(PtpClock * ptpClock) { displayDefault(ptpClock); displayCurrent(ptpClock); displayParent(ptpClock); displayGlobal(ptpClock); displayPort(ptpClock); displayForeignMaster(ptpClock); displayBuffer(ptpClock); displayOthers(ptpClock); } ptpd-2.2.2/src/datatypes.h0000600000175000017500000004213011770426154014407 0ustar ernieernie#ifndef DATATYPES_H_ #define DATATYPES_H_ #include /*Struct defined in spec*/ /** *\file * \brief Main structures used in ptpdv2 * * 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 { /* see src/def/README for a note on this X-macro */ #define OPERATE( name, size, type ) type name; #include "def/derivedData/timeInterval.def" } TimeInterval; /** * \brief The Timestamp type represents a positive time with respect to the epoch */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/derivedData/timestamp.def" } Timestamp; /** * \brief The ClockIdentity type identifies a clock */ typedef Octet ClockIdentity[CLOCK_IDENTITY_LENGTH]; /** * \brief The PortIdentity identifies a PTP port. */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/derivedData/portIdentity.def" } PortIdentity; /** * \brief The PortAdress type represents the protocol address of a PTP port */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/derivedData/portAddress.def" } PortAddress; /** * \brief The ClockQuality represents the quality of a clock */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/derivedData/clockQuality.def" } ClockQuality; /** * \brief The TLV type represents TLV extension fields */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/derivedData/tlv.def" } TLV; /** * \brief The PTPText data type is used to represent textual material in PTP messages */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/derivedData/ptpText.def" } PTPText; /** * \brief The FaultRecord type is used to construct fault logs */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/derivedData/faultRecord.def" } FaultRecord; /** * \brief The PhysicalAddress type is used to represent a physical address */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/derivedData/physicalAddress.def" } PhysicalAddress; /** * \brief The common header for all PTP messages (Table 18 of the spec) */ /* Message header */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/message/header.def" } 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; }MsgAnnounce; /** * \brief Sync message fields (Table 26 of the spec) */ /*Sync Message */ typedef struct { Timestamp originTimestamp; }MsgSync; /** * \brief DelayReq message fields (Table 26 of the spec) */ /*DelayReq Message */ typedef struct { Timestamp originTimestamp; }MsgDelayReq; /** * \brief DelayResp message fields (Table 30 of the spec) */ /*delayResp Message*/ typedef struct { Timestamp receiveTimestamp; PortIdentity requestingPortIdentity; }MsgDelayResp; /** * \brief FollowUp message fields (Table 27 of the spec) */ /*Follow-up Message*/ typedef struct { Timestamp preciseOriginTimestamp; }MsgFollowUp; /** * \brief PDelayReq message fields (Table 29 of the spec) */ /*PdelayReq Message*/ typedef struct { Timestamp originTimestamp; }MsgPDelayReq; /** * \brief PDelayResp message fields (Table 30 of the spec) */ /*PdelayResp Message*/ typedef struct { Timestamp requestReceiptTimestamp; PortIdentity requestingPortIdentity; }MsgPDelayResp; /** * \brief PDelayRespFollowUp message fields (Table 31 of the spec) */ /*PdelayRespFollowUp Message*/ typedef struct { Timestamp responseOriginTimestamp; PortIdentity requestingPortIdentity; }MsgPDelayRespFollowUp; /** * \brief Signaling message fields (Table 33 of the spec) */ /*Signaling Message*/ typedef struct { PortIdentity targetPortIdentity; char* tlv; }MsgSignaling; /** * \brief Management TLV message fields */ /* Management TLV Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/managementTLV.def" Octet* dataField; } ManagementTLV; /** * \brief Management TLV Clock Description fields (Table 41 of the spec) */ /* Management TLV Clock Description Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/clockDescription.def" } MMClockDescription; /** * \brief Management TLV User Description fields (Table 43 of the spec) */ /* Management TLV User Description Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/userDescription.def" } MMUserDescription; /** * \brief Management TLV Initialize fields (Table 44 of the spec) */ /* Management TLV Initialize Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/initialize.def" } MMInitialize; /** * \brief Management TLV Default Data Set fields (Table 50 of the spec) */ /* Management TLV Default Data Set Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/defaultDataSet.def" } MMDefaultDataSet; /** * \brief Management TLV Current Data Set fields (Table 55 of the spec) */ /* Management TLV Current Data Set Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/currentDataSet.def" } MMCurrentDataSet; /** * \brief Management TLV Parent Data Set fields (Table 56 of the spec) */ /* Management TLV Parent Data Set Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/parentDataSet.def" } MMParentDataSet; /** * \brief Management TLV Time Properties Data Set fields (Table 57 of the spec) */ /* Management TLV Time Properties Data Set Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/timePropertiesDataSet.def" } MMTimePropertiesDataSet; /** * \brief Management TLV Port Data Set fields (Table 61 of the spec) */ /* Management TLV Port Data Set Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/portDataSet.def" } MMPortDataSet; /** * \brief Management TLV Priority1 fields (Table 51 of the spec) */ /* Management TLV Priority1 Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/priority1.def" } MMPriority1; /** * \brief Management TLV Priority2 fields (Table 52 of the spec) */ /* Management TLV Priority2 Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/priority2.def" } MMPriority2; /** * \brief Management TLV Domain fields (Table 53 of the spec) */ /* Management TLV Domain Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/domain.def" } MMDomain; /** * \brief Management TLV Slave Only fields (Table 54 of the spec) */ /* Management TLV Slave Only Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/slaveOnly.def" } MMSlaveOnly; /** * \brief Management TLV Log Announce Interval fields (Table 62 of the spec) */ /* Management TLV Log Announce Interval Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/logAnnounceInterval.def" } MMLogAnnounceInterval; /** * \brief Management TLV Announce Receipt Timeout fields (Table 63 of the spec) */ /* Management TLV Announce Receipt Timeout Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/announceReceiptTimeout.def" } MMAnnounceReceiptTimeout; /** * \brief Management TLV Log Sync Interval fields (Table 64 of the spec) */ /* Management TLV Log Sync Interval Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/logSyncInterval.def" } MMLogSyncInterval; /** * \brief Management TLV Version Number fields (Table 67 of the spec) */ /* Management TLV Version Number Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/versionNumber.def" } MMVersionNumber; /** * \brief Management TLV Time fields (Table 48 of the spec) */ /* Management TLV Time Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/time.def" } MMTime; /** * \brief Management TLV Clock Accuracy fields (Table 49 of the spec) */ /* Management TLV Clock Accuracy Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/clockAccuracy.def" } MMClockAccuracy; /** * \brief Management TLV UTC Properties fields (Table 58 of the spec) */ /* Management TLV UTC Properties Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/utcProperties.def" } MMUtcProperties; /** * \brief Management TLV Traceability Properties fields (Table 59 of the spec) */ /* Management TLV Traceability Properties Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/traceabilityProperties.def" } MMTraceabilityProperties; /** * \brief Management TLV Delay Mechanism fields (Table 65 of the spec) */ /* Management TLV Delay Mechanism Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/delayMechanism.def" } MMDelayMechanism; /** * \brief Management TLV Log Min Pdelay Req Interval fields (Table 66 of the spec) */ /* Management TLV Log Min Pdelay Req Interval Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/logMinPdelayReqInterval.def" } MMLogMinPdelayReqInterval; /** * \brief Management TLV Error Status fields (Table 71 of the spec) */ /* Management TLV Error Status Message */ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/managementTLV/errorStatus.def" } MMErrorStatus; /** * \brief Management message fields (Table 37 of the spec) */ /*management Message*/ typedef struct { #define OPERATE( name, size, type ) type name; #include "def/message/management.def" ManagementTLV* 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; } 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; /** * \struct PtpClock * \brief Main program data structure */ /* main program data structure */ typedef struct { /* Default data set */ /*Static members*/ Boolean twoStepFlag; ClockIdentity clockIdentity; UInteger16 numberPorts; /*Dynamic members*/ ClockQuality clockQuality; /*Configurable members*/ UInteger8 priority1; UInteger8 priority2; UInteger8 domainNumber; Boolean slaveOnly; /* Current data set */ /*Dynamic members*/ UInteger16 stepsRemoved; TimeInternal offsetFromMaster; TimeInternal meanPathDelay; /* Parent data set */ /*Dynamic members*/ PortIdentity parentPortIdentity; Boolean parentStats; UInteger16 observedParentOffsetScaledLogVariance; Integer32 observedParentClockPhaseChangeRate; ClockIdentity grandmasterIdentity; ClockQuality grandmasterClockQuality; UInteger8 grandmasterPriority1; UInteger8 grandmasterPriority2; /* Global time properties data set */ /*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 */ /*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; /* 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; MsgManagement outgoingManageTmp; 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; */ Integer32 observed_drift; 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; Boolean sentPDelayReq; UInteger16 sentPDelayReqSequenceId; UInteger16 sentDelayReqSequenceId; UInteger16 sentSyncSequenceId; UInteger16 sentAnnounceSequenceId; UInteger16 recvPDelayReqSequenceId; UInteger16 recvSyncSequenceId; UInteger16 recvPDelayRespSequenceId; Boolean waitingForFollow; Boolean waitingForDelayResp; offset_from_master_filter ofm_filt; one_way_delay_filter owd_filt; Boolean message_activity; IntervalTimer itimer[TIMER_ARRAY_SIZE]; NetPath netPath; /*Usefull to init network stuff*/ UInteger8 port_communication_technology; Octet port_uuid_field[PTP_UUID_LENGTH]; int reset_count; int current_init_clock; int can_step_clock; 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 startup_in_progress; /* user description is max size + 1 to leave space for a null terminator */ Octet user_description[USER_DESCRIPTION_MAX + 1]; #ifdef PTP_EXPERIMENTAL Integer32 MasterAddr; // used for hybrid mode, when receiving announces Integer32 LastSlaveAddr; // used for hybrid mode, when receiving delayreqs #endif Integer32 discardedPacketCount; // used for autotuning the maxDelay threshold } PtpClock; /** * \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; #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 */ Integer32 origMaxDelay; /* Lower bound of nanoseconds of delay */ Boolean maxDelayAutoTune; Integer32 discardedPacketThreshold; Boolean noAdjust; Boolean displayStats; 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; 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_delayreq_interval_master; 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 } RunTimeOpts; #endif /*DATATYPES_H_*/ ptpd-2.2.2/src/arith.c0000600000175000017500000001661711770426154013526 0ustar ernieernie/*- * Copyright (c) 2011-2012 George V. Neville-Neil, * Steven Kreuzer, * Martin Burnicki, * Jan Breuer, * Gael Mace, * Alexandre Van Kempen, * Inaqui Delgado, * Rick Ratzel, * National Instruments. * Copyright (c) 2009-2010 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. */ /** * @file arith.c * @date Tue Jul 20 16:12:51 2010 * * @brief Time format conversion routines and additional math functions. * * */ #include "ptpd.h" double round (double __x); void internalTime_to_integer64(TimeInternal internal, Integer64 *bigint) { /* TODO: implement me */ } void integer64_to_internalTime(Integer64 bigint, TimeInternal * internal) { int sign; int64_t scaledNanoseconds; scaledNanoseconds = bigint.msb; scaledNanoseconds <<=32; scaledNanoseconds += bigint.lsb; /*determine sign of result big integer number*/ if (scaledNanoseconds < 0) { scaledNanoseconds = -scaledNanoseconds; sign = -1; } else { sign = 1; } /*fractional nanoseconds are excluded (see 5.3.2)*/ scaledNanoseconds >>= 16; internal->seconds = sign * (scaledNanoseconds / 1000000000); internal->nanoseconds = sign * (scaledNanoseconds % 1000000000); } void fromInternalTime(TimeInternal * internal, Timestamp * external) { /* * fromInternalTime is only used to convert time given by the system * to a timestamp As a consequence, no negative value can normally * be found in (internal) * * Note that offsets are also represented with TimeInternal structure, * and can be negative, but offset are never convert into Timestamp * so there is no problem here. */ if ((internal->seconds & ~INT_MAX) || (internal->nanoseconds & ~INT_MAX)) { DBG("Negative value canno't be converted into timestamp \n"); return; } else { external->secondsField.lsb = internal->seconds; external->nanosecondsField = internal->nanoseconds; external->secondsField.msb = 0; } } void toInternalTime(TimeInternal * internal, Timestamp * external) { /* Program will not run after 2038... */ if (external->secondsField.lsb < INT_MAX) { internal->seconds = external->secondsField.lsb; internal->nanoseconds = external->nanosecondsField; } else { DBG("Clock servo canno't be executed : " "seconds field is higher than signed integer (32bits) \n"); return; } } void ts_to_InternalTime(struct timespec *a, TimeInternal * b) { b->seconds = a->tv_sec; b->nanoseconds = a->tv_nsec; } void tv_to_InternalTime(struct timeval *a, TimeInternal * b) { b->seconds = a->tv_sec; b->nanoseconds = a->tv_usec * 1000; } void normalizeTime(TimeInternal * r) { r->seconds += r->nanoseconds / 1000000000; r->nanoseconds -= r->nanoseconds / 1000000000 * 1000000000; if (r->seconds > 0 && r->nanoseconds < 0) { r->seconds -= 1; r->nanoseconds += 1000000000; } else if (r->seconds < 0 && r->nanoseconds > 0) { r->seconds += 1; r->nanoseconds -= 1000000000; } } void addTime(TimeInternal * r, const TimeInternal * x, const TimeInternal * y) { r->seconds = x->seconds + y->seconds; r->nanoseconds = x->nanoseconds + y->nanoseconds; normalizeTime(r); } void subTime(TimeInternal * r, const TimeInternal * x, const TimeInternal * y) { r->seconds = x->seconds - y->seconds; r->nanoseconds = x->nanoseconds - y->nanoseconds; normalizeTime(r); } /// Divide an internal time value /// /// @param r the time to convert /// @param divisor /// #if 0 /* TODO: this function could be simplified, as currently it is only called to halve the time */ void divTime(TimeInternal *r, int divisor) { uint64_t nanoseconds; if (divisor <= 0) return; nanoseconds = ((uint64_t)r->seconds * 1000000000) + r->nanoseconds; nanoseconds /= divisor; r->seconds = 0; r->nanoseconds = nanoseconds; normalizeTime(r); } #endif void div2Time(TimeInternal *r) { r->nanoseconds += r->seconds % 2 * 1000000000; r->seconds /= 2; r->nanoseconds /= 2; normalizeTime(r); } /* clear an internal time value */ void clearTime(TimeInternal *time) { time->seconds = 0; time->nanoseconds = 0; } /* sets a time value to a certain nanoseconds */ void nano_to_Time(TimeInternal *time, int nano) { time->seconds = 0; time->nanoseconds = nano; normalizeTime(time); } /* greater than operation */ int gtTime(TimeInternal *x, TimeInternal *y) { TimeInternal r; subTime(&r, x, y); return !isTimeInternalNegative(&r); } /* remove sign from variable */ void absTime(TimeInternal *time) { time->seconds = abs(time->seconds); time->nanoseconds = abs(time->nanoseconds); } /* if 2 time values are close enough for X nanoseconds */ int is_Time_close(TimeInternal *x, TimeInternal *y, int nanos) { TimeInternal r1; TimeInternal r2; // first, subtract the 2 values. then call abs(), then call gtTime for requested the number of nanoseconds subTime(&r1, x, y); absTime(&r1); nano_to_Time(&r2, nanos); return !gtTime(&r1, &r2); } int check_timestamp_is_fresh2(TimeInternal * timeA, TimeInternal * timeB) { int ret; ret = is_Time_close(timeA, timeB, 1000000); // maximum 1 millisecond offset DBG2("check_timestamp_is_fresh: %d\n ", ret); return ret; } int check_timestamp_is_fresh(TimeInternal * timeA) { TimeInternal timeB; getTime(&timeB); return check_timestamp_is_fresh2(timeA, &timeB); } int isTimeInternalNegative(const TimeInternal * p) { return (p->seconds < 0) || (p->nanoseconds < 0); } float secondsToMidnight(void) { TimeInternal now; getTime(&now); Integer32 stmI = (now.seconds - (now.seconds % 86400) + 86400) - now.seconds; return (stmI + 0.0 - now.nanoseconds / 1E9); } 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); } ptpd-2.2.2/src/constants.h0000600000175000017500000002266711770426154014442 0ustar ernieernie#ifndef CONSTANTS_H_ #define CONSTANTS_H_ /** *\file * \brief Default values and constants used in ptpdv2 * * This header file includes all default values used during initialization * and enumeration defined in the spec */ #define PTPD_PROGNAME "ptpd2" /* FIXME: make these parameterized, either through command-line options or make variables */ #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" #define MANUFACTURER_ID_OUI0 \ 0xFF #define MANUFACTURER_ID_OUI1 \ 0xFF #define MANUFACTURER_ID_OUI2 \ 0xFF #define PROTOCOL \ "IEEE 802.3" #define PRODUCT_DESCRIPTION \ ";;" #define REVISION \ ";;2.2" #define USER_DESCRIPTION \ "PTPDv2" #define USER_DESCRIPTION_MAX 128 /* implementation specific constants */ #define DEFAULT_INBOUND_LATENCY 0 /* in nsec */ #define DEFAULT_OUTBOUND_LATENCY 0 /* in nsec */ #define DEFAULT_NO_RESET_CLOCK FALSE #define DEFAULT_DOMAIN_NUMBER 0 #define DEFAULT_DELAY_MECHANISM E2E // TODO #define DEFAULT_AP 10 #define DEFAULT_AI 1000 #define DEFAULT_DELAY_S 6 #define DEFAULT_ANNOUNCE_INTERVAL 1 /* 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_DELAYREQ_INTERVAL 0 /* new value from page 237 of the standard */ #define DEFAULT_SYNC_INTERVAL 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 */ #define DEFAULT_QUALIFICATION_TIMEOUT 2 #define DEFAULT_FOREIGN_MASTER_TIME_WINDOW 4 #define DEFAULT_FOREIGN_MASTER_THRESHOLD 2 /* 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. */ #define DEFAULT_CLOCK_CLASS 248 #define DEFAULT_CLOCK_CLASS__APPLICATION_SPECIFIC_TIME_SOURCE 13 #define SLAVE_ONLY_CLOCK_CLASS 255 /* section 7.6.2.5, page 56: 0x20 Time accurate to 25ns ... 0x31 Time accurate to > 10s 0xFE Unkown accuracy */ #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 */ /* 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_MAX_FOREIGN_RECORDS 5 #define DEFAULT_PARENTS_STATS FALSE /* features, only change to refelect changes in implementation */ #define NUMBER_PORTS 1 #define VERSION_PTP 2 #define TWO_STEP_FLAG TRUE #define BOUNDARY_CLOCK FALSE #define SLAVE_ONLY FALSE #define NO_ADJUST FALSE /** \name Packet length Minimal length values for each message. If TLV used length could be higher.*/ /**\{*/ #define HEADER_LENGTH 34 #define ANNOUNCE_LENGTH 64 #define SYNC_LENGTH 44 #define FOLLOW_UP_LENGTH 44 #define PDELAY_REQ_LENGTH 54 #define DELAY_REQ_LENGTH 44 #define DELAY_RESP_LENGTH 54 #define PDELAY_RESP_LENGTH 54 #define PDELAY_RESP_FOLLOW_UP_LENGTH 54 #define MANAGEMENT_LENGTH 48 #define TLV_LENGTH 6 /** \}*/ /*Enumeration defined in tables of the spec*/ /** * \brief Domain Number (Table 2 in the spec)*/ enum { DFLT_DOMAIN_NUMBER = 0, ALT1_DOMAIN_NUMBER, ALT2_DOMAIN_NUMBER, ALT3_DOMAIN_NUMBER }; /** * \brief Network Protocol (Table 3 in the spec)*/ enum { UDP_IPV4=1,UDP_IPV6,IEE_802_3,DeviceNet,ControlNet,PROFINET }; /** * \brief Time Source (Table 7 in the spec)*/ enum { ATOMIC_CLOCK=0x10,GPS=0x20,TERRESTRIAL_RADIO=0x30,PTP=0x40,NTP=0x50,HAND_SET=0x60,OTHER=0x90,INTERNAL_OSCILLATOR=0xA0 }; /** * \brief Delay mechanism (Table 9 in the spec)*/ enum { E2E=1,P2P=2,DELAY_DISABLED=0xFE }; /** * \brief PTP timers */ enum { PDELAYREQ_INTERVAL_TIMER=0,/**<\brief Timer handling the PdelayReq Interval*/ DELAYREQ_INTERVAL_TIMER,/**<\brief Timer handling the delayReq Interva*/ SYNC_INTERVAL_TIMER,/**<\brief Timer handling Interval between master sends two Syncs messages */ ANNOUNCE_RECEIPT_TIMER,/**<\brief Timer handling announce receipt timeout*/ ANNOUNCE_INTERVAL_TIMER, /**<\brief Timer handling interval before master sends two announce messages*/ /* non-spec timers */ OPERATOR_MESSAGES_TIMER, /* used to limit the operator messages */ LEAP_SECOND_PAUSE_TIMER, /* timer used for pausing updates when leap second is imminent */ TIMER_ARRAY_SIZE }; /** * \brief PTP Management Message managementId values (Table 40 in the spec) */ /* SLAVE_ONLY conflicts with another constant, so scope with MM_ */ enum { /* Applicable to all node types */ MM_NULL_MANAGEMENT=0x0000, MM_CLOCK_DESCRIPTION=0x0001, MM_USER_DESCRIPTION=0x0002, MM_SAVE_IN_NON_VOLATILE_STORAGE=0x0003, MM_RESET_NON_VOLATILE_STORAGE=0x0004, MM_INITIALIZE=0x0005, MM_FAULT_LOG=0x0006, MM_FAULT_LOG_RESET=0x0007, /* Reserved: 0x0008 - 0x1FFF */ /* Applicable to ordinary and boundary clocks */ MM_DEFAULT_DATA_SET=0x2000, MM_CURRENT_DATA_SET=0x2001, MM_PARENT_DATA_SET=0x2002, MM_TIME_PROPERTIES_DATA_SET=0x2003, MM_PORT_DATA_SET=0x2004, MM_PRIORITY1=0x2005, MM_PRIORITY2=0x2006, MM_DOMAIN=0x2007, MM_SLAVE_ONLY=0x2008, MM_LOG_ANNOUNCE_INTERVAL=0x2009, MM_ANNOUNCE_RECEIPT_TIMEOUT=0x200A, MM_LOG_SYNC_INTERVAL=0x200B, MM_VERSION_NUMBER=0x200C, MM_ENABLE_PORT=0x200D, MM_DISABLE_PORT=0x200E, MM_TIME=0x200F, MM_CLOCK_ACCURACY=0x2010, MM_UTC_PROPERTIES=0x2011, MM_TRACEABILITY_PROPERTIES=0x2012, MM_TIMESCALE_PROPERTIES=0x2013, MM_UNICAST_NEGOTIATION_ENABLE=0x2014, MM_PATH_TRACE_LIST=0x2015, MM_PATH_TRACE_ENABLE=0x2016, MM_GRANDMASTER_CLUSTER_TABLE=0x2017, MM_UNICAST_MASTER_TABLE=0x2018, MM_UNICAST_MASTER_MAX_TABLE_SIZE=0x2019, MM_ACCEPTABLE_MASTER_TABLE=0x201A, MM_ACCEPTABLE_MASTER_TABLE_ENABLED=0x201B, MM_ACCEPTABLE_MASTER_MAX_TABLE_SIZE=0x201C, MM_ALTERNATE_MASTER=0x201D, MM_ALTERNATE_TIME_OFFSET_ENABLE=0x201E, MM_ALTERNATE_TIME_OFFSET_NAME=0x201F, MM_ALTERNATE_TIME_OFFSET_MAX_KEY=0x2020, MM_ALTERNATE_TIME_OFFSET_PROPERTIES=0x2021, /* Reserved: 0x2022 - 0x3FFF */ /* Applicable to transparent clocks */ MM_TRANSPARENT_CLOCK_DEFAULT_DATA_SET=0x4000, MM_TRANSPARENT_CLOCK_PORT_DATA_SET=0x4001, MM_PRIMARY_DOMAIN=0x4002, /* Reserved: 0x4003 - 0x5FFF */ /* Applicable to ordinary, boundary, and transparent clocks */ MM_DELAY_MECHANISM=0x6000, MM_LOG_MIN_PDELAY_REQ_INTERVAL=0x6001, /* Reserved: 0x6002 - 0xBFFF */ /* Implementation-specific identifiers: 0xC000 - 0xDFFF */ /* Assigned by alternate PTP profile: 0xE000 - 0xFFFE */ /* Reserved: 0xFFFF */ }; /** * \brief MANAGEMENT MESSAGE INITIALIZE (Table 44 in the spec) */ #define INITIALIZE_EVENT 0x0 /** * \brief MANAGEMENT ERROR STATUS managementErrorId (Table 72 in the spec) */ enum { RESPONSE_TOO_BIG=0x0001, NO_SUCH_ID=0x0002, WRONG_LENGTH=0x0003, WRONG_VALUE=0x0004, NOT_SETABLE=0x0005, NOT_SUPPORTED=0x0006, GENERAL_ERROR=0xFFFE }; /* * \brief PTP tlvType values (Table 34 in the spec) */ enum { /* Standard TLVs */ TLV_MANAGEMENT=0x0001, TLV_MANAGEMENT_ERROR_STATUS=0x0002, TLV_ORGANIZATION_EXTENSION=0x0003, /* Optional unicast message negotiation TLVs */ TLV_REQUEST_UNICAST_TRANSMISSION=0x0004, TLV_GRANT_UNICAST_TRANSMISSION=0x0005, TLV_CANCEL_UNICAST_TRANSMISSION=0x0006, TLV_ACKNOWLEDGE_CANCEL_UNICAST_TRANSMISSION=0x0007, /* Optional path trace mechanism TLV */ TLV_PATH_TRACE=0x0008, /* Optional alternate timescale TLV */ ALTERNATE_TIME_OFFSET_INDICATOR=0x0009, /*Security TLVs */ AUTHENTICATION=0x2000, AUTHENTICATION_CHALLENGE=0x2001, SECURITY_ASSOCIATION_UPDATE=0x2002, /* Cumulative frequency scale factor offset */ CUM_FREQ_SCALE_FACTOR_OFFSET=0x2003 }; /** * \brief Management Message actions (Table 38 in the spec) */ enum { GET=0, SET, RESPONSE, COMMAND, ACKNOWLEDGE }; /** * \brief flagField1 bit position values (Table 20 in the spec) */ enum { LI61=0, LI59, UTCV, PTPT, /* this is referred to as PTP in the spec but already defined above */ TTRA, FTRA }; /** * \brief PTP states */ enum { PTP_INITIALIZING=1, PTP_FAULTY, PTP_DISABLED, PTP_LISTENING, PTP_PRE_MASTER, PTP_MASTER, PTP_PASSIVE, PTP_UNCALIBRATED, PTP_SLAVE }; /** * \brief PTP Messages */ enum { SYNC=0x0, DELAY_REQ, PDELAY_REQ, PDELAY_RESP, FOLLOW_UP=0x8, DELAY_RESP, PDELAY_RESP_FOLLOW_UP, ANNOUNCE, SIGNALING, MANAGEMENT, }; enum { PTP_ETHER, PTP_DEFAULT }; /** * \brief PTP flags */ enum { PTP_ALTERNATE_MASTER = 0x01, PTP_TWO_STEP = 0x02, PTP_UNICAST = 0x04, PTP_PROFILE_SPECIFIC_1 = 0x20, PTP_PROFILE_SPECIFIC_2 = 0x40, PTP_SECURITY = 0x80, }; enum { PTP_LI_61 = 0x01, PTP_LI_59 = 0x02, PTP_UTC_REASONABLE = 0x04, PTP_TIMESCALE = 0x08, TIME_TRACEABLE = 0x10, FREQUENCY_TRACEABLE = 0x20, }; #endif /*CONSTANTS_H_*/ ptpd-2.2.2/doc/0000700000175000017500000000000012127234277012214 5ustar ernieernieptpd-2.2.2/README0000600000175000017500000000273211770426154012335 0ustar ernieernieREADME for PTPd Version 2 - Introduction - This is the source code for the PTP daemon (PTPd) implementing the Precision Time Protocol (PTP) version 2 as defined by 'IEEE Std 1588-2008'. PTP provides precise time coordination of Ethernet LAN connected computers. It was designed primarily for instrumentation and control systems. - Use - PTPd can coordinate the clocks of a group of LAN connected computers with each other. It has been shown to achieve microsecond level coordination, even on limited platforms. The 'ptpd' program can be built from the included source code; see the release notes about platform compatibility. To use the program, run 'ptpd' on a group of LAN connected computers. Compile with 'PTPD_DBG' defined and run with the '-c' argument to watch what's going on. If you are just looking for software to update the time on your desktop, you probably want something that implements the Network Time Protocol. It can coordinate computer clocks with an absolute time reference such as UTC. - Legal notice - PTPd was written by using only information contained within 'IEEE Std 1588-2008'. IEEE 1588 may contain patented technology, the use of which is not under the control of the authors of PTPd. Users of IEEE 1588 may need to obtain a license for the patented technology in the protocol. Contact the IEEE for licensing information. PTPd is licenced under a 2 Clause BSD Open Source License see COPYRIGHT for full information. PTPd comes with absolutely no warranty.