From 9ff8bc910087fd1ee2e636b3efbb47a576f7109e38060e60f50bf985a74118ae Mon Sep 17 00:00:00 2001 From: Stephan Kulow Date: Fri, 28 Oct 2011 17:42:28 +0000 Subject: [PATCH] Accepting request 89582 from network:time OBS-URL: https://build.opensuse.org/request/show/89582 OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/ptpd?expand=0&rev=13 --- append_to_cflags.patch | 11 - conf.ptpd.init | 2 +- glibc210.patch | 11 - ptpd-1.0.0-git599b03b.patch | 5794 ----------------------------------- ptpd-1.0.0.tar.bz2 | 3 - ptpd-1.1.0.tar.gz | 3 + ptpd.changes | 21 + ptpd.spec | 27 +- 8 files changed, 33 insertions(+), 5839 deletions(-) delete mode 100644 append_to_cflags.patch delete mode 100644 glibc210.patch delete mode 100644 ptpd-1.0.0-git599b03b.patch delete mode 100644 ptpd-1.0.0.tar.bz2 create mode 100644 ptpd-1.1.0.tar.gz diff --git a/append_to_cflags.patch b/append_to_cflags.patch deleted file mode 100644 index 19c908a..0000000 --- a/append_to_cflags.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- src/Makefile -+++ src/Makefile -@@ -1,7 +1,7 @@ - # Makefile for ptpd - - RM = rm -f --CFLAGS = -Wall -+CFLAGS += -Wall - #CPPFLAGS = -DPTPD_DBG -DPTPD_NO_DAEMON - - PROG = ptpd diff --git a/conf.ptpd.init b/conf.ptpd.init index 8fa694e..2c52428 100644 --- a/conf.ptpd.init +++ b/conf.ptpd.init @@ -34,7 +34,7 @@ # Should-Start: $time # Required-Stop: $remote_fs $syslog $network # Should-Stop: $time -# Default-Start: 3 4 5 +# Default-Start: 3 5 # Default-Stop: 0 1 2 6 # Short-Description: Precision Time Protocol as defined by IEEE 1588 # Description: PTP is designed to provide very precise diff --git a/glibc210.patch b/glibc210.patch deleted file mode 100644 index 89ec267..0000000 --- a/glibc210.patch +++ /dev/null @@ -1,11 +0,0 @@ -Index: ptpd-1.0.0/src/arith.c -=================================================================== ---- ptpd-1.0.0.orig/src/arith.c -+++ ptpd-1.0.0/src/arith.c -@@ -1,5 +1,6 @@ - /* arith.c */ - -+#include - #include "ptpd.h" - - /* from annex C of the spec */ diff --git a/ptpd-1.0.0-git599b03b.patch b/ptpd-1.0.0-git599b03b.patch deleted file mode 100644 index c60c941..0000000 --- a/ptpd-1.0.0-git599b03b.patch +++ /dev/null @@ -1,5794 +0,0 @@ -diff --git a/Doxyfile b/Doxyfile -new file mode 100644 -index 0000000..7b41ff5 ---- /dev/null -+++ b/Doxyfile -@@ -0,0 +1,1161 @@ -+# Doxyfile 1.3.9.1 -+ -+# 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 -+#--------------------------------------------------------------------------- -+ -+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -+# by quotes) that should identify the project. -+ -+PROJECT_NAME = PTPd -+ -+# 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 = -+ -+# 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: -+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, -+# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, -+# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, -+# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, -+# Swedish, and Ukrainian. -+ -+OUTPUT_LANGUAGE = English -+ -+# This tag can be used to specify the encoding used in the generated output. -+# The encoding is not always determined by the language that is chosen, -+# but also whether or not the output is meant for Windows or non-Windows users. -+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES -+# forces the Windows encoding (this is the default for the Windows binary), -+# whereas setting the tag to NO uses a Unix-style encoding (the default for -+# all platforms other than Windows). -+ -+USE_WINDOWS_ENCODING = NO -+ -+# 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 the Qt-style comments (thus requiring an -+# explicit @brief command for a brief description. -+ -+JAVADOC_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 DETAILS_AT_TOP tag is set to YES then Doxygen -+# will output the detailed description near the top, like JavaDoc. -+# If set to NO, the detailed description appears after the member -+# documentation. -+ -+DETAILS_AT_TOP = 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 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 -+ -+# 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 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 -+ -+#--------------------------------------------------------------------------- -+# 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 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_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. -+ -+SHOW_DIRECTORIES = YES -+ -+#--------------------------------------------------------------------------- -+# 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 -+ -+# 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. -+ -+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 = src -+ -+# 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 -+ -+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. -+ -+EXCLUDE_PATTERNS = -+ -+# 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 = YES -+ -+# 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 = NO -+ -+# If the REFERENCED_BY_RELATION tag is set to YES (the default) -+# 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 (the default) -+# then for each documented function all documented entities -+# called/used by that function will be listed. -+ -+REFERENCES_RELATION = 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 = NO -+ -+# 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 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 compressed 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 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 -+ -+# 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 -+ -+# If the GENERATE_TREEVIEW tag is set to YES, 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. -+ -+GENERATE_TREEVIEW = NO -+ -+# 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 -+ -+#--------------------------------------------------------------------------- -+# 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 = YES -+ -+# 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 = NO -+ -+# 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 = NO -+ -+# 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 = NO -+ -+# 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_PREDEFINED 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 -+ -+# 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 = NO -+ -+# 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 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 tags 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 = NO -+ -+# 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 -+ -+# 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 on 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 MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width -+# (in pixels) of the graphs generated by dot. If a graph becomes larger than -+# this value, doxygen will try to truncate the graph, so that it fits within -+# the specified constraint. Beware that most browsers cannot cope with very -+# large images. -+ -+MAX_DOT_GRAPH_WIDTH = 1024 -+ -+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height -+# (in pixels) of the graphs generated by dot. If a graph becomes larger than -+# this value, doxygen will try to truncate the graph, so that it fits within -+# the specified constraint. Beware that most browsers cannot cope with very -+# large images. -+ -+MAX_DOT_GRAPH_HEIGHT = 1024 -+ -+# 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 a graph may be further truncated if the graph's image dimensions are -+# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). -+# If 0 is used for the depth value (the default), the graph is not depth-constrained. -+ -+MAX_DOT_GRAPH_DEPTH = 0 -+ -+# 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 -+ -+#--------------------------------------------------------------------------- -+# Configuration::additions 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 = NO -diff --git a/INSTALL b/INSTALL -new file mode 100644 -index 0000000..004396c ---- /dev/null -+++ b/INSTALL -@@ -0,0 +1,13 @@ -+Run make inside the src directory to compile the ptpd binary. -+ -+You can influence the compilation by setting variables on the -+make command line: -+ CPPFLAGS=-DPTPD_DBGV -+ detailed debugging -+ CPPFLAGS=-DPTPD_DBG -+ less detailed debugging -+ CPPFLAGS="-DHAVE_LINUX_NET_TSTAMP_H -Idep/include" -+ enable -z linux_hw/linux_sw mode, using bundled -+ Linux header file -+ -+The resulting binary works as-is without having to install it. -diff --git a/extras/Makefile b/extras/Makefile -new file mode 100644 -index 0000000..bc101e7 ---- /dev/null -+++ b/extras/Makefile -@@ -0,0 +1,5 @@ -+MPICC = mpicc -+CFLAGS = -g -O2 -+ -+timertest: timertest.c -+ $(MPICC) $(CFLAGS) -lelf -lm -lrt $< -o $@ -\ No newline at end of file -diff --git a/extras/timertest.c b/extras/timertest.c -new file mode 100644 -index 0000000..e57a409 ---- /dev/null -+++ b/extras/timertest.c -@@ -0,0 +1,779 @@ -+/******************************************************************* -+ * -+ * Copyright (c) 2006-2008, Intel Corporation -+ * 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 the Intel Corporation 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 Intel Corporation ''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 Intel Corporation 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. -+ * -+ *******************************************************************/ -+ -+/** -+ * This program tests the quality of system time (resolution and -+ * distribution of increments) and of synchronization between different -+ * nodes in a cluster. Synchronization is tested by having all processes -+ * sleep for a while, then let each pair of processes exchange multiple -+ * messages. Only two processes are active during each message exchange. -+ * -+ * Clock offset calculation: For each message exchange three time stamps -+ * are taken: t_send -> t_middle -> t_recv. Under the assumption that the -+ * message transmission in both direction is equally fast, it follows that -+ * (t_send + t_recv) / 2 = t_middle + offset. To remove noise the exchanges -+ * with the highest (t_recv - t_send) delta are excluded before averaging the -+ * remaining samples. -+ * -+ * Usage: Compile and start as an MPI application with one process per -+ * node. It runs until killed. For unmonitored operation of a specific -+ * duration use Intel MPI's "MPD_TIMEOUT" option. -+ * -+ * Output: Is written to the syslog and stderr. Output starts with -+ * some information about the resolution of the system time call. Then -+ * for each message exchange the process with the smaller rank logs -+ * the clock offset with its peer. -+ * -+ * Analysis: perfbase experiment and input descriptions are provided to -+ * import the output of timertest and PTPd from a syslog file. -+ * -+ * Author: Patrick Ohly -+ */ -+ -+#include -+ -+#ifndef _WIN32 -+# include -+# include -+# include -+#else -+# include -+# include -+# define sleep( sec ) Sleep( 1000 * (sec) ) -+# define popen _popen -+# define pclose _pclose -+# define pclose _pclose -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define MSG_CNT 1000 /**< number of message to be sent back and forth */ -+#define NUM_BINS 11 /**< number of bins for offset histogram */ -+#define LATENCY_TEST 11 /**< number of seconds that each message latency measurement is supposed to run */ -+#define MAX_SAMPLES 10000 /**< maximum number of samples to collect for median/average clock increment */ -+#define CLOCK_DURATION 5 /**< duration of clock increment test in seconds */ -+ -+#define HAVE_LIBELF_H 1 /**< compile in support for finding functions in virtual dynamic shared object (VDSO); -+ this is necessary because on Intel 64 that VDSO was added recently in 2.6.23d-rc1 -+ and glibc will not yet use it by itself */ -+ -+#ifndef _WIN32 -+typedef int (*clock_gettime_t)(clockid_t clock_id, struct timespec *tp); -+static clock_gettime_t my_clock_gettime = clock_gettime; -+ -+typedef int (*gettimeofday_t)(struct timeval *tp, void *tzp); -+static gettimeofday_t my_gettimeofday = (void *)gettimeofday; -+#endif -+ -+#ifdef HAVE_LIBELF_H -+#include -+/** -+ * return absolute address of dynamic symbol in Linux kernel vdso -+ */ -+static void *findVDSOSym(const char *symname); -+#endif -+ -+/** type used to count seconds */ -+typedef double seconds_t; -+ -+/** type used to count clock ticks */ -+typedef long long ticks_t; -+ -+/** -+ * format number of seconds with ns/us/ms/s suffix (depending on magnitude) -+ * and configurable number of digits before the decimal point (width) and after -+ * it (precision) -+ */ -+static const char *prettyprintseconds( seconds_t seconds, int width, int precision, char *buffer ) -+{ -+ static char localbuffer[80]; -+ seconds_t absseconds = fabs( seconds ); -+ -+ if( !buffer ) { -+ buffer = localbuffer; -+ } -+ -+ if( absseconds < 1e-6 ) { -+ sprintf( buffer, "%*.*fns", width, precision, seconds * 1e9 ); -+ } else if( absseconds < 1e-3 ) { -+ sprintf( buffer, "%*.*fus", width, precision, seconds * 1e6 ); -+ } else if( absseconds < 1 ) { -+ sprintf( buffer, "%*.*fms", width, precision, seconds * 1e3 ); -+ } else { -+ sprintf( buffer, "%*.*fs", width, precision, seconds ); -+ } -+ -+ return buffer; -+} -+ -+/** generates a string of 'width' many hash signs */ -+static const char *printbar( int width ) -+{ -+ static char buffer[80]; -+ -+ if( width > sizeof(buffer) - 1 ) { -+ width = sizeof(buffer) - 1; -+ } -+ memset( buffer, '#', width ); -+ buffer[width] = 0; -+ return buffer; -+} -+ -+/** only on Linux: switch between gettimeofday() and clock_gettime() calls */ -+static int usetod; -+ -+/** returns system time as number of ticks since common epoch */ -+static ticks_t systicks(void) -+{ -+#ifdef _WIN32 -+ struct _timeb timebuffer; -+ _ftime( &timebuffer ); -+ return (ticks_t)timebuffer.time * 1000 + timebuffer.millitm; -+#else -+ if (usetod) { -+ struct timeval cur_time; -+ my_gettimeofday( &cur_time, NULL ); -+ return (ticks_t)cur_time.tv_sec * 1000000 + cur_time.tv_usec; -+ } else { -+ struct timespec cur_time; -+ my_clock_gettime(CLOCK_REALTIME, &cur_time); -+ return (ticks_t)cur_time.tv_sec * 1000000000ul + cur_time.tv_nsec; -+ } -+#endif -+} -+ -+/** duration of one clock tick in seconds */ -+static seconds_t clockperiod; -+ -+/** returns system time as number of seconds since common epoch */ -+static seconds_t systime(void) -+{ -+ ticks_t ticks = systicks(); -+ return ticks * clockperiod; -+} -+ -+/** the result of one ping/pong message exchange */ -+struct sample { -+ seconds_t t_send, t_middle, t_recv; -+}; -+ -+/** offset between nodes given one sample */ -+static seconds_t offset( struct sample *sample ) -+{ -+ return (sample->t_recv + sample->t_send) / 2 - sample->t_middle; -+} -+ -+/** qsort comparison function for sorting by increasing duration of samples */ -+static int compare_duration( const void *a, const void *b ) -+{ -+ const struct sample *sa = a, *sb = b; -+ seconds_t tmp = (sb->t_recv - sb->t_send) - (sa->t_recv - sa->t_send); -+ -+ if( tmp < 0 ) { -+ return -1; -+ } else if( tmp > 0 ) { -+ return 1; -+ } else { -+ return 0; -+ } -+} -+ -+/** -+ * send messages back and forth between two process in MPI_COMM_WORLD -+ * without time stamping -+ */ -+static void simplepingpong( int source, int target, int tag, int num ) -+{ -+ MPI_Status status; -+ int i, rank; -+ char buffer[1]; -+ -+ MPI_Comm_rank( MPI_COMM_WORLD, &rank ); -+ -+ /* message exchange */ -+ for( i = 0; i < num; i++ ) { -+ if( rank == target ) { -+ MPI_Recv( buffer, 0, MPI_BYTE, source, tag, MPI_COMM_WORLD, &status ); -+ MPI_Send( buffer, 0, MPI_BYTE, source, tag, MPI_COMM_WORLD ); -+ } else if( rank == source ) { -+ MPI_Send( buffer, 0, MPI_BYTE, target, tag, MPI_COMM_WORLD ); -+ MPI_Recv( buffer, 0, MPI_BYTE, target, tag, MPI_COMM_WORLD, &status ); -+ } -+ } -+} -+ -+/** -+ * send messages back and forth between two process in MPI_COMM_WORLD, -+ * then calculate the clock offset and log it -+ */ -+static void pingpong( int source, int target, int tag, int num, int dryrun ) -+{ -+ MPI_Status status; -+ int i, rank; -+ char buffer[1]; -+ seconds_t *t_middle; -+ struct sample *samples; -+ char host[MPI_MAX_PROCESSOR_NAME + 1], peer[MPI_MAX_PROCESSOR_NAME + 1]; -+ int len; -+ -+ t_middle = malloc(sizeof(*t_middle) * num); -+ samples = malloc(sizeof(*samples) * num); -+ -+ MPI_Comm_rank( MPI_COMM_WORLD, &rank ); -+ MPI_Get_processor_name(host, &len); -+ host[len] = 0; -+ memset( buffer, 0, sizeof(buffer) ); -+ -+ /* message exchange */ -+ for( i = 0; i < num; i++ ) { -+ if( rank == target ) { -+ MPI_Recv( buffer, 0, MPI_BYTE, source, tag, MPI_COMM_WORLD, &status ); -+ t_middle[i] = systime(); -+ MPI_Send( buffer, 0, MPI_BYTE, source, tag, MPI_COMM_WORLD ); -+ } else if( rank == source ) { -+ samples[i].t_send = systime(); -+ MPI_Send( buffer, 0, MPI_BYTE, target, tag, MPI_COMM_WORLD ); -+ MPI_Recv( buffer, 0, MPI_BYTE, target, tag, MPI_COMM_WORLD, &status ); -+ samples[i].t_recv = systime(); -+ } -+ } -+ -+ /* calculation of offset */ -+ if( dryrun ) { -+ /* don't print */ -+ } else if( rank == source ) { -+ MPI_Datatype type; -+ MPI_Status status; -+ int start, end; -+ seconds_t average_off = 0, deviation_off = 0, min_off = 1e9, max_off = -1e9; -+ seconds_t average_round = 0, deviation_round = 0, min_round = 1e9, max_round = -1e9; -+ unsigned int maxcount = 0, histogram[NUM_BINS], bin; -+ char buffers[10][80]; -+ -+ /* sort incoming time stamps into the right place in each sample */ -+ MPI_Type_vector( num, 1, 3, MPI_DOUBLE, &type ); -+ MPI_Type_commit( &type ); -+ MPI_Recv( &samples[0].t_middle, 1, type, target, 0, MPI_COMM_WORLD, &status ); -+ -+ /* get peer name */ -+ MPI_Recv(peer, sizeof(peer), MPI_CHAR, target, 0, MPI_COMM_WORLD, &status); -+ -+ /* sort by increasing duration */ -+ qsort( samples, num, sizeof(*samples), compare_duration ); -+ -+ /* -+ * calculate min, max, average and empirical (n-1) standard deviation -+ * of offset, ignoring the 10% of the samples at borders -+ */ -+ memset( histogram, 0, sizeof(histogram) ); -+ start = num * 5 / 100; -+ end = num * 95 / 100; -+ for( i = start; -+ i < end; -+ i++ ) { -+ seconds_t tmp; -+ -+ tmp = offset(samples + i); -+ if( tmp < min_off ) { -+ min_off = tmp; -+ } -+ if( tmp > max_off ) { -+ max_off = tmp; -+ } -+ -+ tmp = samples[i].t_recv - samples[i].t_send; -+ if( tmp < min_round ) { -+ min_round = tmp; -+ } -+ if (tmp > max_round) { -+ max_round = tmp; -+ } -+ } -+ -+ for( i = start; -+ i < end; -+ i++ ) { -+ seconds_t off = offset(samples + i); -+ seconds_t round = samples[i].t_recv - samples[i].t_send; -+ -+ bin = (off - min_off) * NUM_BINS / (max_off - min_off); -+ /* sanity check */ -+ if( bin >= NUM_BINS ) { -+ bin = NUM_BINS - 1; -+ } -+ histogram[bin]++; -+ average_off += off; -+ deviation_off += off * off; -+ -+ average_round += round; -+ deviation_round += round * round; -+ } -+ for( bin = 0; bin < NUM_BINS; bin++ ) { -+ if( histogram[bin] > maxcount ) { -+ maxcount = histogram[bin]; -+ } -+ } -+ average_off /= end - start; -+ deviation_off = sqrt( deviation_off / ( end - start ) - average_off * average_off ); -+ average_round /= end - start; -+ deviation_round = sqrt( deviation_round / ( end - start ) - average_round * average_round ); -+ -+ syslog(LOG_INFO, "offset %s - %s", host, peer); -+ syslog(LOG_INFO, -+ "min/average/max/deviation of offset and round-trip time: " -+ "%s %s %s %s %s %s %s %s", -+ prettyprintseconds( min_off, 0, 3, buffers[0] ), -+ prettyprintseconds( average_off, 0, 3, buffers[1] ), -+ prettyprintseconds( max_off, 0, 3, buffers[2] ), -+ prettyprintseconds( deviation_off, 0, 3, buffers[3] ), -+ prettyprintseconds( min_round, 0, 3, buffers[4] ), -+ prettyprintseconds( average_round, 0, 3, buffers[5] ), -+ prettyprintseconds( max_round, 0, 3, buffers[6] ), -+ prettyprintseconds( deviation_round, 0, 3, buffers[7] )); -+ for( bin = 0; bin < NUM_BINS; bin++ ) { -+ syslog(LOG_INFO, " >= %s: %s %u\n", -+ prettyprintseconds( min_off + bin * ( max_off - min_off ) / NUM_BINS, 8, 3, buffers[0] ), -+ printbar( 40 * histogram[bin] / maxcount ), -+ histogram[bin] ); -+ } -+ syslog(LOG_INFO, " >= %s: 0\n", -+ prettyprintseconds( max_off, 8, 3, buffers[0] )); -+ } else if( rank == target ) { -+ MPI_Send(t_middle, num, MPI_DOUBLE, source, 0, MPI_COMM_WORLD); -+ MPI_Send(host, strlen(host)+1, MPI_CHAR, source, 0, MPI_COMM_WORLD); -+ } -+ -+ free(samples); -+ free(t_middle); -+} -+ -+/** qsort routine for increasing sort of doubles */ -+static int compare_ticks( const void *a, const void *b ) -+{ -+ ticks_t delta = *(const ticks_t *)a - *(const ticks_t *)b; -+ -+ return delta < 0 ? -1 : -+ delta > 0 ? 1 : -+ 0; -+} -+ -+/** the initial clock samples encounted by genhistogram() */ -+static ticks_t samples[MAX_SAMPLES]; -+ -+/** number of entries in samples array */ -+static unsigned int count; -+ -+/** -+ * call timer source repeatedly and record delta between samples -+ * in histogram and samples array -+ * -+ * @param duration maximum number of seconds for whole run -+ * @param min_increase first slot in histogram is for values < 0, -+ * second for >=0 and < min_increase -+ * @param bin_size width of all following bins -+ * @param histogram_size number of slots, including special ones -+ * @param histogram buffer for histogram, filled by this function -+ * @return number of calls to systicks() -+ */ -+static unsigned int genhistogram(seconds_t duration, -+ ticks_t min_increase, -+ ticks_t bin_size, -+ unsigned int histogram_size, -+ unsigned int *histogram) -+{ -+ ticks_t increase; -+ ticks_t startticks, lastticks, nextticks; -+ ticks_t endticks = duration / clockperiod; -+ unsigned int calls = 0; -+ -+ startticks = systicks(); -+ lastticks = 0; -+ count = 0; -+ memset(histogram, 0, sizeof(*histogram) * histogram_size); -+ do { -+ calls++; -+ nextticks = systicks() - startticks; -+ increase = nextticks - lastticks; -+ if( increase < 0 ) { -+ histogram[0]++; -+ } else if( increase > 0 ) { -+ unsigned int index; -+ -+ if( count < MAX_SAMPLES ) { -+ samples[count] = increase; -+ count++; -+ } -+ -+ if( increase < min_increase ) { -+ index = 1; -+ } else { -+ index = (unsigned int)( ( increase - min_increase ) / bin_size ) + 2; -+ if( index >= histogram_size ) { -+ index = histogram_size - 1; -+ } -+ } -+ histogram[index]++; -+ } -+ lastticks = nextticks; -+ } while( lastticks < endticks ); -+ -+ return calls; -+} -+ -+/** -+ * runs a timer performance test for the given duration -+ * -+ * @param duration duration of test in seconds -+ */ -+void timerperformance(seconds_t duration) -+{ -+ unsigned int i; -+ unsigned int max = 0; -+ char buffer[3][256]; -+ double average = 0, median = 0; -+ unsigned int calls; -+ unsigned int simple_histogram[3]; -+ ticks_t min_increase, bin_size, max_increase; -+ unsigned int histogram_size; -+ unsigned int *clockhistogram; -+ -+ /* determine range of clock increases for real run */ -+ calls = genhistogram(2.0, 1, 1, 3, simple_histogram); -+ qsort(samples, count, sizeof(samples[0]), compare_ticks); -+ -+ /* shoot for 10 slots, but allow for some extra slots at both ends as needed */ -+ min_increase = samples[0] == 1 ? 1 : samples[0] * 9 / 10; -+ max_increase = samples[count - 1]; -+ bin_size = (max_increase - min_increase) / 10; -+ if (bin_size * clockperiod <= 1e-9) { -+ bin_size = 1e-9 / clockperiod; -+ } -+ histogram_size = (max_increase - min_increase) / bin_size + 3 + 5; -+ clockhistogram = malloc(histogram_size * sizeof(*clockhistogram)); -+ calls = genhistogram(duration, min_increase, bin_size, histogram_size, clockhistogram); -+ qsort(samples, count, sizeof(samples[0]), compare_ticks); -+ -+ /* print average and medium increase */ -+ for( i = 0; i < count; i++ ) { -+ average += samples[i]; -+ } -+ average /= count; -+ qsort(samples, count, sizeof(samples[0]), compare_ticks); -+ median = samples[count/2]; -+ syslog(LOG_INFO, "average clock increase %s -> %.3fHz, median clock increase %s -> %3.fHz, %s/call", -+ prettyprintseconds(average * clockperiod, 0, 3, buffer[0]), 1/average/clockperiod, -+ prettyprintseconds(median * clockperiod, 0, 3, buffer[1]), 1/median/clockperiod, -+ prettyprintseconds(duration / calls, 0, 3, buffer[2])); -+ -+ for( i = 0; i < histogram_size; i++ ) { -+ if( clockhistogram[i] > max ) { -+ max = clockhistogram[i]; -+ } -+ } -+ syslog(LOG_INFO, " < %11.3fus: %s %u", -+ 0.0, -+ printbar(clockhistogram[0] * 20 / max), -+ clockhistogram[0]); -+ syslog(LOG_INFO, " < %11.3fus: %s %u", -+ min_increase * clockperiod * 1e6, -+ printbar( clockhistogram[1] * 20 / max ), -+ clockhistogram[1]); -+ for( i = 2; i < histogram_size; i++ ) { -+ syslog(LOG_INFO, ">= %11.3fus: %s %u", -+ ( ( i - 2 ) * bin_size + min_increase ) * clockperiod * 1e6, -+ printbar( clockhistogram[i] * 20 / max ), -+ clockhistogram[i]); -+ } -+ printf( "\n" ); -+ -+ free(clockhistogram); -+} -+ -+/* -+ * command line parameter handling -+ */ -+static const char usage[] = "timertest \n" -+#ifndef _WIN32 -+ " -g use gettimeofday() instead of clock_gettime() [default: clock_gettime()\n" -+#ifdef HAVE_LIBELF_H -+ " -d do not extract pointer to system functions from virtual dynamic shared\n" -+ " instead of relying on glibc to do that (current glibc does not\n" -+ " yet do that for the new 2.6.23-rc1 VDSO) [default: on]\n" -+#endif -+#endif -+ "\n" -+ "First determines the resolution of the local clocks in each process.\n" -+ "Then it does ping-pong tests between each pair of processes to measure\n" -+ "the clock offset at each exchange. Runs until killed.\n" -+ "Run with one process to just test clock resolution.\n" -+ ; -+ -+int main( int argc, char **argv ) -+{ -+ int rank, size; -+ int option; -+ int vdso = 1; -+ int c; -+ int source, target; -+ -+ MPI_Init( &argc, &argv ); -+ MPI_Comm_rank( MPI_COMM_WORLD, &rank ); -+ MPI_Comm_size( MPI_COMM_WORLD, &size ); -+ -+ while ((c = getopt(argc, argv, -+ "" -+#ifndef _WIN32 -+ "g" -+#ifdef HAVE_LIBELF_H -+ "d" -+#endif -+#endif -+ )) != -1) { -+ switch (c) { -+#ifndef _WIN32 -+ case 'g': -+ usetod = 1; -+ break; -+#ifdef HAVE_LIBELF_H -+ case 'd': -+ vdso = 0; -+ break; -+#endif -+#endif -+ default: -+ fputs(usage, stderr); -+ exit(1); -+ } -+ } -+ -+ option = 0; -+#ifdef LOG_PERROR -+ option |= LOG_PERROR; -+#endif -+ -+ clockperiod = -+#ifdef _WIN32 -+ 1e-3 -+#else -+ usetod ? -+ 1e-6 : -+ 1e-9 -+#endif -+ ; -+ -+ openlog("timertest", option, LOG_USER); -+ -+#ifdef HAVE_LIBELF_H -+ if (vdso) { -+ if (usetod) { -+ my_gettimeofday = findVDSOSym("gettimeofday"); -+ if (!my_gettimeofday) { -+ my_gettimeofday = (void *)gettimeofday; -+ } -+ } else { -+ my_clock_gettime = findVDSOSym("clock_gettime"); -+ if (!my_clock_gettime) { -+ my_clock_gettime = clock_gettime; -+ } -+ } -+ } -+#endif -+ -+#ifndef _WIN32 -+ syslog(LOG_NOTICE, "using %s from %s", -+ usetod ? "gettimeofday()" : "clock_gettime()", -+ (usetod ? (my_gettimeofday == (void *)gettimeofday) : (my_clock_gettime == clock_gettime)) ? "glibc" : "VDSO"); -+#endif -+ -+ timerperformance(CLOCK_DURATION); -+ -+ if (size > 1) { -+ for( source = 0; source < size - 1; source++ ) { -+ for( target = source + 1; target < size; target++ ) { -+ ticks_t start, middle, end; -+ MPI_Barrier( MPI_COMM_WORLD ); -+ start = systicks(); -+ simplepingpong( source, target, 123, MSG_CNT ); -+ middle = systicks(); -+ pingpong( source, target, 123, MSG_CNT, 1 ); -+ end = systicks(); -+ -+ if (rank == source) { -+ syslog(LOG_NOTICE, "overhead for %d<->%d ping-pong time stamping: %f%%", -+ source, target, -+ 100 * (double)(end - middle) / (double)(middle - start) - 100); -+ } -+ -+ MPI_Barrier( MPI_COMM_WORLD ); -+ } -+ } -+ } -+ -+ while (size > 1) { -+ if(!rank) { -+ syslog(LOG_NOTICE, "%s", printbar(75)); -+ } -+ for( source = 0; source < size - 1; source++ ) { -+ for( target = source + 1; target < size; target++ ) { -+ MPI_Barrier( MPI_COMM_WORLD ); -+ pingpong( source, target, 123, MSG_CNT, 0 ); -+ MPI_Barrier( MPI_COMM_WORLD ); -+ } -+ } -+ -+ sleep(LATENCY_TEST); -+ } -+ -+ MPI_Finalize(); -+ -+ return 0; -+} -+ -+#ifdef HAVE_LIBELF_H -+ -+#if __WORDSIZE == 32 -+# define ElfNative_Ehdr Elf32_Ehdr -+# define elfnative_getehdr elf32_getehdr -+# define ElfNative_Shdr Elf32_Shdr -+# define elfnative_getshdr elf32_getshdr -+# define ElfNative_Sym Elf32_Sym -+# define ELFNATIVE_ST_BIND ELF32_ST_BIND -+# define ELFNATIVE_ST_TYPE ELF32_ST_TYPE -+# define ElfNative_Phdr Elf32_Phdr -+# define elfnative_getphdr elf32_getphdr -+#else -+# define ElfNative_Ehdr Elf64_Ehdr -+# define elfnative_getehdr elf64_getehdr -+# define ElfNative_Shdr Elf64_Shdr -+# define elfnative_getshdr elf64_getshdr -+# define ElfNative_Sym Elf64_Sym -+# define ELFNATIVE_ST_BIND ELF64_ST_BIND -+# define ELFNATIVE_ST_TYPE ELF64_ST_TYPE -+# define ElfNative_Phdr Elf64_Phdr -+# define elfnative_getphdr elf64_getphdr -+#endif -+ -+static void *findVDSOSym(const char *symname) -+{ -+ Elf *elf; -+ void *res = NULL; -+ char *start = NULL, *end = NULL; -+ FILE *map; -+ -+ /** -+ * Normally a program gets a pointer to the vdso via the ELF aux -+ * vector entry AT_SYSINFO_EHDR (see -+ * http://manugarg.googlepages.com/aboutelfauxiliaryvectors) at -+ * startup. At runtime for a library, reading the memory map is -+ * simpler. -+ */ -+ map = fopen("/proc/self/maps", "r"); -+ if (map) { -+ char line[320]; -+ -+ while (fgets(line, sizeof(line), map) != NULL) { -+ /* fputs(line, stdout); */ -+ if (strstr(line, "[vdso]")) { -+ sscanf(line, "%p-%p", &start, &end); -+ break; -+ } -+ } -+ fclose(map); -+ } -+ -+ /** -+ * we know where the vdso is and that it contains an ELF object -+ * => search the symbol via libelf -+ */ -+ if (start) { -+ elf = elf_memory(start, end-start); -+ if (elf) { -+ Elf_Scn *scn; -+ size_t loadaddr = 0; -+ -+ for (scn = elf_nextscn(elf, NULL); -+ scn && !res; -+ scn = elf_nextscn(elf, scn)) { -+ ElfNative_Shdr *shdr = elfnative_getshdr(scn); -+ Elf_Data *data; -+ -+ /* -+ * All addresses are absolute, but the Linux kernel -+ * maps it at a different one. The load address can be -+ * determined by looking at any absolute address and -+ * substracting its offset relative to the file -+ * beginning because the whole file will be mapped -+ * into memory. We pick the first section for that. -+ */ -+ if (!loadaddr) { -+ loadaddr = shdr->sh_addr - shdr->sh_offset; -+ } -+ -+ if( !shdr || -+ shdr->sh_type != SHT_DYNSYM ) { -+ continue; -+ } -+ -+ data = elf_getdata(scn, 0); -+ if (!data || !data->d_size) { -+ continue; -+ } -+ -+ ElfNative_Sym *sym = (ElfNative_Sym *)data->d_buf; -+ ElfNative_Sym *lastsym = (ElfNative_Sym *)((char *)data->d_buf + data->d_size); -+ -+ for( ; !res && sym < lastsym; sym++ ) { -+ const char *name; -+ -+ if( sym->st_value == 0 || /* need valid address and size */ -+ sym->st_size == 0 || -+ ELFNATIVE_ST_TYPE( sym->st_info ) != STT_FUNC || /* only functions */ -+ sym->st_shndx == SHN_UNDEF ) { /* ignore dynamic linker stubs */ -+ continue; -+ } -+ -+ name = elf_strptr( elf, shdr->sh_link, (size_t)sym->st_name ); -+ if( name && !strcmp(symname, name) ) { -+ res = (void *)(sym->st_value - loadaddr + start); -+ } -+ } -+ } -+ elf_end(elf); -+ } -+ } -+ -+ return res; -+} -+#endif /* HAVE_LIBELF_H */ -diff --git a/src/Makefile b/src/Makefile -index cf9afff..e26bf08 100644 ---- a/src/Makefile -+++ b/src/Makefile -@@ -6,7 +6,8 @@ CFLAGS = -Wall - - PROG = ptpd - OBJ = ptpd.o arith.o bmc.o probe.o protocol.o \ -- dep/msg.o dep/net.o dep/servo.o dep/startup.o dep/sys.o dep/timer.o -+ dep/msg.o dep/net.o dep/servo.o dep/startup.o dep/sys.o dep/timer.o \ -+ dep/time.o - HDR = ptpd.h constants.h datatypes.h \ - dep/ptpd_dep.h dep/constants_dep.h dep/datatypes_dep.h - -diff --git a/src/bmc.c b/src/bmc.c -index 753b6a5..ca0d426 100644 ---- a/src/bmc.c -+++ b/src/bmc.c -@@ -2,12 +2,14 @@ - - #include "ptpd.h" - --void initData(RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void initData(PtpClock *ptpClock) - { -+ TimeInternal now; -+ - DBG("initData\n"); - -- if(rtOpts->slaveOnly) -- rtOpts->clockStratum = 255; -+ if(ptpClock->runTimeOpts.slaveOnly) -+ ptpClock->runTimeOpts.clockStratum = 255; - - /* Port configuration data set */ - ptpClock->last_sync_event_sequence_number = 0; -@@ -19,27 +21,30 @@ void initData(RunTimeOpts *rtOpts, PtpClock *ptpClock) - ptpClock->clock_communication_technology = ptpClock->port_communication_technology; - memcpy(ptpClock->clock_uuid_field, ptpClock->port_uuid_field, PTP_UUID_LENGTH); - ptpClock->clock_port_id_field = 0; -- ptpClock->clock_stratum = rtOpts->clockStratum; -- memcpy(ptpClock->clock_identifier, rtOpts->clockIdentifier, PTP_CODE_STRING_LENGTH); -- ptpClock->sync_interval = rtOpts->syncInterval; -+ ptpClock->clock_stratum = ptpClock->runTimeOpts.clockStratum; -+ memcpy(ptpClock->clock_identifier, ptpClock->runTimeOpts.clockIdentifier, PTP_CODE_STRING_LENGTH); -+ ptpClock->sync_interval = ptpClock->runTimeOpts.syncInterval; - -- ptpClock->clock_variance = rtOpts->clockVariance; /* see spec 7.7 */ -+ ptpClock->clock_variance = ptpClock->runTimeOpts.clockVariance; /* see spec 7.7 */ - ptpClock->clock_followup_capable = CLOCK_FOLLOWUP; -- ptpClock->preferred = rtOpts->clockPreferred; -+ ptpClock->preferred = ptpClock->runTimeOpts.clockPreferred; - ptpClock->initializable = INITIALIZABLE; - ptpClock->external_timing = EXTERNAL_TIMING; - ptpClock->is_boundary_clock = BOUNDARY_CLOCK; -- memcpy(ptpClock->subdomain_name, rtOpts->subdomainName, PTP_SUBDOMAIN_NAME_LENGTH); -+ memcpy(ptpClock->subdomain_name, ptpClock->runTimeOpts.subdomainName, PTP_SUBDOMAIN_NAME_LENGTH); - ptpClock->number_ports = NUMBER_PORTS; - ptpClock->number_foreign_records = 0; -- ptpClock->max_foreign_records = rtOpts->max_foreign_records; -+ ptpClock->max_foreign_records = ptpClock->runTimeOpts.max_foreign_records; - - /* Global time properties data set */ -- ptpClock->current_utc_offset = rtOpts->currentUtcOffset; -- ptpClock->epoch_number = rtOpts->epochNumber; -+ ptpClock->current_utc_offset = ptpClock->runTimeOpts.currentUtcOffset; -+ ptpClock->epoch_number = ptpClock->runTimeOpts.epochNumber; - - /* other stuff */ -- ptpClock->random_seed = ptpClock->port_uuid_field[PTP_UUID_LENGTH-1]; -+ timerNow(&now); -+ ptpClock->random_seed = ptpClock->port_uuid_field[PTP_UUID_LENGTH-1] ^ -+ now.seconds ^ -+ now.nanoseconds; - } - - /* see spec table 18 */ -@@ -279,9 +284,9 @@ B: - return -1; /* B1 */ - } - --UInteger8 bmcStateDecision(MsgHeader *header, MsgSync *sync, RunTimeOpts *rtOpts, PtpClock *ptpClock) -+UInteger8 bmcStateDecision(MsgHeader *header, MsgSync *sync, PtpClock *ptpClock) - { -- if(rtOpts->slaveOnly) -+ if(ptpClock->runTimeOpts.slaveOnly) - { - s1(header, sync, ptpClock); - return PTP_SLAVE; -@@ -312,7 +317,7 @@ UInteger8 bmcStateDecision(MsgHeader *header, MsgSync *sync, RunTimeOpts *rtOpts - } - } - --UInteger8 bmc(ForeignMasterRecord *foreign, RunTimeOpts *rtOpts, PtpClock *ptpClock) -+UInteger8 bmc(ForeignMasterRecord *foreign, PtpClock *ptpClock) - { - Integer16 i, best; - -@@ -333,6 +338,6 @@ UInteger8 bmc(ForeignMasterRecord *foreign, RunTimeOpts *rtOpts, PtpClock *ptpCl - DBGV("bmc: best record %d\n", best); - ptpClock->foreign_record_best = best; - -- return bmcStateDecision(&foreign[best].header, &foreign[best].sync, rtOpts, ptpClock); -+ return bmcStateDecision(&foreign[best].header, &foreign[best].sync, ptpClock); - } - -diff --git a/src/constants.h b/src/constants.h -index 0f1972a..43837b3 100644 ---- a/src/constants.h -+++ b/src/constants.h -@@ -14,6 +14,7 @@ - #define DEFAULT_INBOUND_LATENCY 0 /* in nsec */ - #define DEFAULT_OUTBOUND_LATENCY 0 /* in nsec */ - #define DEFAULT_NO_RESET_CLOCK FALSE -+#define DEFAULT_NO_ADJUST_CLOCK FALSE - #define DEFAULT_AP 10 - #define DEFAULT_AI 1000 - #define DEFAULT_DELAY_S 6 -diff --git a/src/datatypes.h b/src/datatypes.h -index 4196f5e..20d5309 100644 ---- a/src/datatypes.h -+++ b/src/datatypes.h -@@ -205,8 +205,84 @@ typedef struct - MsgSync sync; - } ForeignMasterRecord; - -+typedef enum { -+ TIME_SYSTEM, /**< use and control system time */ -+ TIME_NIC, /**< use and control time inside network interface (via Intel -+ igb ioctl()) */ -+ /** -+ * a combination of PTP between NICs (as in TIME_NIC via Intel igb -+ * ioctl()) plus a local synchronization between NIC and system -+ * time: -+ * -+ * - NIC time is controlled via PTP packets, main time seen -+ * by PTPd is the NIC time -+ * - system_to_nic and nic_to_system delays are provided by -+ * device driver on request -+ * - time.c adapts NIC time to system time on master and system time -+ * to NIC time on slaves by feeding these offsets into another -+ * instance of the clock servo (as in TIME_SYSTEM) -+ * -+ * The command line options only apply to one clock sync and -+ * the defaults are used for the other: -+ * - NIC time: default values for clock control (adjust and reset) -+ * and servo (coefficients), configurable PTP -+ * - system time: configurable clock control and servo, PTP options do -+ * not apply -+ */ -+ TIME_BOTH, -+ /** -+ * time used and controlled by PTP is the system time, but hardware -+ * assistance in the NIC is used to time stamp packages (via Intel -+ * igb ioctl()) -+ */ -+ TIME_SYSTEM_ASSISTED, -+ /** -+ * Time used and controlled by PTP is the system time with hardware -+ * packet time stamping via standard Linux net_tstamp.h API. -+ */ -+ TIME_SYSTEM_LINUX_HW, -+ /** -+ * Time used and controlled by PTP is the system time with software -+ * packet time stamping via standard Linux net_tstamp.h API. -+ */ -+ TIME_SYSTEM_LINUX_SW, -+ -+ TIME_MAX -+} Time; -+ -+/* program options set at run-time */ -+typedef struct { -+ Integer8 syncInterval; -+ Octet subdomainName[PTP_SUBDOMAIN_NAME_LENGTH]; -+ Octet clockIdentifier[PTP_CODE_STRING_LENGTH]; -+ UInteger32 clockVariance; -+ UInteger8 clockStratum; -+ Boolean clockPreferred; -+ Integer16 currentUtcOffset; -+ UInteger16 epochNumber; -+ Octet ifaceName[IFACE_NAME_LENGTH]; -+ Boolean noResetClock; -+ Boolean noAdjust; -+ Boolean displayStats; -+ Boolean csvStats; -+ Octet unicastAddress[NET_ADDRESS_LENGTH]; -+ Integer16 ap, ai; -+ Integer16 s; -+ TimeInternal inboundLatency, outboundLatency; -+ Integer16 max_foreign_records; -+ Boolean slaveOnly; -+ Boolean probe; -+ UInteger8 probe_management_key; -+ UInteger16 probe_record_key; -+ Boolean halfEpoch; -+ Time time; -+} RunTimeOpts; -+ - /* main program data structure */ - typedef struct { -+ /* settings associate with this instance of PtpClock */ -+ RunTimeOpts runTimeOpts; -+ - /* Default data set */ - UInteger8 clock_communication_technology; - Octet clock_uuid_field[PTP_UUID_LENGTH]; -@@ -240,6 +316,7 @@ typedef struct { - Boolean parent_stats; - Integer16 observed_variance; - Integer32 observed_drift; -+ long adj; - Boolean utc_reasonable; - UInteger8 grandmaster_communication_technology; - Octet grandmaster_uuid_field[PTP_UUID_LENGTH]; -@@ -303,7 +380,32 @@ typedef struct { - - UInteger16 Q; - UInteger16 R; -- -+ -+ /** -+ * TRUE when the clock is used to synchronize NIC and system time and -+ * the process is the PTP_MASTER. The offset and adjustment calculation -+ * is always "master (= NIC) to slave (= system time)" and PTP_SLAVEs -+ * update their system time, but the master needs to invert the -+ * clock adjustment and control NIC time instead. This way -+ * the master's system time is propagated to slaves. -+ */ -+ Boolean nic_instead_of_system; -+ -+ /** -+ * TRUE if outgoing packets are not to be time-stamped in advance. -+ * Instead the outgoing time stamp is generated as it is transmitted -+ * and must be sent in a follow-up message. -+ */ -+ Boolean delayedTiming; -+ -+ /** -+ * a prefix to be inserted before messages about the clock: -+ * may be empty, but not NULL -+ * -+ * used to distinguish multiple active clock servos per process -+ */ -+ const char *name; -+ - Boolean sentDelayReq; - UInteger16 sentDelayReqSequenceId; - Boolean waitingForFollow; -@@ -316,35 +418,7 @@ typedef struct { - IntervalTimer itimer[TIMER_ARRAY_SIZE]; - - NetPath netPath; -- --} PtpClock; - --/* program options set at run-time */ --typedef struct { -- Integer8 syncInterval; -- Octet subdomainName[PTP_SUBDOMAIN_NAME_LENGTH]; -- Octet clockIdentifier[PTP_CODE_STRING_LENGTH]; -- UInteger32 clockVariance; -- UInteger8 clockStratum; -- Boolean clockPreferred; -- Integer16 currentUtcOffset; -- UInteger16 epochNumber; -- Octet ifaceName[IFACE_NAME_LENGTH]; -- Boolean noResetClock; -- Boolean noAdjust; -- Boolean displayStats; -- Boolean csvStats; -- Octet unicastAddress[NET_ADDRESS_LENGTH]; -- Integer16 ap, ai; -- Integer16 s; -- TimeInternal inboundLatency, outboundLatency; -- Integer16 max_foreign_records; -- Boolean slaveOnly; -- Boolean probe; -- UInteger8 probe_management_key; -- UInteger16 probe_record_key; -- Boolean halfEpoch; -- --} RunTimeOpts; -+} PtpClock; - - #endif -diff --git a/src/dep/constants_dep.h b/src/dep/constants_dep.h -index 78900ec..33ce022 100644 ---- a/src/dep/constants_dep.h -+++ b/src/dep/constants_dep.h -@@ -5,6 +5,8 @@ - - /* platform dependent */ - -+#include -+ - #if !defined(linux) && !defined(__NetBSD__) && !defined(__FreeBSD__) - #error Not ported to this architecture, please update. - #endif -@@ -56,8 +58,86 @@ - # endif - #endif - -- --#define ADJ_FREQ_MAX 512000 -+/** -+ * This value used to be used as limit for adjtimex() and the clock -+ * servo. The limits of adjtimex() are now determined at runtime in -+ * dep/time.c and are larger than before because also the us/tick -+ * value is adjusted, but the servo still uses this limit as sanity -+ * check. Now that the underlying system is no longer the limiting -+ * factor, perhaps the value should be configurable? -+ * -+ * The value was 5120000 (based on the maximum adjustment possible by -+ * adjusting just the frequency in adjtimex()), but that turned out to -+ * be too small on a system under load: as soon as the observed drift -+ * got larger than 5120000, it was clamped and the frequency -+ * adjustment remained too small to drive the offset back to -+ * zero. Here's a log of that situation: -+ * -+00:00:10 knlcst4 ptpd: state, one way delay, offset from master, drift, variance, clock adjustment (ppb) -+00:00:10 knlcst4 ptpd: init -+00:00:10 knlcst4 ptpd: lstn -+00:00:30 knlcst4 ptpd: mst -+00:00:31 knlcst4 ptpd: slv, 0.000000000, 0.000000000, 0, 0, 0 -+00:00:33 knlcst4 ptpd: slv, 0.000000000, 0.000104000, -104, 0, 10504 -+00:00:35 knlcst4 ptpd: slv, 0.000000000, 0.001509000, -1613, 0, 152513 -+00:00:37 knlcst4 ptpd: slv, 0.000000000, 0.001842500, 229, 0, -184479 -+00:00:39 knlcst4 ptpd: slv, 0.000000000, 0.001268000, 1497, 0, -128297 -+... -+00:30:17 knlcst4 ptpd: slv, 0.003492994, 0.000289006, -175627, 0, 146727 -+00:30:19 knlcst4 ptpd: slv, 0.003492994, 0.004753994, -180380, 0, 655779 -+00:30:21 knlcst4 ptpd: slv, 0.003487771, 0.000671117, -179709, 0, 112598 -+00:30:23 knlcst4 ptpd: slv, 0.003487771, 0.002132729, -177577, 0, -35695 -+00:30:25 knlcst4 ptpd: slv, 0.003487771, 0.004137270, -181714, 0, 595441 -+00:30:27 knlcst4 ptpd: slv, 0.003487771, 0.006283270, -187997, 0, 816324 -+00:30:29 knlcst4 ptpd: slv, 0.003487771, 0.004510770, -192507, 0, 643584 -+[offset continuesly grows from here on] -+... -+00:35:03 knlcst4 ptpd: slv, 0.003435910, 0.061051910, -4602318, 0, 10707509 -+00:35:05 knlcst4 ptpd: slv, 0.003435910, 0.061784910, -4664102, 0, 10842593 -+00:35:07 knlcst4 ptpd: slv, 0.003435910, 0.060685910, -4724787, 0, 10793378 -+00:35:09 knlcst4 ptpd: slv, 0.003435910, 0.057270910, -4782057, 0, 10509148 -+00:35:11 knlcst4 ptpd: slv, 0.003435910, 0.055342410, -4837399, 0, 10371640 -+00:35:13 knlcst4 ptpd: slv, 0.003435910, 0.059554910, -4896953, 0, 10852444 -+00:35:15 knlcst4 ptpd: slv, 0.003435910, 0.061615910, -4958568, 0, 11120159 -+00:35:17 knlcst4 ptpd: slv, 0.003435910, 0.062028410, -5020596, 0, 11223437 -+00:35:19 knlcst4 ptpd: slv, 0.003435910, 0.063420910, -5084016, 0, 11426107 -+[offset gets clamped at -5120000] -+00:35:21 knlcst4 ptpd: slv, 0.003435910, 0.060922910, -5120000, 0, 11212291 -+00:35:23 knlcst4 ptpd: slv, 0.003435910, 0.061610910, -5120000, 0, 11281091 -+00:35:25 knlcst4 ptpd: slv, 0.003435910, 0.061527910, -5120000, 0, 11272791 -+00:35:27 knlcst4 ptpd: slv, 0.003435910, 0.063027910, -5120000, 0, 11422791 -+... -+[maximum value for adjustment reached] -+00:53:31 knlcst4 ptpd: slv, 0.003368888, 0.285260388, -5120000, 0, 33554432 -+00:53:33 knlcst4 ptpd: slv, 0.003368888, 0.284009388, -5120000, 0, 33520938 -+00:53:35 knlcst4 ptpd: slv, 0.003368888, 0.282019888, -5120000, 0, 33321988 -+00:53:37 knlcst4 ptpd: slv, 0.003368888, 0.284614888, -5120000, 0, 33554432 -+00:53:39 knlcst4 ptpd: slv, 0.003368888, 0.287445888, -5120000, 0, 33554432 -+00:53:41 knlcst4 ptpd: slv, 0.003368888, 0.283629388, -5120000, 0, 33482938 -+00:53:43 knlcst4 ptpd: slv, 0.003368888, 0.283933388, -5120000, 0, 33513338 -+00:53:45 knlcst4 ptpd: slv, 0.003368888, 0.289208888, -5120000, 0, 33554432 -+00:53:47 knlcst4 ptpd: slv, 0.003368888, 0.289672388, -5120000, 0, 33554432 -+00:53:49 knlcst4 ptpd: slv, 0.003368888, 0.284487388, -5120000, 0, 33554432 -+00:53:51 knlcst4 ptpd: slv, 0.003368888, 0.277956888, -5120000, 0, 32915688 -+00:53:53 knlcst4 ptpd: slv, 0.003368888, 0.283933388, -5120000, 0, 33513338 -+00:53:55 knlcst4 ptpd: slv, 0.003368888, 0.287284388, -5120000, 0, 33554432 -+00:53:57 knlcst4 ptpd: slv, 0.003368888, 0.285872388, -5120000, 0, 33554432 -+00:53:59 knlcst4 ptpd: slv, 0.003368888, 0.290243888, -5120000, 0, 33554432 -+00:54:01 knlcst4 ptpd: slv, 0.003368888, 0.292336388, -5120000, 0, 33554432 -+00:54:03 knlcst4 ptpd: slv, 0.003368888, 0.292805388, -5120000, 0, 33554432 -+00:54:05 knlcst4 ptpd: slv, 0.003379526, 0.291303707, -5120000, 0, 33554432 -+... -+01:51:44 knlcst4 ptpd: slv, 0.003304196, 0.995760196, -5120000, 0, 33554432 -+01:51:46 knlcst4 ptpd: slv, 0.003304196, 0.993363696, -5120000, 0, 33554432 -+01:51:48 knlcst4 ptpd: resetting system clock to 1200569796s 698871196e-9 -+01:51:49 knlcst4 ptpd: slv, 0.003304196, -1.000127196, 0, 0, 0 -+01:51:51 knlcst4 ptpd: slv, 0.003304196, 0.001648402, 1648, 0, -166488 -+01:51:53 knlcst4 ptpd: slv, 0.003304196, 0.004037804, 5685, 0, -409465 -+01:51:55 knlcst4 ptpd: slv, 0.003304196, 0.002923304, 8608, 0, -300938 -+01:51:57 knlcst4 ptpd: slv, 0.003304196, 0.000091696, 8517, 0, 652 -+[cycle repeats] -+ */ -+#define ADJ_FREQ_MAX 512000000 - - /* UDP/IPv4 dependent */ - -diff --git a/src/dep/datatypes_dep.h b/src/dep/datatypes_dep.h -index b42f2f8..6b7e127 100644 ---- a/src/dep/datatypes_dep.h -+++ b/src/dep/datatypes_dep.h -@@ -23,6 +23,11 @@ typedef struct { - - typedef struct { - Integer32 eventSock, generalSock, multicastAddr, unicastAddr; -+#if defined(linux) -+ /** for further ioctl() calls on eventSock */ -+ struct ifreq eventSockIFR; -+#endif -+ UInteger16 lastNetSendEventLength; - } NetPath; - - #endif -diff --git a/src/dep/e1000_ioctl.h b/src/dep/e1000_ioctl.h -new file mode 100644 -index 0000000..0e39254 ---- /dev/null -+++ b/src/dep/e1000_ioctl.h -@@ -0,0 +1,190 @@ -+/******************************************************************************* -+ * -+ * Intel(R) Gigabit Ethernet Linux driver -+ * -+ * Copyright (c) 2006-2008, Intel Corporation -+ * 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 the Intel Corporation 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 Intel Corporation ''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 Intel Corporation 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. -+ * -+ *******************************************************************************/ -+ -+/** @todo add size field to structs to allow for extensions */ -+ -+#ifndef __E1000_IOCTL_H__ -+#define __E1000_IOCTL_H__ -+ -+/** -+ * a time stamp -+ * -+ * The value is always positive, negative time stamps must be -+ * represented with an additional +1/-1 sign factor. -+ * -+ * @todo use a better definition for 64 bit unsigned which works -+ * in the driver and user space -+ */ -+struct E1000_TS { -+ unsigned long long seconds; -+ unsigned int nanoseconds; -+}; -+ -+/** -+ * Initialize NIC for PTP time stamping. -+ * After this call E1000_TSYNC_SYSTIME_IOCTL will return -+ * time stamps which are somewhat related to the current system time, -+ * but will drift apart again. -+ * -+ * @return 0 for success -+ */ -+#define E1000_TSYNC_INIT_IOCTL 0x89f0 -+ -+/** -+ * Optionally (if ARGU::negative_offset != 0) update NIC time by given -+ * offset and return current time. Current time is inaccurate because -+ * accessing the NIC incurrs a certain non-deterministic delay. -+ * -+ * @todo determine how large the delay is in reality -+ */ -+#define E1000_TSYNC_SYSTIME_IOCTL 0x89f1 -+ -+/** parameters and results of E1000_TSYNC_SYSTIME_IOCTL */ -+struct E1000_TSYNC_SYSTIME_ARGU { -+ /** input: offset to be applied to time; output: current time */ -+ struct E1000_TS time; -+ -+ /** -+ * <0: substract input offset -+ * >0: add input offset -+ * =0: only read current time -+ */ -+ int negative_offset; -+}; -+ -+/** -+ * Speed up (positive value) or slow down the clock by a -+ * certain amount specified as parts per billion (1e-9). -+ * For example, a parameter of 1 means "add 1 microsecond -+ * every second". -+ */ -+#define E1000_TSYNC_ADJTIME_IOCTL 0x89f2 -+ -+struct E1000_TSYNC_ADJTIME_ARGU { -+ /** -+ * input: adjustment to be applied to time in ppb (1e-9); -+ * output: current adjustment -+ */ -+ long long adj; -+ -+ /** -+ * only set adjustment if != 0 -+ */ -+ int set_adj; -+}; -+ -+/** @todo: consolidate enable/disable ioctl() calls into one? */ -+ -+/** enable time stamping of outgoing PTP packets, returns 0 if successful */ -+#define E1000_TSYNC_ENABLETX_IOCTL 0x89f4 -+/** disable time stamping of outgoing PTP packets, returns 0 if successful */ -+#define E1000_TSYNC_DISABLETX_IOCTL 0x89f5 -+ -+/** -+ * enable time stamping of incoming PTP packets, returns 0 if successful -+ * -+ * *(int *)&ifr_data determines mode -+ */ -+#define E1000_TSYNC_ENABLERX_IOCTL 0x89f8 -+ -+/** @todo: add RX timestamp mode = 5 */ -+enum { -+ E1000_L2_V2_SYNC, /**< time stamp incoming layer 2 PTP V2 Sync packets */ -+ E1000_L2_V2_DELAY, /**< time stamp incoming layer 2 PTP V2 Delay_Req packets */ -+ E1000_UDP_V1_SYNC, /**< time stamp incoming UDP PTP V1 Sync packets */ -+ E1000_UDP_V1_DELAY, /**< time stamp incoming UDP PTP V1 Delay_Req packets */ -+ E1000_TSYNC_MAX -+}; -+ -+ -+/** disable time stamping of incoming PTP packets, returns 0 if successful */ -+#define E1000_TSYNC_DISABLERX_IOCTL 0x89f9 -+ -+/** get information about send/receive time stamps */ -+#define E1000_TSYNC_READTS_IOCTL 0x89fc -+ -+struct E1000_TSYNC_READTS_ARGU { -+ /** -+ * in: not only return NIC time stamps, but also the -+ * corresponding system time (may cause additional overhead) -+ */ -+ int withSystemTime; -+ -+ /** out: receive information is only valid if rx_valid != 0 */ -+ int rx_valid; -+ /** out: receive NIC time stamp */ -+ struct E1000_TS rx; -+ /** out (if withSystemTime was true): the corresponding receive system time */ -+ struct E1000_TS rx_sys; -+ /** out: the PTP sequence ID of the time stamped packet */ -+ uint16_t sourceSequenceId; -+ /** out: the PTP source ID of the time stamped packet */ -+ unsigned char sourceIdentity[6]; -+ -+ /** out: send information is only valid if tx_valid != 0 */ -+ int tx_valid; -+ -+ /** out: send NIC time stamp */ -+ struct E1000_TS tx; -+ /** out (if withSystemTime was true): the corresponding send system time */ -+ struct E1000_TS tx_sys; -+}; -+ -+/** -+ * Correlates system time and NIC time each time it is called. The -+ * - offsetFromSystem = NIC time - system time -+ * is calculated as in PTP/IEEE1555: -+ * - oneWayDelay = (NICToSystem + systemToNIC)/2 -+ * - offsetFromSystem = systemToNIC - oneWayDelay -+ * = (systemToNIC - NICToSystem)/2 -+ * -+ * A driver which does not measure both delays can simply set one -+ * delay to zero and return twice the offset in the other field. -+ * -+ * A positive offset means that the NIC time is higher than the system -+ * time, i.e. either the system clock must speed up to catch up with -+ * the NIC or the NIC must slow down. -+ */ -+#define E1000_TSYNC_COMPARETS_IOCTL 0x89fd -+ -+struct E1000_TSYNC_COMPARETS_ARGU { -+ /** out: one-way delay for sending from NIC to system */ -+ struct E1000_TS NICToSystem; -+ /** +1 for positiv delay or -1 for negative one */ -+ int NICToSystemSign; -+ -+ /** one-way delay for sending from system to NIC */ -+ struct E1000_TS systemToNIC; -+ /** +1 for positiv delay or -1 for negative one */ -+ int systemToNICSign; -+}; -+ -+#endif /* __E1000_IOCTL_H__ */ -diff --git a/src/dep/msg.c b/src/dep/msg.c -index 8b43150..22b276e 100644 ---- a/src/dep/msg.c -+++ b/src/dep/msg.c -@@ -155,7 +155,7 @@ void msgUnpackManagement(void *buf, MsgManagement *manage) - } - - UInteger8 msgUnloadManagement(void *buf, MsgManagement *manage, -- PtpClock *ptpClock, RunTimeOpts *rtOpts) -+ PtpClock *ptpClock) - { - TimeInternal internalTime; - TimeRepresentation externalTime; -@@ -203,29 +203,29 @@ UInteger8 msgUnloadManagement(void *buf, MsgManagement *manage, - break; - - case PTP_MM_SET_SYNC_INTERVAL: -- rtOpts->syncInterval = *(Integer8*)(buf + 63); -+ ptpClock->runTimeOpts.syncInterval = *(Integer8*)(buf + 63); - break; - - case PTP_MM_SET_SUBDOMAIN: -- memcpy(rtOpts->subdomainName, buf + 60, 16); -- DBG("set subdomain to %s\n", rtOpts->subdomainName); -+ memcpy(ptpClock->runTimeOpts.subdomainName, buf + 60, 16); -+ DBG("set subdomain to %s\n", ptpClock->runTimeOpts.subdomainName); - break; - - case PTP_MM_SET_TIME: - externalTime.seconds = flip32(*(UInteger32*)(buf + 60)); - externalTime.nanoseconds = flip32(*(Integer32*)(buf + 64)); - toInternalTime(&internalTime, &externalTime, &ptpClock->halfEpoch); -- setTime(&internalTime); -+ setTime(&internalTime, ptpClock); - break; - - case PTP_MM_UPDATE_DEFAULT_DATA_SET: -- if(!rtOpts->slaveOnly) -+ if(!ptpClock->runTimeOpts.slaveOnly) - ptpClock->clock_stratum = *(UInteger8*)(buf + 63); - memcpy(ptpClock->clock_identifier, buf + 64, 4); - ptpClock->clock_variance = flip16(*(Integer16*)(buf + 70)); - ptpClock->preferred = *(UInteger8*)(buf + 75); -- rtOpts->syncInterval = *(UInteger8*)(buf + 79); -- memcpy(rtOpts->subdomainName, buf + 80, 16); -+ ptpClock->runTimeOpts.syncInterval = *(UInteger8*)(buf + 79); -+ memcpy(ptpClock->runTimeOpts.subdomainName, buf + 80, 16); - break; - - case PTP_MM_UPDATE_GLOBAL_TIME_PROPERTIES: -@@ -372,7 +372,7 @@ void msgPackHeader(void *buf, PtpClock *ptpClock) - setFlag((buf + 34), PTP_BOUNDARY_CLOCK); - } - --void msgPackSync(void *buf, Boolean burst, -+void msgPackSync(void *buf, Boolean burst, Boolean ptpAssist, - TimeRepresentation *originTimestamp, PtpClock *ptpClock) - { - *(UInteger8*)(buf +20) = 1; /* messageType */ -@@ -386,7 +386,14 @@ void msgPackSync(void *buf, Boolean burst, - setFlag((buf + 34), PARENT_STATS); - else - clearFlag((buf + 34), PARENT_STATS); -- -+ /** -+ * @todo: before adding this conditional ptpAssist the PTP_ASSIST -+ * was never set although a Follow_Up was sent - a bug in the original PTPd? -+ */ -+ if(ptpAssist) -+ setFlag((buf + 34), PTP_ASSIST); -+ else -+ clearFlag((buf + 34), PTP_ASSIST); - - *(Integer32*)(buf + 40) = flip32(originTimestamp->seconds); - *(Integer32*)(buf + 44) = flip32(originTimestamp->nanoseconds); -@@ -411,7 +418,7 @@ void msgPackSync(void *buf, Boolean burst, - *(Integer32*)(buf + 120) = shift8(ptpClock->utc_reasonable, 3); - } - --void msgPackDelayReq(void *buf, Boolean burst, -+void msgPackDelayReq(void *buf, Boolean burst, Boolean ptpAssist, - TimeRepresentation *originTimestamp, PtpClock *ptpClock) - { - *(UInteger8*)(buf + 20) = 1; /* messageType */ -@@ -425,6 +432,10 @@ void msgPackDelayReq(void *buf, Boolean burst, - setFlag((buf + 34), PARENT_STATS); - else - clearFlag((buf + 34), PARENT_STATS); -+ if(ptpAssist) -+ setFlag((buf + 34), PTP_ASSIST); -+ else -+ clearFlag((buf + 34), PTP_ASSIST); - - *(Integer32*)(buf + 40) = flip32(originTimestamp->seconds); - *(Integer32*)(buf + 44) = flip32(originTimestamp->nanoseconds); -@@ -457,6 +468,7 @@ void msgPackFollowUp(void *buf, UInteger16 associatedSequenceId, - *(UInteger8*)(buf + 32) = PTP_FOLLOWUP_MESSAGE; /* control */ - clearFlag((buf + 34), PTP_SYNC_BURST); - clearFlag((buf + 34), PARENT_STATS); -+ clearFlag((buf + 34), PTP_ASSIST); - - *(Integer32*)(buf + 40) = shift16(flip16(associatedSequenceId), 1); - *(Integer32*)(buf + 44) = flip32(preciseOriginTimestamp->seconds); -@@ -471,6 +483,7 @@ void msgPackDelayResp(void *buf, MsgHeader *header, - *(UInteger8*)(buf + 32) = PTP_DELAY_RESP_MESSAGE; /* control */ - clearFlag((buf + 34), PTP_SYNC_BURST); - clearFlag((buf + 34), PARENT_STATS); -+ clearFlag((buf + 34), PTP_ASSIST); - - *(Integer32*)(buf + 40) = flip32(delayReceiptTimestamp->seconds); - *(Integer32*)(buf + 44) = flip32(delayReceiptTimestamp->nanoseconds); -@@ -486,6 +499,7 @@ UInteger16 msgPackManagement(void *buf, MsgManagement *manage, PtpClock *ptpCloc - *(UInteger8*)(buf + 32) = PTP_MANAGEMENT_MESSAGE; /* control */ - clearFlag((buf + 34), PTP_SYNC_BURST); - clearFlag((buf + 34), PARENT_STATS); -+ clearFlag((buf + 34), PTP_ASSIST); - *(Integer32*)(buf + 40) = shift8(manage->targetCommunicationTechnology, 1); - memcpy(buf + 42, manage->targetUuid, 6); - *(Integer32*)(buf + 48) = shift16(flip16(manage->targetPortId), 0) | shift16(flip16(MM_STARTING_BOUNDARY_HOPS), 1); -@@ -516,6 +530,7 @@ UInteger16 msgPackManagementResponse(void *buf, MsgHeader *header, MsgManagement - *(UInteger8*)(buf + 32) = PTP_MANAGEMENT_MESSAGE; /* control */ - clearFlag((buf + 34), PTP_SYNC_BURST); - clearFlag((buf + 34), PARENT_STATS); -+ clearFlag((buf + 34), PTP_ASSIST); - *(Integer32*)(buf + 40) = shift8(header->sourceCommunicationTechnology, 1); - memcpy(buf + 42, header->sourceUuid, 6); - *(Integer32*)(buf + 48) = shift16(flip16(header->sourcePortId), 0) | shift16(flip16(MM_STARTING_BOUNDARY_HOPS), 1); -@@ -619,7 +634,7 @@ UInteger16 msgPackManagementResponse(void *buf, MsgHeader *header, MsgManagement - *(UInteger8*)(buf + 55) = PTP_MM_GLOBAL_TIME_DATA_SET; - *(Integer32*)(buf + 56) = shift16(flip16(24), 1); - -- getTime(&internalTime); -+ getTime(&internalTime, ptpClock); - fromInternalTime(&internalTime, &externalTime, ptpClock->halfEpoch); - *(Integer32*)(buf + 60) = flip32(externalTime.seconds); - *(Integer32*)(buf + 64) = flip32(externalTime.nanoseconds); -diff --git a/src/dep/net.c b/src/dep/net.c -index 2408d55..d9db35e 100644 ---- a/src/dep/net.c -+++ b/src/dep/net.c -@@ -61,7 +61,7 @@ UInteger8 lookupCommunicationTechnology(UInteger8 communicationTechnology) - } - - UInteger32 findIface(Octet *ifaceName, UInteger8 *communicationTechnology, -- Octet *uuid, NetPath *netPath) -+ Octet *uuid, PtpClock *ptpClock) - { - #if defined(linux) - -@@ -83,7 +83,7 @@ UInteger32 findIface(Octet *ifaceName, UInteger8 *communicationTechnology, - i = 0; - memcpy(device[i].ifr_name, ifaceName, IFACE_NAME_LENGTH); - -- if(ioctl(netPath->eventSock, SIOCGIFHWADDR, &device[i]) < 0) -+ if(ioctl(ptpClock->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); -@@ -94,7 +94,7 @@ UInteger32 findIface(Octet *ifaceName, UInteger8 *communicationTechnology, - { - /* no iface specified */ - /* get list of network interfaces*/ -- if(ioctl(netPath->eventSock, SIOCGIFCONF, &data) < 0) -+ if(ioctl(ptpClock->netPath.eventSock, SIOCGIFCONF, &data) < 0) - { - PERROR("failed query network interfaces"); - return 0; -@@ -108,11 +108,11 @@ UInteger32 findIface(Octet *ifaceName, UInteger8 *communicationTechnology, - { - 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) -+ if(ioctl(ptpClock->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) -+ else if(ioctl(ptpClock->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); -@@ -134,12 +134,13 @@ UInteger32 findIface(Octet *ifaceName, UInteger8 *communicationTechnology, - return 0; - } - -- if(ioctl(netPath->eventSock, SIOCGIFADDR, &device[i]) < 0) -+ if(ioctl(ptpClock->netPath.eventSock, SIOCGIFADDR, &device[i]) < 0) - { - PERROR("failed to get ip address"); - return 0; - } -- -+ -+ ptpClock->netPath.eventSockIFR = device[i]; - return ((struct sockaddr_in *)&device[i].ifr_addr)->sin_addr.s_addr; - - #elif defined(BSD_INTERFACE_FUNCTIONS) -@@ -205,10 +206,10 @@ UInteger32 findIface(Octet *ifaceName, UInteger8 *communicationTechnology, - return FALSE; - } - -- printf("==> %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)) -- ); -+ 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); -@@ -223,7 +224,7 @@ UInteger32 findIface(Octet *ifaceName, UInteger8 *communicationTechnology, - /* must specify 'subdomainName', optionally 'ifaceName', if not then pass ifaceName == "" */ - /* returns other args */ - /* on socket options, see the 'socket(7)' and 'ip' man pages */ --Boolean netInit(NetPath *netPath, RunTimeOpts *rtOpts, PtpClock *ptpClock) -+Boolean netInit(PtpClock *ptpClock) - { - int temp, i; - struct in_addr interfaceAddr, netAddr; -@@ -231,25 +232,26 @@ Boolean netInit(NetPath *netPath, RunTimeOpts *rtOpts, PtpClock *ptpClock) - struct ip_mreq imr; - char addrStr[NET_ADDRESS_LENGTH]; - char *s; -+ Boolean useSystemTimeStamps = ptpClock->runTimeOpts.time == TIME_SYSTEM; - - 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 ) -+ if( (ptpClock->netPath.eventSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP) ) < 0 -+ || (ptpClock->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)) ) -+ if( !(interfaceAddr.s_addr = findIface(ptpClock->runTimeOpts.ifaceName, &ptpClock->port_communication_technology, -+ ptpClock->port_uuid_field, ptpClock)) ) - return FALSE; - - temp = 1; /* allow address reuse */ -- if( setsockopt(netPath->eventSock, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(int)) < 0 -- || setsockopt(netPath->generalSock, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(int)) < 0 ) -+ if( setsockopt(ptpClock->netPath.eventSock, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(int)) < 0 -+ || setsockopt(ptpClock->netPath.generalSock, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(int)) < 0 ) - { - DBG("failed to set socket reuse\n"); - } -@@ -259,14 +261,14 @@ Boolean netInit(NetPath *netPath, RunTimeOpts *rtOpts, PtpClock *ptpClock) - 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) -+ if(bind(ptpClock->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) -+ if(bind(ptpClock->netPath.generalSock, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0) - { - PERROR("failed to bind general socket"); - return FALSE; -@@ -277,21 +279,21 @@ Boolean netInit(NetPath *netPath, RunTimeOpts *rtOpts, PtpClock *ptpClock) - *(Integer16*)ptpClock->general_port_address = PTP_GENERAL_PORT; - - /* send a uni-cast address if specified (useful for testing) */ -- if(rtOpts->unicastAddress[0]) -+ if(ptpClock->runTimeOpts.unicastAddress[0]) - { -- if(!inet_aton(rtOpts->unicastAddress, &netAddr)) -+ if(!inet_aton(ptpClock->runTimeOpts.unicastAddress, &netAddr)) - { -- ERROR("failed to encode uni-cast address: %s\n", rtOpts->unicastAddress); -+ ERROR("failed to encode uni-cast address: %s\n", ptpClock->runTimeOpts.unicastAddress); - return FALSE; - } - -- netPath->unicastAddr = netAddr.s_addr; -+ ptpClock->netPath.unicastAddr = netAddr.s_addr; - } - else -- netPath->unicastAddr = 0; -+ ptpClock->netPath.unicastAddr = 0; - - /* resolve PTP subdomain */ -- if(!lookupSubdomainAddress(rtOpts->subdomainName, addrStr)) -+ if(!lookupSubdomainAddress(ptpClock->runTimeOpts.subdomainName, addrStr)) - return FALSE; - - if(!inet_aton(addrStr, &netAddr)) -@@ -300,7 +302,7 @@ Boolean netInit(NetPath *netPath, RunTimeOpts *rtOpts, PtpClock *ptpClock) - return FALSE; - } - -- netPath->multicastAddr = netAddr.s_addr; -+ ptpClock->netPath.multicastAddr = netAddr.s_addr; - - s = addrStr; - for(i = 0; i < SUBDOMAIN_ADDRESS_LENGTH; ++i) -@@ -316,16 +318,16 @@ Boolean netInit(NetPath *netPath, RunTimeOpts *rtOpts, PtpClock *ptpClock) - /* multicast send only on specified interface */ - imr.imr_multiaddr.s_addr = netAddr.s_addr; - imr.imr_interface.s_addr = 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 ) -+ if( setsockopt(ptpClock->netPath.eventSock, IPPROTO_IP, IP_MULTICAST_IF, &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0 -+ || setsockopt(ptpClock->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 ) -+ if( setsockopt(ptpClock->netPath.eventSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(struct ip_mreq)) < 0 -+ || setsockopt(ptpClock->netPath.generalSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(struct ip_mreq)) < 0 ) - { - PERROR("failed to join the multi-cast group"); - return FALSE; -@@ -333,61 +335,76 @@ Boolean netInit(NetPath *netPath, RunTimeOpts *rtOpts, PtpClock *ptpClock) - - /* set socket time-to-live to 1 */ - temp = 1; -- if( setsockopt(netPath->eventSock, IPPROTO_IP, IP_MULTICAST_TTL, &temp, sizeof(int)) < 0 -- || setsockopt(netPath->generalSock, IPPROTO_IP, IP_MULTICAST_TTL, &temp, sizeof(int)) < 0 ) -+ if( setsockopt(ptpClock->netPath.eventSock, IPPROTO_IP, IP_MULTICAST_TTL, &temp, sizeof(int)) < 0 -+ || setsockopt(ptpClock->netPath.generalSock, IPPROTO_IP, IP_MULTICAST_TTL, &temp, sizeof(int)) < 0 ) - { - PERROR("failed to set the multi-cast time-to-live"); - return FALSE; - } - -- /* enable loopback */ -- temp = 1; -- 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 ) -+ /* set loopback: needed only for time stamping with the system clock */ -+ temp = useSystemTimeStamps; -+ if( setsockopt(ptpClock->netPath.eventSock, IPPROTO_IP, IP_MULTICAST_LOOP, &temp, sizeof(int)) < 0 -+ || setsockopt(ptpClock->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() */ -- temp = 1; -- if( setsockopt(netPath->eventSock, SOL_SOCKET, SO_TIMESTAMP, &temp, sizeof(int)) < 0 -- || setsockopt(netPath->generalSock, SOL_SOCKET, SO_TIMESTAMP, &temp, sizeof(int)) < 0 ) -+ /* make timestamps available through recvmsg() (only needed for time stamping with system clock) */ -+ temp = useSystemTimeStamps; -+ if( setsockopt(ptpClock->netPath.eventSock, SOL_SOCKET, SO_TIMESTAMP, &temp, sizeof(int)) < 0 -+ || setsockopt(ptpClock->netPath.generalSock, SOL_SOCKET, SO_TIMESTAMP, &temp, sizeof(int)) < 0 ) - { - PERROR("failed to enable receive time stamps"); - return FALSE; - } - -- - return TRUE; - } - - /* shut down the UDP stuff */ --Boolean netShutdown(NetPath *netPath) -+Boolean netShutdown(PtpClock *ptpClock) - { - struct ip_mreq imr; - -- imr.imr_multiaddr.s_addr = netPath->multicastAddr; -+#ifdef HAVE_LINUX_NET_TSTAMP_H -+ if (ptpClock->runTimeOpts.time == TIME_SYSTEM_LINUX_HW && -+ ptpClock->netPath.eventSock > 0) { -+ struct hwtstamp_config hwconfig; -+ -+ ptpClock->netPath.eventSockIFR.ifr_data = (void *)&hwconfig; -+ memset(&hwconfig, 0, sizeof(&hwconfig)); -+ -+ hwconfig.tx_type = HWTSTAMP_TX_OFF; -+ hwconfig.rx_filter = HWTSTAMP_FILTER_NONE; -+ if (ioctl(ptpClock->netPath.eventSock, SIOCSHWTSTAMP, &ptpClock->netPath.eventSockIFR) < 0) { -+ PERROR("turning off net_tstamp SIOCSHWTSTAMP: %s", strerror(errno)); -+ } -+ } -+#endif -+ -+ imr.imr_multiaddr.s_addr = ptpClock->netPath.multicastAddr; - imr.imr_interface.s_addr = htonl(INADDR_ANY); - -- 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)); -+ setsockopt(ptpClock->netPath.eventSock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(struct ip_mreq)); -+ setsockopt(ptpClock->netPath.generalSock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(struct ip_mreq)); - -- netPath->multicastAddr = 0; -- netPath->unicastAddr = 0; -+ ptpClock->netPath.multicastAddr = 0; -+ ptpClock->netPath.unicastAddr = 0; - -- if(netPath->eventSock > 0) -- close(netPath->eventSock); -- netPath->eventSock = -1; -+ if(ptpClock->netPath.eventSock > 0) -+ close(ptpClock->netPath.eventSock); -+ ptpClock->netPath.eventSock = -1; - -- if(netPath->generalSock > 0) -- close(netPath->generalSock); -- netPath->generalSock = -1; -+ if(ptpClock->netPath.generalSock > 0) -+ close(ptpClock->netPath.generalSock); -+ ptpClock->netPath.generalSock = -1; - - return TRUE; - } - --int netSelect(TimeInternal *timeout, NetPath *netPath) -+int netSelect(TimeInternal *timeout, PtpClock *ptpClock) - { - int ret, nfds; - fd_set readfds; -@@ -397,8 +414,8 @@ int netSelect(TimeInternal *timeout, NetPath *netPath) - return FALSE; - - FD_ZERO(&readfds); -- FD_SET(netPath->eventSock, &readfds); -- FD_SET(netPath->generalSock, &readfds); -+ FD_SET(ptpClock->netPath.eventSock, &readfds); -+ FD_SET(ptpClock->netPath.generalSock, &readfds); - - if(timeout) - { -@@ -409,10 +426,10 @@ int netSelect(TimeInternal *timeout, NetPath *netPath) - else - tv_ptr = 0; - -- if(netPath->eventSock > netPath->generalSock) -- nfds = netPath->eventSock; -+ if(ptpClock->netPath.eventSock > ptpClock->netPath.generalSock) -+ nfds = ptpClock->netPath.eventSock; - else -- nfds = netPath->generalSock; -+ nfds = ptpClock->netPath.generalSock; - - ret = select(nfds + 1, &readfds, 0, 0, tv_ptr) > 0; - if(ret < 0) -@@ -424,18 +441,18 @@ int netSelect(TimeInternal *timeout, NetPath *netPath) - return ret; - } - --ssize_t netRecvEvent(Octet *buf, TimeInternal *time, NetPath *netPath) -+ssize_t netRecvEvent(Octet *buf, TimeInternal *time, PtpClock *ptpClock) - { -- ssize_t ret; -+ ssize_t ret = 0; - struct msghdr msg; - struct iovec vec[1]; - struct sockaddr_in from_addr; - union { - struct cmsghdr cm; -- char control[CMSG_SPACE(sizeof(struct timeval))]; -+ char control[512]; - } cmsg_un; - struct cmsghdr *cmsg; -- struct timeval *tv; -+ Boolean have_time; - - vec[0].iov_base = buf; - vec[0].iov_len = PACKET_SIZE; -@@ -452,8 +469,36 @@ ssize_t netRecvEvent(Octet *buf, TimeInternal *time, NetPath *netPath) - msg.msg_control = cmsg_un.control; - msg.msg_controllen = sizeof(cmsg_un.control); - msg.msg_flags = 0; -- -- ret = recvmsg(netPath->eventSock, &msg, MSG_DONTWAIT); -+ -+#ifdef HAVE_LINUX_NET_TSTAMP_H -+ if(ptpClock->runTimeOpts.time == TIME_SYSTEM_LINUX_HW || -+ ptpClock->runTimeOpts.time == TIME_SYSTEM_LINUX_SW) { -+ ret = recvmsg(ptpClock->netPath.eventSock, &msg, MSG_ERRQUEUE|MSG_DONTWAIT); -+ if(ret <= 0) { -+ if (errno != EAGAIN && errno != EINTR) -+ return ret; -+ } else { -+ /* -+ * strip network transport header: assumes that this is the -+ * most recently sent message -+ */ -+ if(ret > ptpClock->netPath.lastNetSendEventLength) { -+ memmove(buf, -+ buf + ret - ptpClock->netPath.lastNetSendEventLength, -+ ptpClock->netPath.lastNetSendEventLength); -+ ret = ptpClock->netPath.lastNetSendEventLength; -+ } else { -+ /* No clue what this message is. Skip it. */ -+ PERROR("received unexpected bounce via error queue"); -+ ret = 0; -+ } -+ } -+ } -+#endif /* HAVE_LINUX_NET_TSTAMP_H */ -+ -+ if(ret <= 0) { -+ ret = recvmsg(ptpClock->netPath.eventSock, &msg, MSG_DONTWAIT); -+ } - if(ret <= 0) - { - if(errno == EAGAIN || errno == EINTR) -@@ -468,11 +513,11 @@ ssize_t netRecvEvent(Octet *buf, TimeInternal *time, NetPath *netPath) - return 0; - } - -- /* get time stamp of packet */ -+ /* get time stamp of packet? */ - if(!time) - { -- ERROR("null receive time stamp argument\n"); -- return 0; -+ /* caller does not need time (probably wasn't even enabled) */ -+ return ret; - } - - if(msg.msg_flags&MSG_CTRUNC) -@@ -481,25 +526,54 @@ ssize_t netRecvEvent(Octet *buf, TimeInternal *time, NetPath *netPath) - return 0; - } - -- if(msg.msg_controllen < sizeof(cmsg_un.control)) -- { -- ERROR("received short ancillary data (%d/%d)\n", -- msg.msg_controllen, (int)sizeof(cmsg_un.control)); -- -- return 0; -- } -- -- tv = 0; -- for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) -- { -- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) -- tv = (struct timeval *)CMSG_DATA(cmsg); -+ for (cmsg = CMSG_FIRSTHDR(&msg), have_time = FALSE; -+ !have_time && cmsg != NULL; -+ cmsg = CMSG_NXTHDR(&msg, cmsg)) -+ { -+ if (cmsg->cmsg_level == SOL_SOCKET) { -+ switch (cmsg->cmsg_type) { -+ case SCM_TIMESTAMP: { -+ struct timeval *tv = (struct timeval *)CMSG_DATA(cmsg); -+ if(cmsg->cmsg_len < sizeof(*tv)) -+ { -+ ERROR("received short SCM_TIMESTAMP (%d/%d)\n", -+ cmsg->cmsg_len, sizeof(*tv)); -+ return 0; -+ } -+ time->seconds = tv->tv_sec; -+ time->nanoseconds = tv->tv_usec*1000; -+ have_time = TRUE; -+ break; -+ } -+#ifdef HAVE_LINUX_NET_TSTAMP_H -+ case SO_TIMESTAMPING: { -+ /* array of three time stamps: software, HW, raw HW */ -+ struct timespec *stamp = -+ (struct timespec *)CMSG_DATA(cmsg); -+ if(cmsg->cmsg_len < sizeof(*stamp) * 3) -+ { -+ ERROR("received short SO_TIMESTAMPING (%d/%d)\n", -+ cmsg->cmsg_len, (int)sizeof(*stamp) * 3); -+ return 0; -+ } -+ if (ptpClock->runTimeOpts.time == TIME_SYSTEM_LINUX_HW) { -+ /* look at second element in array which is the HW tstamp */ -+ stamp++; -+ } -+ if (stamp->tv_sec && stamp->tv_nsec) { -+ time->seconds = stamp->tv_sec; -+ time->nanoseconds = stamp->tv_nsec; -+ have_time = TRUE; -+ } -+ break; -+ } -+#endif /* HAVE_LINUX_NET_TSTAMP_H */ -+ } -+ } - } - -- if(tv) -+ if(have_time) - { -- time->seconds = tv->tv_sec; -- time->nanoseconds = tv->tv_usec*1000; - DBGV("kernel recv time stamp %us %dns\n", time->seconds, time->nanoseconds); - } - else -@@ -507,20 +581,20 @@ ssize_t netRecvEvent(Octet *buf, TimeInternal *time, NetPath *netPath) - /* 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("no recieve time stamp\n"); -+ DBG("no receive time stamp\n"); - return 0; - } - - return ret; - } - --ssize_t netRecvGeneral(Octet *buf, NetPath *netPath) -+ssize_t netRecvGeneral(Octet *buf, PtpClock *ptpClock) - { - ssize_t ret; - struct sockaddr_in addr; - socklen_t addr_len = sizeof(struct sockaddr_in); - -- ret = recvfrom(netPath->generalSock, buf, PACKET_SIZE, MSG_DONTWAIT, (struct sockaddr *)&addr, &addr_len); -+ ret = recvfrom(ptpClock->netPath.generalSock, buf, PACKET_SIZE, MSG_DONTWAIT, (struct sockaddr *)&addr, &addr_len); - if(ret <= 0) - { - if(errno == EAGAIN || errno == EINTR) -@@ -532,24 +606,95 @@ ssize_t netRecvGeneral(Octet *buf, NetPath *netPath) - return ret; - } - --ssize_t netSendEvent(Octet *buf, UInteger16 length, NetPath *netPath) -+ssize_t netSendEvent(Octet *buf, UInteger16 length, TimeInternal *sendTimeStamp, PtpClock *ptpClock) - { - ssize_t ret; - struct sockaddr_in addr; - - addr.sin_family = AF_INET; - addr.sin_port = htons(PTP_EVENT_PORT); -- addr.sin_addr.s_addr = netPath->multicastAddr; -- -- ret = sendto(netPath->eventSock, buf, length, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); -+ addr.sin_addr.s_addr = ptpClock->netPath.multicastAddr; -+ ptpClock->netPath.lastNetSendEventLength = length; -+ -+ ret = sendto(ptpClock->netPath.eventSock, buf, length, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); - if(ret <= 0) - DBG("error sending multi-cast event message\n"); -- -- if(netPath->unicastAddr) -+ else if(sendTimeStamp) -+ { -+ /* -+ * The packet is assumed to generated a time stamp soon. For -+ * simplicity reasons wait until it got time stamped. -+ * -+ * Tests under load showed that the time stamp was not -+ * always generated (packet dropped inside the driver?). -+ * This situation is handled by trying only for a while, -+ * then giving up and returning a zero timestamp. -+ */ -+#undef DEBUG_PACKET_LOSS -+#ifdef DEBUG_PACKET_LOSS -+ /* to debug the error case drop every 10th outgoing packet */ -+ static int debugPacketCounter; -+ debugPacketCounter++; -+#endif -+ sendTimeStamp->seconds = 0; -+ sendTimeStamp->nanoseconds = 0; -+ -+ /* fast path: get send time stamp directly */ -+ if( -+#ifdef DEBUG_PACKET_LOSS -+ (debugPacketCounter % 10) && -+#endif -+ getSendTime(sendTimeStamp, ptpClock)) { -+ DBGV("got send time stamp in first attempt\n"); -+ } else { -+ /* -+ * need to wait for it: need to check system time, counting -+ * the number of nanoSleep()s is too inaccurate because it -+ * each call sleeps much longer than requested -+ */ -+ TimeInternal start, now; -+ timerNow(&start); -+ while(TRUE) { -+ Boolean gotTime; -+ TimeInternal delayAfterPacketSend; -+ delayAfterPacketSend.seconds = 0; -+ delayAfterPacketSend.nanoseconds = 1000; -+ nanoSleep(&delayAfterPacketSend); -+ gotTime = -+#ifdef DEBUG_PACKET_LOSS -+ (debugPacketCounter % 10) && -+#endif -+ getSendTime(sendTimeStamp, ptpClock); -+ timerNow(&now); -+ subTime(&now, &now, &start); -+ /* 0.5 seconds is the maximum we wait... */ -+ if(gotTime || now.seconds >= 1 || now.nanoseconds >= 500000000) { -+ DBGV("%s send time stamp after %d.%09ds\n", -+ gotTime ? "got" : "failed to get", -+ now.seconds, now.nanoseconds); -+#ifdef PTPD_DBGV -+ if (!gotTime) { -+ /* unpack the message because that logs its content */ -+ MsgHeader header; -+ DBGV("unpacking message without time stamp\n"); -+ msgUnpackHeader(buf, &header); -+ } -+#endif -+ break; -+ } -+ } -+ } -+ } -+ -+ /** -+ * @TODO: why is the packet sent twice when unicast is enabled? -+ * If that's correct, deal with the send time stamps. -+ */ -+ if(ptpClock->netPath.unicastAddr) - { -- addr.sin_addr.s_addr = netPath->unicastAddr; -+ addr.sin_addr.s_addr = ptpClock->netPath.unicastAddr; - -- ret = sendto(netPath->eventSock, buf, length, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); -+ ret = sendto(ptpClock->netPath.eventSock, buf, length, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); - if(ret <= 0) - DBG("error sending uni-cast event message\n"); - } -@@ -557,24 +702,24 @@ ssize_t netSendEvent(Octet *buf, UInteger16 length, NetPath *netPath) - return ret; - } - --ssize_t netSendGeneral(Octet *buf, UInteger16 length, NetPath *netPath) -+ssize_t netSendGeneral(Octet *buf, UInteger16 length, PtpClock *ptpClock) - { - ssize_t ret; - struct sockaddr_in addr; - - addr.sin_family = AF_INET; - addr.sin_port = htons(PTP_GENERAL_PORT); -- addr.sin_addr.s_addr = netPath->multicastAddr; -+ addr.sin_addr.s_addr = ptpClock->netPath.multicastAddr; - -- ret = sendto(netPath->generalSock, buf, length, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); -+ ret = sendto(ptpClock->netPath.generalSock, buf, length, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); - if(ret <= 0) - DBG("error sending multi-cast general message\n"); - -- if(netPath->unicastAddr) -+ if(ptpClock->netPath.unicastAddr) - { -- addr.sin_addr.s_addr = netPath->unicastAddr; -+ addr.sin_addr.s_addr = ptpClock->netPath.unicastAddr; - -- ret = sendto(netPath->eventSock, buf, length, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); -+ ret = sendto(ptpClock->netPath.eventSock, buf, length, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); - if(ret <= 0) - DBG("error sending uni-cast general message\n"); - } -diff --git a/src/dep/ptpd_dep.h b/src/dep/ptpd_dep.h -index b898e3e..c018d64 100644 ---- a/src/dep/ptpd_dep.h -+++ b/src/dep/ptpd_dep.h -@@ -10,6 +10,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -19,22 +20,58 @@ - #include - #include - -+#ifdef HAVE_LINUX_NET_TSTAMP_H -+#include "asm/types.h" -+#include "linux/net_tstamp.h" -+#include "linux/errqueue.h" -+ -+#ifndef SO_TIMESTAMPNS -+# define SO_TIMESTAMPNS 35 -+#endif -+ -+#ifndef SO_TIMESTAMPING -+# define SO_TIMESTAMPING 37 -+#endif -+ -+#ifndef SIOCGSTAMPNS -+# define SIOCGSTAMPNS 0x8907 -+#endif -+ -+#ifndef SIOCSHWTSTAMP -+# define SIOCSHWTSTAMP 0x89b0 -+#endif -+ -+#endif /* HAVE_LINUX_NET_TSTAMP_H */ -+ -+/** -+ * route output either into syslog or stderr, depending on global useSyslog settings -+ * @param priority same as for syslog() -+ * @param format printf style format string -+ */ -+void message(int priority, const char *format, ...); -+ -+/** -+ * if TRUE then message() will print via syslog(); no init required and -+ * can be reverted to FALSE at any time -+ */ -+extern Boolean useSyslog; - - /* system messages */ --#define ERROR(x, ...) fprintf(stderr, "(ptpd error) " x, ##__VA_ARGS__) --#define PERROR(x, ...) fprintf(stderr, "(ptpd error) " x ": %m\n", ##__VA_ARGS__) --#define NOTIFY(x, ...) fprintf(stderr, "(ptpd notice) " x, ##__VA_ARGS__) -+#define ERROR(x, ...) message(LOG_ERR, x, ##__VA_ARGS__) -+#define PERROR(x, ...) message(LOG_ERR, x ": %m\n", ##__VA_ARGS__) -+#define NOTIFY(x, ...) message(LOG_NOTICE, x, ##__VA_ARGS__) -+#define INFO(x, ...) message(LOG_INFO, x, ##__VA_ARGS__) - - /* debug messages */ - #ifdef PTPD_DBGV - #define PTPD_DBG --#define DBGV(x, ...) fprintf(stderr, "(ptpd debug) " x, ##__VA_ARGS__) -+#define DBGV(x, ...) message(LOG_DEBUG, x, ##__VA_ARGS__) - #else - #define DBGV(x, ...) - #endif - - #ifdef PTPD_DBG --#define DBG(x, ...) fprintf(stderr, "(ptpd debug) " x, ##__VA_ARGS__) -+#define DBG(x, ...) message(LOG_DEBUG, x, ##__VA_ARGS__) - #else - #define DBG(x, ...) - #endif -@@ -85,11 +122,11 @@ void msgUnpackDelayReq(void*,MsgDelayReq*); - void msgUnpackFollowUp(void*,MsgFollowUp*); - void msgUnpackDelayResp(void*,MsgDelayResp*); - void msgUnpackManagement(void*,MsgManagement*); --UInteger8 msgUnloadManagement(void*,MsgManagement*,PtpClock*,RunTimeOpts*); -+UInteger8 msgUnloadManagement(void*,MsgManagement*,PtpClock*); - void msgUnpackManagementPayload(void *buf, MsgManagement *manage); - void msgPackHeader(void*,PtpClock*); --void msgPackSync(void*,Boolean,TimeRepresentation*,PtpClock*); --void msgPackDelayReq(void*,Boolean,TimeRepresentation*,PtpClock*); -+void msgPackSync(void*,Boolean,Boolean,TimeRepresentation*,PtpClock*); -+void msgPackDelayReq(void*,Boolean,Boolean,TimeRepresentation*,PtpClock*); - void msgPackFollowUp(void*,UInteger16,TimeRepresentation*,PtpClock*); - void msgPackDelayResp(void*,MsgHeader*,TimeRepresentation*,PtpClock*); - UInteger16 msgPackManagement(void*,MsgManagement*,PtpClock*); -@@ -97,21 +134,21 @@ UInteger16 msgPackManagementResponse(void*,MsgHeader*,MsgManagement*,PtpClock*); - - /* net.c */ - /* linux API dependent */ --Boolean netInit(NetPath*,RunTimeOpts*,PtpClock*); --Boolean netShutdown(NetPath*); --int netSelect(TimeInternal*,NetPath*); --ssize_t netRecvEvent(Octet*,TimeInternal*,NetPath*); --ssize_t netRecvGeneral(Octet*,NetPath*); --ssize_t netSendEvent(Octet*,UInteger16,NetPath*); --ssize_t netSendGeneral(Octet*,UInteger16,NetPath*); -+Boolean netInit(PtpClock*); -+Boolean netShutdown(PtpClock*); -+int netSelect(TimeInternal*,PtpClock*); -+ssize_t netRecvEvent(Octet*,TimeInternal*,PtpClock*); -+ssize_t netRecvGeneral(Octet*,PtpClock*); -+ssize_t netSendEvent(Octet*,UInteger16,TimeInternal*,PtpClock*); -+ssize_t netSendGeneral(Octet*,UInteger16,PtpClock*); - - /* servo.c */ --void initClock(RunTimeOpts*,PtpClock*); -+void initClock(PtpClock*); - void updateDelay(TimeInternal*,TimeInternal*, -- one_way_delay_filter*,RunTimeOpts*,PtpClock*); -+ one_way_delay_filter*,PtpClock*); - void updateOffset(TimeInternal*,TimeInternal*, -- offset_from_master_filter*,RunTimeOpts*,PtpClock*); --void updateClock(RunTimeOpts*,PtpClock*); -+ offset_from_master_filter*,PtpClock*); -+void updateClock(PtpClock*); - - /* startup.c */ - /* unix API dependent */ -@@ -120,20 +157,119 @@ void ptpdShutdown(void); - - /* sys.c */ - /* unix API dependent */ --void displayStats(RunTimeOpts*,PtpClock*); --Boolean nanoSleep(TimeInternal*); --void getTime(TimeInternal*); --void setTime(TimeInternal*); -+void displayStats(PtpClock*); - UInteger16 getRand(UInteger32*); --Boolean adjFreq(Integer32); - --/* timer.c */ -+/** -+ * @defgroup time Time Source -+ * -+ * Interface to the clock which is used to time stamp -+ * packages and which is adjusted by PTPd. -+ * -+ * The intention is to hide different actual implementations -+ * behind one interface: -+ * - system time (gettimeofday()) -+ * - NIC time (timer inside the network hardware) -+ * - ... -+ */ -+/*@{*/ -+/** @file time.c */ -+Boolean initTime(PtpClock*); -+void getTime(TimeInternal*, PtpClock*); -+void setTime(TimeInternal*, PtpClock*); -+ -+/** -+ * Adjusts the time, ideally by varying the clock rate. -+ * -+ * @param adj frequency adjustment: a time source which supports that ignores the offset -+ * @param offset offset (reference time - local time) from last measurement: a time source which -+ * cannot adjust the frequence must fall back to this cruder method (may be NULL) -+ */ -+void adjTime(Integer32 adj, TimeInternal *offset, PtpClock*); -+ -+/** -+ * Adjusts the time by shifting the clock. -+ * -+ * @param offset this value must be substracted from clock (might be negative) -+ */ -+void adjTimeOffset(TimeInternal *offset, PtpClock*); -+ -+/** -+ * Gets the time when the latest outgoing packet left the host. -+ * -+ * There is no way to identify the packet the time stamp belongs to, -+ * so this must be called after sending each packet until the time -+ * stamp for the packet is available. This can be some (hopefully -+ * small) time after the packet was passed to the IP stack. -+ * -+ * There is no mechanism either to determine packet loss and thus a -+ * time stamp which never becomes available. -+ * -+ * @todo Can such packet loss occur? -+ * -+ * Does not work with TIME_SYSTEM. -+ * -+ * @retval sendTimeStamp set to the time when the packet left the host -+ * @return TRUE if the time stamp was available -+ */ -+Boolean getSendTime(TimeInternal *sendTimeStamp, PtpClock*); -+ -+/** -+ * Gets the time when the packet identified by the given attributes -+ * was received by the host. -+ * -+ * Because the arrival of packets is out of the control of PTPd, the -+ * time stamping must support unique identification of which time -+ * stamp belongs to which packet. -+ * -+ * Due to packet loss in the receive queue, there can be time stamps -+ * without IP packets. getReceiveTime() automatically discards stale -+ * time stamps, including the ones that where returned by -+ * getReceiveTime(). This implies that there is not guarantee that -+ * calling getReceiveTime() more than once for the same packet -+ * will always return a result. -+ * -+ * Due to hardware limitations only one time stamp might be stored -+ * until queried by the NIC driver; this can lead to packets without -+ * time stamp. This needs to be handled by the caller of -+ * getReceiveTime(), for example by ignoring the packet. -+ * -+ * Does not work with TIME_SYSTEM. -+ * -+ * @retval recvTimeStamp set to the time when the packet entered the host, if available -+ * @return TRUE if the time stamp was available -+ */ -+Boolean getReceiveTime(TimeInternal *recvTimeStamp, -+ Octet sourceUuid[PTP_UUID_LENGTH], -+ UInteger16 sequenceId, PtpClock*); -+ -+/** called regularly every second while process is idle */ -+void timeNoActivity(PtpClock*); -+ -+/** -+ * called while still in the old state and before entering a new one: -+ * transition is relevant for hardware assisted timing -+ */ -+void timeToState(UInteger8 state, PtpClock *ptpClock); -+ -+/*@}*/ -+ -+/** -+ * @defgroup timer regular wakeup at different timer intervals -+ * -+ * This timing is always done using the system time of the host. -+ */ -+/*@{*/ -+/** @file timer.c */ - void initTimer(void); - void timerUpdate(IntervalTimer*); - void timerStop(UInteger16,IntervalTimer*); - void timerStart(UInteger16,UInteger16,IntervalTimer*); - Boolean timerExpired(UInteger16,IntervalTimer*); -- -+Boolean nanoSleep(TimeInternal*); -+/** gets the current system time */ -+void timerNow(TimeInternal*); -+/*@}*/ - - #endif - -diff --git a/src/dep/servo.c b/src/dep/servo.c -index ae2da3b..3a3458e 100644 ---- a/src/dep/servo.c -+++ b/src/dep/servo.c -@@ -1,8 +1,8 @@ - #include "../ptpd.h" - --void initClock(RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void initClock(PtpClock *ptpClock) - { -- DBG("initClock\n"); -+ DBG("%sinitClock\n", ptpClock->name); - - /* clear vars */ - ptpClock->master_to_slave_delay.seconds = ptpClock->master_to_slave_delay.nanoseconds = 0; -@@ -10,20 +10,23 @@ void initClock(RunTimeOpts *rtOpts, PtpClock *ptpClock) - ptpClock->observed_variance = 0; - ptpClock->observed_drift = 0; /* clears clock servo accumulator (the I term) */ - ptpClock->owd_filt.s_exp = 0; /* clears one-way delay filter */ -- ptpClock->halfEpoch = ptpClock->halfEpoch || rtOpts->halfEpoch; -- rtOpts->halfEpoch = 0; -+ ptpClock->halfEpoch = ptpClock->halfEpoch || ptpClock->runTimeOpts.halfEpoch; -+ ptpClock->runTimeOpts.halfEpoch = 0; - - /* level clock */ -- if(!rtOpts->noAdjust) -- adjFreq(0); -+ if(!ptpClock->runTimeOpts.noAdjust) -+ adjTime(0, NULL, ptpClock); - } - - void updateDelay(TimeInternal *send_time, TimeInternal *recv_time, -- one_way_delay_filter *owd_filt, RunTimeOpts *rtOpts, PtpClock *ptpClock) -+ one_way_delay_filter *owd_filt, PtpClock *ptpClock) - { - Integer16 s; - -- DBGV("updateDelay\n"); -+ DBGV("%supdateDelay send %10ds %11dns recv %10ds %11dns\n", -+ ptpClock->name, -+ send_time->seconds, send_time->nanoseconds, -+ recv_time->seconds, recv_time->nanoseconds); - - /* calc 'slave_to_master_delay' */ - subTime(&ptpClock->slave_to_master_delay, recv_time, send_time); -@@ -32,6 +35,11 @@ void updateDelay(TimeInternal *send_time, TimeInternal *recv_time, - addTime(&ptpClock->one_way_delay, &ptpClock->master_to_slave_delay, &ptpClock->slave_to_master_delay); - ptpClock->one_way_delay.seconds /= 2; - ptpClock->one_way_delay.nanoseconds /= 2; -+ -+ DBGV("%supdateDelay slave_to_master_delay %10ds %11dns one_way_delay %10ds %11dns\n", -+ ptpClock->name, -+ ptpClock->slave_to_master_delay.seconds, ptpClock->slave_to_master_delay.nanoseconds, -+ ptpClock->one_way_delay.seconds, ptpClock->one_way_delay.nanoseconds); - - if(ptpClock->one_way_delay.seconds) - { -@@ -41,7 +49,7 @@ void updateDelay(TimeInternal *send_time, TimeInternal *recv_time, - } - - /* avoid overflowing filter */ -- s = rtOpts->s; -+ s = ptpClock->runTimeOpts.s; - while(abs(owd_filt->y)>>(31-s)) - --s; - -@@ -60,13 +68,16 @@ void updateDelay(TimeInternal *send_time, TimeInternal *recv_time, - owd_filt->nsec_prev = ptpClock->one_way_delay.nanoseconds; - ptpClock->one_way_delay.nanoseconds = owd_filt->y; - -- DBG("delay filter %d, %d\n", owd_filt->y, owd_filt->s_exp); -+ DBG("%sdelay filter %d, %d\n", ptpClock->name, 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) -+ offset_from_master_filter *ofm_filt, PtpClock *ptpClock) - { -- DBGV("updateOffset\n"); -+ DBGV("%supdateOffset send %10ds %11dns recv %10ds %11dns\n", -+ ptpClock->name, -+ send_time->seconds, send_time->nanoseconds, -+ recv_time->seconds, recv_time->nanoseconds); - - /* calc 'master_to_slave_delay' */ - subTime(&ptpClock->master_to_slave_delay, recv_time, send_time); -@@ -74,6 +85,11 @@ void updateOffset(TimeInternal *send_time, TimeInternal *recv_time, - /* update 'offset_from_master' */ - subTime(&ptpClock->offset_from_master, &ptpClock->master_to_slave_delay, &ptpClock->one_way_delay); - -+ DBGV("%supdateOffset master_to_slave_delay %10ds %11dns offset_from_master %10ds %11dns\n", -+ ptpClock->name, -+ ptpClock->master_to_slave_delay.seconds, ptpClock->master_to_slave_delay.nanoseconds, -+ ptpClock->offset_from_master.seconds, ptpClock->offset_from_master.nanoseconds); -+ - if(ptpClock->offset_from_master.seconds) - { - /* cannot filter with secs, clear filter */ -@@ -86,32 +102,29 @@ void updateOffset(TimeInternal *send_time, TimeInternal *recv_time, - ofm_filt->nsec_prev = ptpClock->offset_from_master.nanoseconds; - ptpClock->offset_from_master.nanoseconds = ofm_filt->y; - -- DBGV("offset filter %d\n", ofm_filt->y); -+ DBGV("%soffset filter %d\n", ptpClock->name, ofm_filt->y); - } - --void updateClock(RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void updateClock(PtpClock *ptpClock) - { - Integer32 adj; -- TimeInternal timeTmp; - -- DBGV("updateClock\n"); -+ DBGV("%supdateClock\n", ptpClock->name); - - if(ptpClock->offset_from_master.seconds) - { - /* if secs, reset clock or set freq adjustment to max */ -- if(!rtOpts->noAdjust) -+ if(!ptpClock->runTimeOpts.noAdjust || ptpClock->nic_instead_of_system) - { -- if(!rtOpts->noResetClock) -+ if(!ptpClock->runTimeOpts.noResetClock) - { -- getTime(&timeTmp); -- subTime(&timeTmp, &timeTmp, &ptpClock->offset_from_master); -- setTime(&timeTmp); -- initClock(rtOpts, ptpClock); -+ adjTimeOffset(&ptpClock->offset_from_master, ptpClock); -+ initClock(ptpClock); - } - else - { - adj = ptpClock->offset_from_master.nanoseconds > 0 ? ADJ_FREQ_MAX : -ADJ_FREQ_MAX; -- adjFreq(-adj); -+ adjTime(-adj, &ptpClock->offset_from_master, ptpClock); - } - } - } -@@ -120,13 +133,13 @@ void updateClock(RunTimeOpts *rtOpts, PtpClock *ptpClock) - /* the PI controller */ - - /* no negative or zero attenuation */ -- if(rtOpts->ap < 1) -- rtOpts->ap = 1; -- if(rtOpts->ai < 1) -- rtOpts->ai = 1; -+ if(ptpClock->runTimeOpts.ap < 1) -+ ptpClock->runTimeOpts.ap = 1; -+ if(ptpClock->runTimeOpts.ai < 1) -+ ptpClock->runTimeOpts.ai = 1; - - /* the accumulator for the I component */ -- ptpClock->observed_drift += ptpClock->offset_from_master.nanoseconds/rtOpts->ai; -+ ptpClock->observed_drift += ptpClock->offset_from_master.nanoseconds/ptpClock->runTimeOpts.ai; - - /* clamp the accumulator to ADJ_FREQ_MAX for sanity */ - if(ptpClock->observed_drift > ADJ_FREQ_MAX) -@@ -134,24 +147,28 @@ void updateClock(RunTimeOpts *rtOpts, PtpClock *ptpClock) - else if(ptpClock->observed_drift < -ADJ_FREQ_MAX) - ptpClock->observed_drift = -ADJ_FREQ_MAX; - -- adj = ptpClock->offset_from_master.nanoseconds/rtOpts->ap + ptpClock->observed_drift; -+ adj = ptpClock->offset_from_master.nanoseconds/ptpClock->runTimeOpts.ap + ptpClock->observed_drift; - - /* apply controller output as a clock tick rate adjustment */ -- if(!rtOpts->noAdjust) -- adjFreq(-adj); -+ if(!ptpClock->runTimeOpts.noAdjust || ptpClock->nic_instead_of_system) -+ adjTime(-adj, &ptpClock->offset_from_master, ptpClock); - } - -- if(rtOpts->displayStats) -- displayStats(rtOpts, ptpClock); -+ if(ptpClock->runTimeOpts.displayStats) -+ displayStats(ptpClock); - -- DBGV("master-to-slave delay: %10ds %11dns\n", -+ DBGV("%smaster-to-slave delay: %10ds %11dns\n", -+ ptpClock->name, - ptpClock->master_to_slave_delay.seconds, ptpClock->master_to_slave_delay.nanoseconds); -- DBGV("slave-to-master delay: %10ds %11dns\n", -+ DBGV("%sslave-to-master delay: %10ds %11dns\n", -+ ptpClock->name, - ptpClock->slave_to_master_delay.seconds, ptpClock->slave_to_master_delay.nanoseconds); -- DBGV("one-way delay: %10ds %11dns\n", -+ DBGV("%sone-way delay: %10ds %11dns\n", -+ ptpClock->name, - ptpClock->one_way_delay.seconds, ptpClock->one_way_delay.nanoseconds); -- DBG("offset from master: %10ds %11dns\n", -+ DBG("%soffset from master: %10ds %11dns\n", -+ ptpClock->name, - ptpClock->offset_from_master.seconds, ptpClock->offset_from_master.nanoseconds); -- DBG("observed drift: %10d\n", ptpClock->observed_drift); -+ DBG("%sobserved drift: %10d\n", ptpClock->name, ptpClock->observed_drift); - } - -diff --git a/src/dep/startup.c b/src/dep/startup.c -index b9bef63..b3a76de 100644 ---- a/src/dep/startup.c -+++ b/src/dep/startup.c -@@ -35,7 +35,7 @@ void catch_close(int sig) - - void ptpdShutdown() - { -- netShutdown(&ptpClock->netPath); -+ netShutdown(ptpClock); - - free(ptpClock->foreign); - free(ptpClock); -@@ -44,9 +44,9 @@ void ptpdShutdown() - PtpClock * ptpdStartup(int argc, char **argv, Integer16 *ret, RunTimeOpts *rtOpts) - { - int c, fd = -1, nondaemon = 0, noclose = 0; -- -+ - /* parse command line arguments */ -- while( (c = getopt(argc, argv, "?cf:dDxta:w:b:u:l:o:e:hy:m:gps:i:v:n:k:r")) != -1 ) { -+ while( (c = getopt(argc, argv, "?cf:dDz:xta:w:b:u:l:o:e:hy:m:gps:i:v:n:k:r")) != -1 ) { - switch(c) { - case '?': - printf( -@@ -54,10 +54,21 @@ PtpClock * ptpdStartup(int argc, char **argv, Integer16 *ret, RunTimeOpts *rtOpt - "-? show this page\n" - "\n" - "-c run in command line (non-daemon) mode\n" --"-f FILE send output to FILE\n" -+"-f FILE send output to FILE (FILE=syslog writes into the system log)\n" - "-d display stats\n" - "-D display stats in .csv format\n" - "\n" -+"-z CLOCK selects which timer is used and controlled\n" -+" system = the host's system time (default)\n" -+" nic = the network interface\n" -+" both = NIC time is synchronized over the network via PTP\n" -+" and system time against NIC via local PTP\n" -+" assisted = system time is synchronized across network via\n" -+" NIC assisted time stamping\n" -+" linux_hw = synchronize system time with Linux kernel assistance\n" -+" via net_tstamp API, uses NIC time stamping\n" -+" linux_sw = synchronize system time with Linux kernel assistance\n" -+" via net_tstamp API, uses software time stamping\n" - "-x do not reset the clock if off by more than one second\n" - "-t do not adjust the system clock\n" - "-a NUMBER,NUMBER specify clock servo P and I attenuations\n" -@@ -93,14 +104,21 @@ PtpClock * ptpdStartup(int argc, char **argv, Integer16 *ret, RunTimeOpts *rtOpt - break; - - case 'f': -- if((fd = creat(optarg, 0400)) != -1) -+ if(!strcmp(optarg, "syslog")) - { -- dup2(fd, STDOUT_FILENO); -- dup2(fd, STDERR_FILENO); -- noclose = 1; -+ useSyslog = TRUE; - } - else -- PERROR("could not open output file"); -+ { -+ if((fd = creat(optarg, 0400)) != -1) -+ { -+ dup2(fd, STDOUT_FILENO); -+ dup2(fd, STDERR_FILENO); -+ noclose = 1; -+ } -+ else -+ PERROR("could not open output file"); -+ } - break; - - case 'd': -@@ -110,12 +128,42 @@ PtpClock * ptpdStartup(int argc, char **argv, Integer16 *ret, RunTimeOpts *rtOpt - break; - - case 'D': --#ifndef PTPD_DBG - rtOpts->displayStats = TRUE; - rtOpts->csvStats = TRUE; --#endif - break; - -+ case 'z': -+ if(!strcasecmp(optarg, "nic")) -+ { -+ rtOpts->time = TIME_NIC; -+ } -+ else if(!strcasecmp(optarg, "system")) -+ { -+ rtOpts->time = TIME_SYSTEM; -+ } -+ else if(!strcasecmp(optarg, "both")) -+ { -+ rtOpts->time = TIME_BOTH; -+ } -+ else if(!strcasecmp(optarg, "assisted")) -+ { -+ rtOpts->time = TIME_SYSTEM_ASSISTED; -+ } -+ else if(!strcasecmp(optarg, "linux_hw")) -+ { -+ rtOpts->time = TIME_SYSTEM_LINUX_HW; -+ } -+ else if(!strcasecmp(optarg, "linux_sw")) -+ { -+ rtOpts->time = TIME_SYSTEM_LINUX_SW; -+ } -+ else -+ { -+ ERROR("Unsupported -z clock '%s'.\n", optarg); -+ *ret = 1; -+ } -+ break; -+ - case 'x': - rtOpts->noResetClock = TRUE; - break; -@@ -222,6 +270,7 @@ PtpClock * ptpdStartup(int argc, char **argv, Integer16 *ret, RunTimeOpts *rtOpt - } - - ptpClock = (PtpClock*)calloc(1, sizeof(PtpClock)); -+ ptpClock->name = ""; - if(!ptpClock) - { - PERROR("failed to allocate memory for protocol engine data"); -@@ -230,6 +279,7 @@ PtpClock * ptpdStartup(int argc, char **argv, Integer16 *ret, RunTimeOpts *rtOpt - } - else - { -+ ptpClock->runTimeOpts = *rtOpts; - 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) -diff --git a/src/dep/sys.c b/src/dep/sys.c -index 5206d34..1e01d82 100644 ---- a/src/dep/sys.c -+++ b/src/dep/sys.c -@@ -1,18 +1,63 @@ - /* sys.c */ - - #include "../ptpd.h" -+#include - --void displayStats(RunTimeOpts *rtOpts, PtpClock *ptpClock) -+Boolean useSyslog; -+ -+void message(int priority, const char *format, ...) -+{ -+ va_list ap; -+ -+ va_start(ap, format); -+ if (useSyslog) -+ { -+ static Boolean logOpened; -+ if (!logOpened) -+ { -+ openlog("ptpd", 0, LOG_USER); -+ logOpened = TRUE; -+ } -+ vsyslog(priority, format, ap); -+ } -+ else -+ { -+ fprintf(stderr, "(ptpd %s) ", -+ 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 ? "debug" : -+ "???"); -+ vfprintf(stderr, format, ap); -+ } -+ va_end(ap); -+} -+ -+static size_t sprintfTime(PtpClock *ptpClock, char *buffer, TimeInternal *t, const char *prefix) -+{ -+ return sprintf(buffer, -+ ", %s%s%d.%09d", -+ ptpClock->runTimeOpts.csvStats ? "" : prefix, -+ (t->seconds < 0 || t->nanoseconds < 0) ? "-" : "", -+ abs(t->seconds), -+ abs(t->nanoseconds)); -+} -+ -+void displayStats(PtpClock *ptpClock) - { - static int start = 1; -- static char sbuf[SCREEN_BUFSZ]; -+ static char sbuf[2 * SCREEN_BUFSZ]; - char *s; - int len = 0; - -- if(start && rtOpts->csvStats) -+ if(start && ptpClock->runTimeOpts.csvStats) - { - start = 0; -- printf("state, one way delay, offset from master, drift, variance"); -+ INFO("state, one way delay, offset from master, drift, variance, clock adjustment (ppb), slave to master delay, master to slave delay\n"); - fflush(stdout); - } - -@@ -32,63 +77,36 @@ void displayStats(RunTimeOpts *rtOpts, PtpClock *ptpClock) - default: s = "?"; break; - } - -- len += sprintf(sbuf + len, "%s%s", rtOpts->csvStats ? "\n": "\rstate: ", s); -+ len += sprintf(sbuf + len, "%s%s%s", ptpClock->runTimeOpts.csvStats ? "": "state: ", ptpClock->name, s); - -- if(ptpClock->port_state == PTP_SLAVE) -+ if(ptpClock->port_state == PTP_SLAVE || -+ (ptpClock->port_state == PTP_MASTER && ptpClock->nic_instead_of_system)) - { -- len += sprintf(sbuf + len, -- ", %s%d.%09d" ", %s%d.%09d", -- rtOpts->csvStats ? "" : "owd: ", -- ptpClock->one_way_delay.seconds, -- abs(ptpClock->one_way_delay.nanoseconds), -- rtOpts->csvStats ? "" : "ofm: ", -- ptpClock->offset_from_master.seconds, -- abs(ptpClock->offset_from_master.nanoseconds)); -- -+ len += sprintfTime(ptpClock, sbuf + len, &ptpClock->one_way_delay, "owd: "); -+ len += sprintfTime(ptpClock, sbuf + len, &ptpClock->offset_from_master, "ofm: "); -+ - len += sprintf(sbuf + len, - ", %s%d" ", %s%d", -- rtOpts->csvStats ? "" : "drift: ", ptpClock->observed_drift, -- rtOpts->csvStats ? "" : "var: ", ptpClock->observed_variance); -+ ptpClock->runTimeOpts.csvStats ? "" : "drift: ", ptpClock->observed_drift, -+ ptpClock->runTimeOpts.csvStats ? "" : "var: ", ptpClock->observed_variance); -+ -+ len += sprintf(sbuf + len, -+ ", %s%ld", -+ ptpClock->runTimeOpts.csvStats ? "" : "adj: ", ptpClock->adj); -+ -+ len += sprintfTime(ptpClock, sbuf + len, &ptpClock->slave_to_master_delay, "stm: "); -+ len += sprintfTime(ptpClock, sbuf + len, &ptpClock->master_to_slave_delay, "mts: "); - } -- -- write(1, sbuf, rtOpts->csvStats ? len : SCREEN_MAXSZ + 1); --} - --Boolean nanoSleep(TimeInternal *t) --{ -- struct timespec ts, tr; -- -- ts.tv_sec = t->seconds; -- ts.tv_nsec = t->nanoseconds; -- -- if(nanosleep(&ts, &tr) < 0) -+ if (ptpClock->runTimeOpts.csvStats) - { -- t->seconds = tr.tv_sec; -- t->nanoseconds = tr.tv_nsec; -- return FALSE; -+ INFO("%s\n", sbuf); -+ } -+ else -+ { -+ /* overwrite the same line over and over again... */ -+ INFO("%.*s\r", SCREEN_MAXSZ + 1, sbuf); - } -- -- return TRUE; --} -- --void getTime(TimeInternal *time) --{ -- struct timeval tv; -- -- gettimeofday(&tv, 0); -- time->seconds = tv.tv_sec; -- time->nanoseconds = tv.tv_usec*1000; --} -- --void setTime(TimeInternal *time) --{ -- struct timeval tv; -- -- tv.tv_sec = time->seconds; -- tv.tv_usec = time->nanoseconds/1000; -- settimeofday(&tv, 0); -- -- NOTIFY("resetting system clock to %ds %dns\n", time->seconds, time->nanoseconds); - } - - UInteger16 getRand(UInteger32 *seed) -@@ -96,18 +114,3 @@ UInteger16 getRand(UInteger32 *seed) - return rand_r((unsigned int*)seed); - } - --Boolean adjFreq(Integer32 adj) --{ -- struct timex 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); -- -- return !adjtimex(&t); --} -- -diff --git a/src/dep/time.c b/src/dep/time.c -new file mode 100644 -index 0000000..2a828b4 ---- /dev/null -+++ b/src/dep/time.c -@@ -0,0 +1,771 @@ -+#include "../ptpd.h" -+#include -+ -+#include "e1000_ioctl.h" -+ -+/** global state for controlling system time when TIME_BOTH is selected */ -+static PtpClock timeBothClock; -+ -+/** -+ * Most recent send time stamp from NIC, 0/0 if none available right now. -+ * Reset by getSendTime(). -+ */ -+static TimeInternal lastSendTime; -+ -+#ifndef RECV_ARRAY_SIZE -+/** -+ * Must be large enough to buffer all time stamps received from the NIC -+ * but not yet requested by the protocol processor. Because new information -+ * can only be added when the protocol asks for old one, this should not -+ * get very full. -+ */ -+# define RECV_ARRAY_SIZE 10 -+#endif -+ -+/** -+ * An array of the latest RECV_ARRAY_SIZE packet receive information. -+ * Once it overflows the oldest ones are overwritten in a round-robin -+ * fashion. -+ */ -+static struct { -+ TimeInternal recvTimeStamp; -+ UInteger16 sequenceId; -+ Octet sourceUuid[PTP_UUID_LENGTH]; -+} lastRecvTimes[RECV_ARRAY_SIZE]; -+ -+/** -+ * Oldest valid and next free entry in lastRecvTimes. -+ * Valid ones are [oldest, free[ if oldest <= free, -+ * otherwise [oldest, RECV_ARRAY_SIZE[ and [0, free[. -+ */ -+static int oldestRecv, nextFreeRecv; -+ -+/** -+ * if TIME_BOTH: measure NIC<->system time offsets and adapt system time -+ * -+ * This function is called whenever init.c gets control; to prevent to -+ * frequent changes it ignores invocations less than one second away from -+ * the previous one. -+ */ -+static void syncSystemWithNIC(PtpClock *ptpClock) -+{ -+ struct E1000_TSYNC_COMPARETS_ARGU ts; -+ TimeInternal delay; -+ static TimeInternal zero; -+ -+ if(ptpClock->runTimeOpts.time != TIME_BOTH) -+ return; -+ else -+ { -+#if 1 -+ static TimeInternal lastsync; -+ TimeInternal now, offset; -+ timerNow(&now); -+ subTime(&offset, &now, &lastsync); -+ if(offset.seconds <= 0) -+ return; -+ lastsync = now; -+#endif -+ } -+ -+ ptpClock->netPath.eventSockIFR.ifr_data = (void *)&ts; -+ memset(&ts, 0, sizeof(ts)); -+ if (ioctl(ptpClock->netPath.eventSock, E1000_TSYNC_COMPARETS_IOCTL, &ptpClock->netPath.eventSockIFR) < 0) { -+ ERROR("could not correlate E1000 hardware and system time on %s: %s\n", -+ ptpClock->netPath.eventSockIFR.ifr_name, -+ strerror(errno)); -+ return; -+ } -+ delay.seconds = ts.systemToNIC.seconds * ts.systemToNICSign; -+ delay.nanoseconds = ts.systemToNIC.nanoseconds * ts.systemToNICSign; -+ DBGV("system to NIC delay %ld.%09d\n", -+ delay.seconds, delay.nanoseconds); -+ updateDelay(&delay, &zero, &timeBothClock.owd_filt, &timeBothClock); -+ -+ delay.seconds = ts.NICToSystem.seconds * ts.NICToSystemSign; -+ delay.nanoseconds = ts.NICToSystem.nanoseconds * ts.NICToSystemSign; -+ DBGV("NIC to system delay %ld.%09d\n", -+ delay.seconds, delay.nanoseconds); -+ updateOffset(&delay, &zero, &timeBothClock.ofm_filt, &timeBothClock); -+ -+ if(ptpClock->port_state == PTP_MASTER) -+ { -+ timeBothClock.nic_instead_of_system = TRUE; -+ timeBothClock.runTimeOpts.time = TIME_NIC; -+ } -+ else -+ { -+ timeBothClock.nic_instead_of_system = FALSE; -+ timeBothClock.runTimeOpts.time = TIME_SYSTEM; -+ } -+ updateClock(&timeBothClock); -+ DBGV("system time updated\n"); -+} -+ -+static Boolean selectNICTimeMode(Boolean sync, PtpClock *ptpClock) -+{ -+ DBGV("time stamp incoming %s packets\n", sync ? "Sync" : "Delay_Req"); -+ -+ switch (ptpClock->runTimeOpts.time) { -+#ifdef HAVE_LINUX_NET_TSTAMP_H -+ case TIME_SYSTEM_LINUX_HW: { -+ struct hwtstamp_config hwconfig; -+ int so_timestamping_flags; -+ -+ ptpClock->netPath.eventSockIFR.ifr_data = (void *)&hwconfig; -+ memset(&hwconfig, 0, sizeof(&hwconfig)); -+ -+ /* -+ * Configure for time stamping of incoming Sync or Delay_Req -+ * messages and for time stamping of all out-going event -+ * messages. Out-going messages will be bounced via the error -+ * queue of the event socket. -+ */ -+ hwconfig.tx_type = HWTSTAMP_TX_ON; -+ hwconfig.rx_filter = sync ? -+ HWTSTAMP_FILTER_PTP_V1_L4_SYNC : -+ HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ; -+ so_timestamping_flags = SOF_TIMESTAMPING_TX_HARDWARE|SOF_TIMESTAMPING_RX_HARDWARE|SOF_TIMESTAMPING_SYS_HARDWARE; -+ -+ if (ioctl(ptpClock->netPath.eventSock, SIOCSHWTSTAMP, &ptpClock->netPath.eventSockIFR) < 0) { -+ if (errno == ERANGE) { -+ /* hardware time stamping not supported */ -+ PERROR("net_tstamp SIOCSHWTSTAMP: mode of operation not supported"); -+ return FALSE; -+ } else { -+ PERROR("net_tstamp SIOCSHWTSTAMP: %s", strerror(errno)); -+ return FALSE; -+ } -+ } -+ -+ if (setsockopt(ptpClock->netPath.eventSock, SOL_SOCKET, SO_TIMESTAMPING, &so_timestamping_flags, sizeof(so_timestamping_flags)) < 0) { -+ PERROR("net_tstamp SO_TIMESTAMPING: %s", strerror(errno)); -+ return FALSE; -+ } -+ break; -+ } -+ case TIME_SYSTEM_LINUX_SW: { -+ /* same as before, but without requiring support by the NIC */ -+ int so_timestamping_flags = -+ SOF_TIMESTAMPING_TX_SOFTWARE|SOF_TIMESTAMPING_RX_SOFTWARE|SOF_TIMESTAMPING_SOFTWARE; -+ if (setsockopt(ptpClock->netPath.eventSock, SOL_SOCKET, SO_TIMESTAMPING, &so_timestamping_flags, sizeof(so_timestamping_flags)) < 0) { -+ PERROR("net_tstamp SO_TIMESTAMPING: %s", strerror(errno)); -+ return FALSE; -+ } -+ break; -+ } -+#else -+ case TIME_SYSTEM_LINUX_SW: -+ case TIME_SYSTEM_LINUX_HW: -+ PERROR("net_tstamp interface not supported"); -+ return FALSE; -+#endif /* HAVE_LINUX_NET_TSTAMP_H */ -+ default: -+ *(int *)&ptpClock->netPath.eventSockIFR.ifr_data = sync ? E1000_UDP_V1_SYNC : E1000_UDP_V1_DELAY; -+ if(ioctl(ptpClock->netPath.eventSock, E1000_TSYNC_ENABLERX_IOCTL, &ptpClock->netPath.eventSockIFR) < 0) { -+ ERROR("could not activate E1000 hardware receive time stamping on %s: %s\n", -+ ptpClock->netPath.eventSockIFR.ifr_name, -+ strerror(errno)); -+ return FALSE; -+ } -+ break; -+ } -+ -+ return TRUE; -+} -+ -+static Boolean initNICTime(Boolean sync, PtpClock *ptpClock) -+{ -+ /** @todo also check success indicator in ifr_data */ -+ if (ioctl(ptpClock->netPath.eventSock, E1000_TSYNC_INIT_IOCTL, &ptpClock->netPath.eventSockIFR) < 0) { -+ ERROR("could not activate E1000 hardware time stamping on %s: %s\n", -+ ptpClock->netPath.eventSockIFR.ifr_name, -+ strerror(errno)); -+ } -+ else if(ioctl(ptpClock->netPath.eventSock, E1000_TSYNC_ENABLETX_IOCTL, &ptpClock->netPath.eventSockIFR) < 0) { -+ ERROR("could not activate E1000 hardware send time stamping on %s: %s\n", -+ ptpClock->netPath.eventSockIFR.ifr_name, -+ strerror(errno)); -+ } -+ else if(!selectNICTimeMode(sync, ptpClock)) { -+ // error already printed -+ } -+ else { -+#if 0 -+ // move the NIC time for debugging purposes -+ TimeInternal timeTmp; -+ -+ DBGV("shift NIC time\n"); -+ getTime(&timeTmp, ptpClock); -+ timeTmp.seconds -= 2; -+ setTime(&timeTmp, ptpClock); -+ DBGV("shift NIC time done\n"); -+#endif -+ return TRUE; -+ } -+ -+ return FALSE; -+} -+ -+Boolean initTime(PtpClock *ptpClock) -+{ -+ switch(ptpClock->runTimeOpts.time) { -+ case TIME_SYSTEM: -+ return TRUE; -+ break; -+ case TIME_BOTH: -+ /* prepare clock servo for controlling system time */ -+ timeBothClock = *ptpClock; -+ timeBothClock.runTimeOpts.time = TIME_SYSTEM; -+ timeBothClock.name = "sys "; -+ initClock(&timeBothClock); -+ -+ /* default options for NIC synchronization */ -+ ptpClock->runTimeOpts.noResetClock = DEFAULT_NO_RESET_CLOCK; -+ ptpClock->runTimeOpts.noAdjust = DEFAULT_NO_ADJUST_CLOCK; -+ ptpClock->runTimeOpts.s = DEFAULT_DELAY_S; -+ ptpClock->runTimeOpts.ap = DEFAULT_AP; -+ ptpClock->runTimeOpts.ai = DEFAULT_AI; -+ -+ return initNICTime(TRUE, ptpClock); -+ break; -+ case TIME_SYSTEM_LINUX_HW: -+ case TIME_SYSTEM_LINUX_SW: -+ return selectNICTimeMode(TRUE, ptpClock); -+ break; -+ case TIME_NIC: -+ case TIME_SYSTEM_ASSISTED: -+ return initNICTime(TRUE, ptpClock); -+ break; -+ default: -+ ERROR("unsupported selection of time source\n"); -+ return FALSE; -+ break; -+ } -+} -+ -+void getTime(TimeInternal *time, PtpClock *ptpClock) -+{ -+ switch(ptpClock->runTimeOpts.time) -+ { -+ case TIME_SYSTEM_LINUX_HW: -+ case TIME_SYSTEM_LINUX_SW: -+ case TIME_SYSTEM_ASSISTED: -+ case TIME_SYSTEM: { -+ struct timeval tv; -+ -+ gettimeofday(&tv, 0); -+ time->seconds = tv.tv_sec; -+ time->nanoseconds = tv.tv_usec*1000; -+ break; -+ } -+ case TIME_BOTH: -+ case TIME_NIC: { -+ struct E1000_TSYNC_SYSTIME_ARGU ts; -+ -+ ptpClock->netPath.eventSockIFR.ifr_data = (void *)&ts; -+ memset(&ts, 0, sizeof(ts)); -+ if (ioctl(ptpClock->netPath.eventSock, E1000_TSYNC_SYSTIME_IOCTL, &ptpClock->netPath.eventSockIFR) < 0) { -+ ERROR("could not read E1000 hardware time on %s: %s\n", -+ ptpClock->netPath.eventSockIFR.ifr_name, -+ strerror(errno)); -+ return; -+ } -+ time->seconds = ts.time.seconds; -+ time->nanoseconds = ts.time.nanoseconds; -+ -+ syncSystemWithNIC(ptpClock); -+ break; -+ } -+ default: -+ ERROR("unsupported selection of time source\n"); -+ break; -+ } -+} -+ -+void setTime(TimeInternal *time, PtpClock *ptpClock) -+{ -+ switch(ptpClock->runTimeOpts.time) -+ { -+ case TIME_SYSTEM_LINUX_HW: -+ case TIME_SYSTEM_LINUX_SW: -+ case TIME_SYSTEM_ASSISTED: -+ case TIME_SYSTEM: { -+ NOTIFY("resetting system clock to %ds %dns\n", time->seconds, time->nanoseconds); -+ struct timeval tv; -+ tv.tv_sec = time->seconds; -+ tv.tv_usec = time->nanoseconds/1000; -+ settimeofday(&tv, 0); -+ break; -+ } -+ case TIME_BOTH: -+ case TIME_NIC: { -+ struct E1000_TSYNC_SYSTIME_ARGU ts; -+ TimeInternal currentTime, offset; -+ -+ NOTIFY("resetting NIC clock to %ds %dns\n", time->seconds, time->nanoseconds); -+ memset(&ts, 0, sizeof(ts)); -+ getTime(¤tTime, ptpClock); -+ subTime(&offset, time, ¤tTime); -+ ts.negative_offset = (offset.seconds < 0 || offset.nanoseconds < 0) ? -1 : 1; -+ ts.time.seconds = ts.negative_offset * offset.seconds; -+ ts.time.nanoseconds = ts.negative_offset * offset.nanoseconds; -+ ptpClock->netPath.eventSockIFR.ifr_data = (void *)&ts; -+ NOTIFY("adding NIC offset %s%ld.%09d (%ld/%p)\n", -+ ts.negative_offset < 0 ? "-" : "", -+ ts.time.seconds, -+ ts.time.nanoseconds, -+ sizeof(ts), &ts); -+ if (ioctl(ptpClock->netPath.eventSock, E1000_TSYNC_SYSTIME_IOCTL, &ptpClock->netPath.eventSockIFR) < 0) { -+ ERROR("could not set E1000 hardware time on %s: %s\n", -+ ptpClock->netPath.eventSockIFR.ifr_name, -+ strerror(errno)); -+ } -+ else -+ { -+ DBGV("new NIC time %ld.%09d\n", -+ ts.time.seconds, -+ ts.time.nanoseconds); -+ syncSystemWithNIC(ptpClock); -+ } -+ break; -+ } -+ default: -+ ERROR("unsupported selection of time source\n"); -+ break; -+ } -+} -+ -+void adjTime(Integer32 adj, TimeInternal *offset, PtpClock *ptpClock) -+{ -+ switch(ptpClock->runTimeOpts.time) -+ { -+ case TIME_SYSTEM_LINUX_HW: -+ case TIME_SYSTEM_LINUX_SW: -+ case TIME_SYSTEM_ASSISTED: -+ case TIME_SYSTEM: { -+ struct timex t; -+ static Boolean maxAdjValid; -+ static long maxAdj; -+ static long minTick, maxTick; -+ static long userHZ; -+ static long tickRes; /* USER_HZ * 1000 [ppb] */ -+ long tickAdj; -+ long freqAdj; -+ int res; -+ -+ if (!maxAdjValid) { -+ userHZ = sysconf(_SC_CLK_TCK); -+ t.modes = 0; -+ adjtimex(&t); -+ maxAdj = t.tolerance / ((1<<16)/1000); -+ tickRes = userHZ * 1000; -+ /* limits from the adjtimex command man page; could be determined via binary search */ -+ minTick = (900000 - 1000000) / userHZ; -+ maxTick = (1100000 - 1000000) / userHZ; -+ maxAdjValid = TRUE; -+ } -+ -+ /* -+ * The Linux man page for the adjtimex() system call does not -+ * describe limits for frequency. The more recent man page for -+ * the adjtimex command on RH5 does and says that -+ * -tolerance <= frequency <= tolerance -+ * which was confirmed by trying out values just outside that interval. -+ * -+ * Note that this contradicts the comments for struct timex which say -+ * that freq and tolerance have different units (scaled ppm vs ppm). -+ * -+ * We follow the actual implementation on Linux 2.6.22 and do the -+ * range check after scaling. -+ */ -+ -+ t.modes = MOD_FREQUENCY|MOD_CLKB; -+ /* -+ * @todo -+ * Where is the official documentation for "scaled ppm"? -+ * Should this perhaps be adj * (1<<16) / 1000 (more accurate -+ * than multiplying by ((1<<16)/1000) == 65)? -+ */ -+ -+ /* -+ * 1 t.tick = 1 e-6 s * USER_HZ 1/s = 1 USER_HZ * 1000 ppb -+ * -+ * Large values of adj can be turned into t.tick adjustments: -+ * tickAdj t.tick = adj ppb / ( USER_HZ * 1000 ppb ) -+ * -+ * Round this so that the error is as small is possible, -+ * because we need to fit that into t.freq. -+ */ -+ freqAdj = adj; -+ tickAdj = 0; -+ if(freqAdj > maxAdj) -+ { -+ tickAdj = (adj - maxAdj + tickRes - 1) / tickRes; -+ if(tickAdj > maxTick) -+ tickAdj = maxTick; -+ freqAdj = adj - tickAdj * tickRes; -+ } -+ else if(freqAdj < -maxAdj) -+ { -+ tickAdj = -((-adj - maxAdj + tickRes - 1) / tickRes); -+ if(tickAdj < minTick) -+ tickAdj = minTick; -+ freqAdj = adj - tickAdj * tickRes; -+ } -+ if(freqAdj > maxAdj) -+ freqAdj = maxAdj; -+ else if(freqAdj < -maxAdj) -+ freqAdj = -maxAdj; -+ -+ t.freq = freqAdj * ((1<<16)/1000); -+ t.tick = tickAdj + 1000000 / userHZ; -+ ptpClock->adj = tickAdj * tickRes + freqAdj; -+ -+ INFO("requested adj %d ppb => adjust system frequency by %d scaled ppm (%d ppb) + %ld us/tick (%d ppb) = adj %d ppb (freq limit %ld/%ld ppm, tick limit %ld/%ld us*USER_HZ)\n", -+ adj, -+ t.freq, freqAdj, -+ t.tick - 1000000 / userHZ, tickAdj * tickRes, -+ ptpClock->adj, -+ -maxAdj, maxAdj, -+ minTick, maxTick); -+ -+ res = adjtimex(&t); -+ switch (res) { -+ case -1: -+ ERROR("adjtimex(freq = %d) failed: %s\n", -+ t.freq, strerror(errno)); -+ break; -+ case TIME_OK: -+ INFO(" -> TIME_OK\n"); -+ break; -+ case TIME_INS: -+ ERROR("adjtimex -> insert leap second?!\n"); -+ break; -+ case TIME_DEL: -+ ERROR("adjtimex -> delete leap second?!\n"); -+ break; -+ case TIME_OOP: -+ ERROR("adjtimex -> leap second in progress?!\n"); -+ break; -+ case TIME_WAIT: -+ ERROR("adjtimex -> leap second has occurred?!\n"); -+ break; -+ case TIME_BAD: -+ ERROR("adjtimex -> time bad\n"); -+ break; -+ default: -+ ERROR("adjtimex -> unknown result %d\n", res); -+ break; -+ } -+ break; -+ } -+ case TIME_BOTH: -+ case TIME_NIC: { -+ if(offset) -+ { -+#if 0 -+ struct E1000_TSYNC_SYSTIME_ARGU ts; -+ memset(&ts, 0, sizeof(ts)); -+ // always store positive seconds/nanoseconds -+ ts.negative_offset = (offset->seconds < 0 || offset->nanoseconds < 0) ? -1 : 1; -+ ts.time.seconds = ts.negative_offset * offset->seconds; -+ ts.time.nanoseconds = ts.negative_offset * offset->nanoseconds; -+ // invert the sign: if offset is positive, we need to substract it and vice versa -+ ts.negative_offset *= -1; -+ DBGV("adjust NIC time by offset %s%lu.%09d (adj %d)\n", -+ ts.negative_offset < 0 ? "-" : "", -+ ts.time.seconds, ts.time.nanoseconds, -+ adj); -+ ptpClock->netPath.eventSockIFR.ifr_data = (void *)&ts; -+ if (ioctl(ptpClock->netPath.eventSock, E1000_TSYNC_SYSTIME_IOCTL, &ptpClock->netPath.eventSockIFR) < 0) { -+ ERROR("could not modify E1000 hardware time on %s: %s\n", -+ ptpClock->netPath.eventSockIFR.ifr_name, -+ strerror(errno)); -+ } -+ else -+ syncSystemWithNIC(ptpClock); -+#else -+ // adjust NIC frequency -+ struct E1000_TSYNC_ADJTIME_ARGU ts; -+ memset(&ts, 0, sizeof(ts)); -+ ts.adj = (long long)adj; -+ if(ptpClock->nic_instead_of_system) -+ ts.adj = -ts.adj; -+ ts.set_adj = TRUE; -+ ptpClock->netPath.eventSockIFR.ifr_data = (void *)&ts; -+ DBGV("adjust NIC frequency by %d ppb\n", ts.adj); -+ ptpClock->adj = ts.adj; -+ if (ioctl(ptpClock->netPath.eventSock, E1000_TSYNC_ADJTIME_IOCTL, &ptpClock->netPath.eventSockIFR) < 0) { -+ ERROR("could not modify E1000 hardware frequency on %s: %s\n", -+ ptpClock->netPath.eventSockIFR.ifr_name, -+ strerror(errno)); -+ } -+ else -+ syncSystemWithNIC(ptpClock); -+#endif -+ } -+ else -+ syncSystemWithNIC(ptpClock); -+ break; -+ } -+ default: -+ ERROR("unsupported selection of time source\n"); -+ break; -+ } -+} -+ -+void adjTimeOffset(TimeInternal *offset, PtpClock *ptpClock) -+{ -+ switch(ptpClock->runTimeOpts.time) -+ { -+ case TIME_BOTH: -+ case TIME_NIC: { -+ struct E1000_TSYNC_SYSTIME_ARGU ts; -+ memset(&ts, 0, sizeof(ts)); -+ // always store positive seconds/nanoseconds -+ ts.negative_offset = (offset->seconds < 0 || offset->nanoseconds < 0) ? -1 : 1; -+ ts.time.seconds = ts.negative_offset * offset->seconds; -+ ts.time.nanoseconds = ts.negative_offset * offset->nanoseconds; -+ -+ // invert the sign: if offset is positive, we need to substract it and vice versa; -+ // when in nic_instead_of_system the logic is already inverted -+ if (!ptpClock->nic_instead_of_system) -+ ts.negative_offset *= -1; -+ -+ DBGV("adjust NIC time by offset %s%lu.%09d\n", -+ ts.negative_offset < 0 ? "-" : "", -+ ts.time.seconds, ts.time.nanoseconds); -+ ptpClock->netPath.eventSockIFR.ifr_data = (void *)&ts; -+ if (ioctl(ptpClock->netPath.eventSock, E1000_TSYNC_SYSTIME_IOCTL, &ptpClock->netPath.eventSockIFR) < 0) { -+ ERROR("could not modify E1000 hardware time on %s: %s\n", -+ ptpClock->netPath.eventSockIFR.ifr_name, -+ strerror(errno)); -+ } -+ else -+ syncSystemWithNIC(ptpClock); -+ break; -+ } -+ default: { -+ TimeInternal timeTmp; -+ -+ getTime(&timeTmp, ptpClock); -+ subTime(&timeTmp, &timeTmp, offset); -+ setTime(&timeTmp, ptpClock); -+ } -+ } -+} -+ -+static void getTimeStamps(PtpClock *ptpClock) -+{ -+ struct E1000_TSYNC_READTS_ARGU ts; -+ -+ ptpClock->netPath.eventSockIFR.ifr_data = (void *)&ts; -+ memset(&ts, 0, sizeof(ts)); -+ ts.withSystemTime = (ptpClock->runTimeOpts.time == TIME_SYSTEM_ASSISTED); -+ if (ioctl(ptpClock->netPath.eventSock, E1000_TSYNC_READTS_IOCTL, &ptpClock->netPath.eventSockIFR) < 0) { -+ ERROR("could not read E1000 hardware time stamps on %s: %s\n", -+ ptpClock->netPath.eventSockIFR.ifr_name, -+ strerror(errno)); -+ return; -+ } -+ -+ DBGV("rx %s, tx %s\n", -+ ts.rx_valid ? "valid" : "invalid", -+ ts.tx_valid ? "valid" : "invalid"); -+ -+ if(ts.rx_valid) -+ { -+ int newIndex; -+ -+ if(nextFreeRecv == RECV_ARRAY_SIZE) -+ { -+ newIndex = 0; -+ nextFreeRecv = 1; -+ oldestRecv = 2; -+ } -+ else -+ { -+ newIndex = nextFreeRecv; -+ nextFreeRecv++; -+ if(oldestRecv && nextFreeRecv == oldestRecv) -+ ++oldestRecv; -+ } -+ -+ if(oldestRecv >= RECV_ARRAY_SIZE) -+ oldestRecv = 0; -+ -+ DBGV("new entry %d, oldest %d, next free %d\n", newIndex, oldestRecv, nextFreeRecv); -+ -+ lastRecvTimes[newIndex].recvTimeStamp.seconds = ts.withSystemTime ? ts.rx_sys.seconds : ts.rx.seconds; -+ lastRecvTimes[newIndex].recvTimeStamp.nanoseconds = ts.withSystemTime ? ts.rx_sys.nanoseconds : ts.rx.nanoseconds; -+ lastRecvTimes[newIndex].sequenceId = ts.sourceSequenceId; -+ memcpy(lastRecvTimes[newIndex].sourceUuid, ts.sourceIdentity, sizeof(ts.sourceIdentity)); -+ -+ DBGV("rx %d: time %lu.%09u (%lu.%09u), sequence %u, uuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", -+ newIndex, -+ lastRecvTimes[newIndex].recvTimeStamp.seconds, -+ lastRecvTimes[newIndex].recvTimeStamp.nanoseconds, -+ ts.withSystemTime ? ts.rx.seconds : 0, -+ ts.withSystemTime ? ts.rx.nanoseconds : 0, -+ lastRecvTimes[newIndex].sequenceId, -+ ts.sourceIdentity[0], -+ ts.sourceIdentity[1], -+ ts.sourceIdentity[2], -+ ts.sourceIdentity[3], -+ ts.sourceIdentity[4], -+ ts.sourceIdentity[5]); -+ } -+ -+ if(ts.tx_valid) -+ { -+ lastSendTime.seconds = ts.withSystemTime ? ts.tx_sys.seconds : ts.tx.seconds; -+ lastSendTime.nanoseconds = ts.withSystemTime ? ts.tx_sys.nanoseconds : ts.tx.nanoseconds; -+ -+ DBGV("tx time %lu.%09u (%lu.%09u)\n", -+ lastSendTime.seconds, -+ lastSendTime.nanoseconds, -+ ts.withSystemTime ? ts.tx.seconds : 0, -+ ts.withSystemTime ? ts.tx.nanoseconds : 0); -+ } -+} -+ -+Boolean getSendTime(TimeInternal *sendTimeStamp, -+ PtpClock *ptpClock) -+{ -+ /* check for new time stamps */ -+ getTimeStamps(ptpClock); -+ -+ if(lastSendTime.seconds || lastSendTime.nanoseconds) -+ { -+ *sendTimeStamp = lastSendTime; -+ lastSendTime.seconds = 0; -+ lastSendTime.nanoseconds = 0; -+ return TRUE; -+ } -+ else -+ return FALSE; -+} -+ -+/** -+ * helper function for getReceiveTime() which searches for time stamp -+ * in lastRecvTimes[leftIndex, rightIndex[ -+ */ -+static Boolean getReceiveTimeFromArray(TimeInternal *recvTimeStamp, -+ Octet sourceUuid[PTP_UUID_LENGTH], -+ UInteger16 sequenceId, -+ int leftIndex, int rightIndex) -+{ -+ int i; -+ -+ for(i = leftIndex; i < rightIndex; i++) -+ { -+ if(!memcmp(lastRecvTimes[i].sourceUuid, sourceUuid, sizeof(lastRecvTimes[i].sourceUuid)) && -+ lastRecvTimes[i].sequenceId == sequenceId) -+ { -+ DBGV("found rx index %d: time %lu.%09u, sequence %u, uuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", -+ i, -+ lastRecvTimes[i].recvTimeStamp.seconds, -+ lastRecvTimes[i].recvTimeStamp.nanoseconds, -+ lastRecvTimes[i].sequenceId, -+ lastRecvTimes[i].sourceUuid[0], -+ lastRecvTimes[i].sourceUuid[1], -+ lastRecvTimes[i].sourceUuid[2], -+ lastRecvTimes[i].sourceUuid[3], -+ lastRecvTimes[i].sourceUuid[4], -+ lastRecvTimes[i].sourceUuid[5]); -+ *recvTimeStamp = lastRecvTimes[i].recvTimeStamp; -+ // invalidate entry to prevent accidental reuse (happened when slaves were -+ // restarted quickly while the master still had their old sequence IDs in the array) -+ memset(&lastRecvTimes[i], 0, sizeof(lastRecvTimes[i])); -+ return TRUE; -+ } -+ } -+ return FALSE; -+} -+ -+Boolean getReceiveTime(TimeInternal *recvTimeStamp, -+ Octet sourceUuid[PTP_UUID_LENGTH], -+ UInteger16 sequenceId, -+ PtpClock *ptpClock) -+{ -+ /* check for new time stamps */ -+ getTimeStamps(ptpClock); -+ -+ if(oldestRecv <= nextFreeRecv) -+ return getReceiveTimeFromArray(recvTimeStamp, sourceUuid, sequenceId, oldestRecv, nextFreeRecv); -+ else -+ { -+ if(getReceiveTimeFromArray(recvTimeStamp, sourceUuid, sequenceId, oldestRecv, RECV_ARRAY_SIZE)) -+ return TRUE; -+ else -+ return getReceiveTimeFromArray(recvTimeStamp, sourceUuid, sequenceId, 0, nextFreeRecv); -+ } -+} -+ -+void timeNoActivity(PtpClock *ptpClock) -+{ -+#ifdef PTPD_DBGV -+ switch(ptpClock->runTimeOpts.time) { -+ case TIME_NIC: -+ case TIME_BOTH: -+ case TIME_SYSTEM_ASSISTED: -+ { -+ TimeInternal now, ts, offset; -+ struct E1000_TSYNC_COMPARETS_ARGU argu; -+ int sign; -+ -+ getTime(&ts, ptpClock); -+ timerNow(&now); -+ subTime(&offset, &now, &ts); -+ sign = (offset.seconds < 0 || offset.nanoseconds < 0) ? -1 : 1; -+ DBGV("system time %d.%09d, NIC time %d.%09d => system time - NIC time = %s%d.%09d\n", -+ now.seconds, now.nanoseconds, -+ ts.seconds, ts.nanoseconds, -+ sign < 0 ? "-" : "", -+ sign * offset.seconds, sign * offset.nanoseconds); -+ -+ ptpClock->netPath.eventSockIFR.ifr_data = (void *)&argu; -+ memset(&ts, 0, sizeof(ts)); -+ if (ioctl(ptpClock->netPath.eventSock, E1000_TSYNC_COMPARETS_IOCTL, &ptpClock->netPath.eventSockIFR) < 0) { -+ ERROR("could not correlate E1000 hardware and system time on %s: %s\n", -+ ptpClock->netPath.eventSockIFR.ifr_name, -+ strerror(errno)); -+ return; -+ } -+ -+ now.seconds = argu.systemToNICSign * argu.systemToNIC.seconds; -+ now.nanoseconds = argu.systemToNICSign * argu.systemToNIC.nanoseconds; -+ ts.seconds = argu.NICToSystemSign * argu.NICToSystem.seconds; -+ ts.nanoseconds = argu.NICToSystemSign * argu.NICToSystem.nanoseconds; -+ subTime(&offset, &now, &ts); -+ offset.seconds /= 2; -+ offset.nanoseconds /= 2; -+ DBGV("delay system to NIC %s%ld.%09d/NIC to system %s%ld.%09d => system - NIC time = %d.%09d\n", -+ argu.systemToNICSign > 0 ? "" : argu.systemToNICSign < 0 ? "-" : "?", -+ argu.systemToNIC.seconds, argu.systemToNIC.nanoseconds, -+ argu.NICToSystemSign > 0 ? "" : argu.NICToSystemSign < 0 ? "-" : "?", -+ argu.NICToSystem.seconds, argu.NICToSystem.nanoseconds, -+ offset.seconds, offset.nanoseconds); -+ break; -+ } -+ } -+#endif -+ syncSystemWithNIC(ptpClock); -+} -+ -+void timeToState(UInteger8 state, PtpClock *ptpClock) -+{ -+ if(ptpClock->runTimeOpts.time > TIME_SYSTEM && -+ state != ptpClock->port_state) -+ { -+ if(state == PTP_MASTER) -+ /* only master listens for Delay_Req... */ -+ selectNICTimeMode(FALSE, ptpClock); -+ else if(ptpClock->port_state == PTP_MASTER) -+ /** ... and only while he still is master */ -+ selectNICTimeMode(TRUE, ptpClock); -+ -+ timeBothClock.port_state = state; -+ } -+} -diff --git a/src/dep/timer.c b/src/dep/timer.c -index f9f4110..b40eb40 100644 ---- a/src/dep/timer.c -+++ b/src/dep/timer.c -@@ -9,7 +9,13 @@ void catch_alarm(int sig) - { - elapsed += TIMER_INTERVAL; - -- DBGV("catch_alarm: elapsed %d\n", elapsed); -+ /* -+ * DBGV() calls vsyslog() which doesn't seem to be reentrant: -+ * with Linux 2.6.23 and libc 2.5 (RH5) this even locked up the -+ * system. -+ * -+ * DBGV("catch_alarm: elapsed %d\n", elapsed); -+ */ - } - - void initTimer(void) -@@ -84,3 +90,28 @@ Boolean timerExpired(UInteger16 index, IntervalTimer *itimer) - return TRUE; - } - -+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 timerNow(TimeInternal *time) -+{ -+ struct timeval tv; -+ -+ gettimeofday(&tv, 0); -+ time->seconds = tv.tv_sec; -+ time->nanoseconds = tv.tv_usec*1000; -+} -diff --git a/src/probe.c b/src/probe.c -index 870a25e..215019f 100644 ---- a/src/probe.c -+++ b/src/probe.c -@@ -8,29 +8,29 @@ UInteger8 management_key_array[KEY_ARRAY_LEN] = - void displayHeader(MsgHeader*); - void displayManagement(MsgHeader*,MsgManagement*); - --void probe(RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void probe(PtpClock *ptpClock) - { - UInteger16 i; - UInteger16 length; -- TimeInternal interval, now, finish, timestamp; -+ TimeInternal interval, now, finish; - - /* check */ -- if(rtOpts->probe_management_key == PTP_MM_UPDATE_DEFAULT_DATA_SET -- || rtOpts->probe_management_key == PTP_MM_UPDATE_GLOBAL_TIME_PROPERTIES -- || rtOpts->probe_management_key == PTP_MM_SET_SYNC_INTERVAL) -+ if(ptpClock->runTimeOpts.probe_management_key == PTP_MM_UPDATE_DEFAULT_DATA_SET -+ || ptpClock->runTimeOpts.probe_management_key == PTP_MM_UPDATE_GLOBAL_TIME_PROPERTIES -+ || ptpClock->runTimeOpts.probe_management_key == PTP_MM_SET_SYNC_INTERVAL) - { - ERROR("send not supported for that management message\n"); - return; - } - - /* init */ -- if(!netInit(&ptpClock->netPath, rtOpts, ptpClock)) -+ if(!netInit(ptpClock)) - { - ERROR("failed to initialize network\n"); - return; - } - -- initData(rtOpts, ptpClock); -+ initData(ptpClock); - msgPackHeader(ptpClock->msgObuf, ptpClock); - - memset(&ptpClock->msgTmp.manage, 0, sizeof(MsgManagement)); -@@ -39,10 +39,10 @@ void probe(RunTimeOpts *rtOpts, PtpClock *ptpClock) - /* send */ - for(i = 0; i < KEY_ARRAY_LEN; ++i) - { -- if(rtOpts->probe_management_key > 0) -+ if(ptpClock->runTimeOpts.probe_management_key > 0) - { -- ptpClock->msgTmp.manage.managementMessageKey = rtOpts->probe_management_key; -- ptpClock->msgTmp.manage.recordKey = rtOpts->probe_record_key; -+ ptpClock->msgTmp.manage.managementMessageKey = ptpClock->runTimeOpts.probe_management_key; -+ ptpClock->msgTmp.manage.recordKey = ptpClock->runTimeOpts.probe_record_key; - } - else - ptpClock->msgTmp.manage.managementMessageKey = management_key_array[i]; -@@ -55,27 +55,27 @@ void probe(RunTimeOpts *rtOpts, PtpClock *ptpClock) - - printf("\n(sending managementMessageKey %hhu)\n", ptpClock->msgTmp.manage.managementMessageKey); - -- if(!netSendGeneral(ptpClock->msgObuf, length, &ptpClock->netPath)) -+ if(!netSendGeneral(ptpClock->msgObuf, length, ptpClock)) - { - ERROR("failed to send message\n"); - return; - } - -- if(rtOpts->probe_management_key > 0) -+ if(ptpClock->runTimeOpts.probe_management_key > 0) - break; - } - -- getTime(&finish); -+ timerNow(&finish); - finish.seconds += PTP_SYNC_INTERVAL_TIMEOUT(ptpClock->sync_interval); - for(;;) - { - interval.seconds = PTP_SYNC_INTERVAL_TIMEOUT(ptpClock->sync_interval); - interval.nanoseconds = 0; -- netSelect(&interval, &ptpClock->netPath); -+ netSelect(&interval, ptpClock); - -- netRecvEvent(ptpClock->msgIbuf, ×tamp, &ptpClock->netPath); -+ netRecvEvent(ptpClock->msgIbuf, NULL, ptpClock); - -- if(netRecvGeneral(ptpClock->msgIbuf, &ptpClock->netPath)) -+ if(netRecvGeneral(ptpClock->msgIbuf, ptpClock)) - { - msgUnpackHeader(ptpClock->msgIbuf, &ptpClock->msgTmpHeader); - -@@ -90,7 +90,7 @@ void probe(RunTimeOpts *rtOpts, PtpClock *ptpClock) - fflush(stdout); - } - -- getTime(&now); -+ timerNow(&now); - if( now.seconds > finish.seconds || (now.seconds == finish.seconds - && now.nanoseconds > finish.nanoseconds) ) - break; -diff --git a/src/protocol.c b/src/protocol.c -index 8bde2ce..0a53b82 100644 ---- a/src/protocol.c -+++ b/src/protocol.c -@@ -2,22 +2,22 @@ - - #include "ptpd.h" - --Boolean doInit(RunTimeOpts*,PtpClock*); --void doState(RunTimeOpts*,PtpClock*); --void toState(UInteger8,RunTimeOpts*,PtpClock*); -- --void handle(RunTimeOpts*,PtpClock*); --void handleSync(MsgHeader*,Octet*,ssize_t,TimeInternal*,Boolean,RunTimeOpts*,PtpClock*); --void handleFollowUp(MsgHeader*,Octet*,ssize_t,Boolean,RunTimeOpts*,PtpClock*); --void handleDelayReq(MsgHeader*,Octet*,ssize_t,TimeInternal*,Boolean,RunTimeOpts*,PtpClock*); --void handleDelayResp(MsgHeader*,Octet*,ssize_t,Boolean,RunTimeOpts*,PtpClock*); --void handleManagement(MsgHeader*,Octet*,ssize_t,Boolean,RunTimeOpts*,PtpClock*); -- --void issueSync(RunTimeOpts*,PtpClock*); --void issueFollowup(TimeInternal*,RunTimeOpts*,PtpClock*); --void issueDelayReq(RunTimeOpts*,PtpClock*); --void issueDelayResp(TimeInternal*,MsgHeader*,RunTimeOpts*,PtpClock*); --void issueManagement(MsgHeader*,MsgManagement*,RunTimeOpts*,PtpClock*); -+Boolean doInit(PtpClock*); -+void doState(PtpClock*); -+void toState(UInteger8,PtpClock*); -+ -+void handle(PtpClock*); -+void handleSync(MsgHeader*,Octet*,ssize_t,TimeInternal*,Boolean,Boolean,PtpClock*); -+void handleFollowUp(MsgHeader*,Octet*,ssize_t,Boolean,PtpClock*); -+void handleDelayReq(MsgHeader*,Octet*,ssize_t,TimeInternal*,Boolean,Boolean,PtpClock*); -+void handleDelayResp(MsgHeader*,Octet*,ssize_t,Boolean,PtpClock*); -+void handleManagement(MsgHeader*,Octet*,ssize_t,Boolean,PtpClock*); -+ -+void issueSync(PtpClock*); -+void issueFollowup(TimeInternal*,PtpClock*); -+void issueDelayReq(PtpClock*); -+void issueDelayResp(TimeInternal*,MsgHeader*,PtpClock*); -+void issueManagement(MsgHeader*,MsgManagement*,PtpClock*); - - MsgSync * addForeign(Octet*,MsgHeader*,PtpClock*); - -@@ -26,43 +26,70 @@ MsgSync * addForeign(Octet*,MsgHeader*,PtpClock*); - 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) -+void protocol(PtpClock *ptpClock) - { - DBG("event POWERUP\n"); - -- toState(PTP_INITIALIZING, rtOpts, ptpClock); -+ toState(PTP_INITIALIZING, ptpClock); - - for(;;) - { - if(ptpClock->port_state != PTP_INITIALIZING) -- doState(rtOpts, ptpClock); -- else if(!doInit(rtOpts, ptpClock)) -+ doState(ptpClock); -+ else if(!doInit(ptpClock)) - return; - - if(ptpClock->message_activity) - DBGV("activity\n"); - else -+ { - DBGV("no activity\n"); -+ timeNoActivity(ptpClock); -+ } - } - } - --Boolean doInit(RunTimeOpts *rtOpts, PtpClock *ptpClock) -+Boolean doInit(PtpClock *ptpClock) - { - DBG("manufacturerIdentity: %s\n", MANUFACTURER_ID); - - /* initialize networking */ -- netShutdown(&ptpClock->netPath); -- if(!netInit(&ptpClock->netPath, rtOpts, ptpClock)) -+ netShutdown(ptpClock); -+ if(!netInit(ptpClock)) - { - ERROR("failed to initialize network\n"); -- toState(PTP_FAULTY, rtOpts, ptpClock); -+ toState(PTP_FAULTY, ptpClock); - return FALSE; - } -- -+ -+ /* initialize timing, may fail e.g. if timer depends on hardware */ -+ if(!initTime(ptpClock)) -+ { -+ ERROR("failed to initialize timing\n"); -+ toState(PTP_FAULTY, ptpClock); -+ return FALSE; -+ } -+ -+ switch (ptpClock->runTimeOpts.time) { -+ case TIME_SYSTEM: -+ case TIME_SYSTEM_LINUX_HW: -+ case TIME_SYSTEM_LINUX_SW: -+ /* -+ * send time stamp will be returned to socket when available, -+ * either via IP_MULTICAST_LOOP or SIOCSHWTSTAMP + error queue -+ */ -+ ptpClock->delayedTiming = FALSE; -+ break; -+ default: -+ /* ask for time stamp shortly after sending */ -+ ptpClock->delayedTiming = TRUE; -+ break; -+ } -+ - /* initialize other stuff */ -- initData(rtOpts, ptpClock); -+ initData(ptpClock); - initTimer(); -- initClock(rtOpts, ptpClock); -+ initClock(ptpClock); - m1(ptpClock); - msgPackHeader(ptpClock->msgObuf, ptpClock); - -@@ -71,7 +98,7 @@ Boolean doInit(RunTimeOpts *rtOpts, PtpClock *ptpClock) - DBG("256*log2(clock variance): %d\n", ptpClock->clock_variance); - DBG("clock stratum: %d\n", ptpClock->clock_stratum); - DBG("clock preferred?: %s\n", ptpClock->preferred?"yes":"no"); -- DBG("bound interface name: %s\n", rtOpts->ifaceName); -+ DBG("bound interface name: %s\n", ptpClock->runTimeOpts.ifaceName); - DBG("communication technology: %d\n", ptpClock->port_communication_technology); - DBG("uuid: %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", - ptpClock->port_uuid_field[0], ptpClock->port_uuid_field[1], ptpClock->port_uuid_field[2], -@@ -85,12 +112,12 @@ Boolean doInit(RunTimeOpts *rtOpts, PtpClock *ptpClock) - DBG("general port address: %hhx %hhx\n", - ptpClock->general_port_address[0], ptpClock->general_port_address[1]); - -- toState(PTP_LISTENING, rtOpts, ptpClock); -+ toState(PTP_LISTENING, ptpClock); - return TRUE; - } - - /* handle actions and events for 'port_state' */ --void doState(RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void doState(PtpClock *ptpClock) - { - UInteger8 state; - -@@ -105,9 +132,9 @@ void doState(RunTimeOpts *rtOpts, PtpClock *ptpClock) - if(ptpClock->record_update) - { - ptpClock->record_update = FALSE; -- state = bmc(ptpClock->foreign, rtOpts, ptpClock); -+ state = bmc(ptpClock->foreign, ptpClock); - if(state != ptpClock->port_state) -- toState(state, rtOpts, ptpClock); -+ toState(state, ptpClock); - } - break; - -@@ -121,27 +148,27 @@ void doState(RunTimeOpts *rtOpts, PtpClock *ptpClock) - /* imaginary troubleshooting */ - - DBG("event FAULT_CLEARED\n"); -- toState(PTP_INITIALIZING, rtOpts, ptpClock); -+ toState(PTP_INITIALIZING, ptpClock); - return; - - case PTP_LISTENING: - case PTP_PASSIVE: - case PTP_UNCALIBRATED: - case PTP_SLAVE: -- handle(rtOpts, ptpClock); -+ handle(ptpClock); - - if(timerExpired(SYNC_RECEIPT_TIMER, ptpClock->itimer)) - { - DBG("event SYNC_RECEIPT_TIMEOUT_EXPIRES\n"); - ptpClock->number_foreign_records = 0; - ptpClock->foreign_record_i = 0; -- if(!rtOpts->slaveOnly && ptpClock->clock_stratum != 255) -+ if(!ptpClock->runTimeOpts.slaveOnly && ptpClock->clock_stratum != 255) - { - m1(ptpClock); -- toState(PTP_MASTER, rtOpts, ptpClock); -+ toState(PTP_MASTER, ptpClock); - } - else if(ptpClock->port_state != PTP_LISTENING) -- toState(PTP_LISTENING, rtOpts, ptpClock); -+ toState(PTP_LISTENING, ptpClock); - } - - break; -@@ -150,18 +177,18 @@ void doState(RunTimeOpts *rtOpts, PtpClock *ptpClock) - if(timerExpired(SYNC_INTERVAL_TIMER, ptpClock->itimer)) - { - DBGV("event SYNC_INTERVAL_TIMEOUT_EXPIRES\n"); -- issueSync(rtOpts, ptpClock); -+ issueSync(ptpClock); - } - -- handle(rtOpts, ptpClock); -+ handle(ptpClock); - -- if(rtOpts->slaveOnly || ptpClock->clock_stratum == 255) -- toState(PTP_LISTENING, rtOpts, ptpClock); -+ if(ptpClock->runTimeOpts.slaveOnly || ptpClock->clock_stratum == 255) -+ toState(PTP_LISTENING, ptpClock); - - break; - - case PTP_DISABLED: -- handle(rtOpts, ptpClock); -+ handle(ptpClock); - break; - - default: -@@ -171,10 +198,10 @@ void doState(RunTimeOpts *rtOpts, PtpClock *ptpClock) - } - - /* perform actions required when leaving 'port_state' and entering 'state' */ --void toState(UInteger8 state, RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void toState(UInteger8 state, PtpClock *ptpClock) - { - ptpClock->message_activity = TRUE; -- -+ - /* leaving state tasks */ - switch(ptpClock->port_state) - { -@@ -184,12 +211,14 @@ void toState(UInteger8 state, RunTimeOpts *rtOpts, PtpClock *ptpClock) - break; - - case PTP_SLAVE: -- initClock(rtOpts, ptpClock); -+ initClock(ptpClock); - break; - - default: - break; - } -+ -+ timeToState(state, ptpClock); - - /* entering state tasks */ - switch(state) -@@ -247,7 +276,7 @@ void toState(UInteger8 state, RunTimeOpts *rtOpts, PtpClock *ptpClock) - case PTP_SLAVE: - DBG("state PTP_PTP_SLAVE\n"); - -- initClock(rtOpts, ptpClock); -+ initClock(ptpClock); - - /* R is chosen to allow a few syncs before we first get a one-way delay estimate */ - /* this is to allow the offset filter to fill for an accurate initial clock reset */ -@@ -271,25 +300,27 @@ void toState(UInteger8 state, RunTimeOpts *rtOpts, PtpClock *ptpClock) - break; - } - -- if(rtOpts->displayStats) -- displayStats(rtOpts, ptpClock); -+ if(ptpClock->runTimeOpts.displayStats) -+ displayStats(ptpClock); - } - - /* check and handle received messages */ --void handle(RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void handle(PtpClock *ptpClock) - { - int ret; - ssize_t length; - Boolean isFromSelf; -+ Boolean isEvent; -+ Boolean badTime = FALSE; - TimeInternal time = { 0, 0 }; - - if(!ptpClock->message_activity) - { -- ret = netSelect(0, &ptpClock->netPath); -+ ret = netSelect(0, ptpClock); - if(ret < 0) - { - PERROR("failed to poll sockets"); -- toState(PTP_FAULTY, rtOpts, ptpClock); -+ toState(PTP_FAULTY, ptpClock); - return; - } - else if(!ret) -@@ -301,21 +332,25 @@ void handle(RunTimeOpts *rtOpts, PtpClock *ptpClock) - } - - DBGV("handle: something\n"); -- -- length = netRecvEvent(ptpClock->msgIbuf, &time, &ptpClock->netPath); -+ -+ isEvent = TRUE; -+ length = netRecvEvent(ptpClock->msgIbuf, -+ ptpClock->delayedTiming ? NULL : &time, -+ ptpClock); - if(length < 0) - { - PERROR("failed to receive on the event socket"); -- toState(PTP_FAULTY, rtOpts, ptpClock); -+ toState(PTP_FAULTY, ptpClock); - return; - } - else if(!length) - { -- length = netRecvGeneral(ptpClock->msgIbuf, &ptpClock->netPath); -+ isEvent = FALSE; -+ length = netRecvGeneral(ptpClock->msgIbuf, ptpClock); - if(length < 0) - { - PERROR("failed to receive on the general socket"); -- toState(PTP_FAULTY, rtOpts, ptpClock); -+ toState(PTP_FAULTY, ptpClock); - return; - } - else if(!length) -@@ -330,18 +365,36 @@ void handle(RunTimeOpts *rtOpts, PtpClock *ptpClock) - if(length < HEADER_LENGTH) - { - ERROR("message shorter than header length\n"); -- toState(PTP_FAULTY, rtOpts, ptpClock); -+ toState(PTP_FAULTY, ptpClock); - return; - } - - msgUnpackHeader(ptpClock->msgIbuf, &ptpClock->msgTmpHeader); -+ -+ if(isEvent && ptpClock->delayedTiming) -+ { -+ /* query hardware for matching receive time stamp */ -+ if(!getReceiveTime(&time, ptpClock->msgTmpHeader.sourceUuid, ptpClock->msgTmpHeader.sequenceId, ptpClock)) -+ { -+ /* -+ * Incoming packets without hardware time stamp cannot be ignored outright because -+ * a master might only be able to time stamp DelayReq packets; ignoring the Sync -+ * packets from another, better clock would break the clock selection protocol. -+ * Therefore set system time as fallback and decide below what to do. -+ */ -+ DBGV("*** message with no time stamp ***\n"); -+ getTime(&time, ptpClock); -+ badTime = TRUE; -+ } -+ } - -- DBGV("event Receipt of Message\n" -+ DBGV("%s Receipt of Message\n" - " version %d\n" - " type %d\n" - " uuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n" - " sequence %d\n" - " time %us %dns\n", -+ isEvent ? "event" : "control", - ptpClock->msgTmpHeader.versionPTP, - ptpClock->msgTmpHeader.control, - ptpClock->msgTmpHeader.sourceUuid[0], ptpClock->msgTmpHeader.sourceUuid[1], -@@ -370,28 +423,28 @@ void handle(RunTimeOpts *rtOpts, PtpClock *ptpClock) - /* 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); -+ subTime(&time, &time, &ptpClock->runTimeOpts.inboundLatency); - - switch(ptpClock->msgTmpHeader.control) - { - case PTP_SYNC_MESSAGE: -- handleSync(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, &time, isFromSelf, rtOpts, ptpClock); -+ handleSync(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, &time, badTime, isFromSelf, ptpClock); - break; - - case PTP_FOLLOWUP_MESSAGE: -- handleFollowUp(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, rtOpts, ptpClock); -+ handleFollowUp(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, ptpClock); - break; - - case PTP_DELAY_REQ_MESSAGE: -- handleDelayReq(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, &time, isFromSelf, rtOpts, ptpClock); -+ handleDelayReq(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, &time, badTime, isFromSelf, ptpClock); - break; - - case PTP_DELAY_RESP_MESSAGE: -- handleDelayResp(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, rtOpts, ptpClock); -+ handleDelayResp(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, ptpClock); - break; - - case PTP_MANAGEMENT_MESSAGE: -- handleManagement(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, rtOpts, ptpClock); -+ handleManagement(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, ptpClock); - break; - - default: -@@ -400,7 +453,7 @@ void handle(RunTimeOpts *rtOpts, PtpClock *ptpClock) - } - } - --void handleSync(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInternal *time, Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void handleSync(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInternal *time, Boolean badTime, Boolean isFromSelf, PtpClock *ptpClock) - { - MsgSync *sync; - TimeInternal originTimestamp; -@@ -408,7 +461,7 @@ void handleSync(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInternal - if(length < SYNC_PACKET_LENGTH) - { - ERROR("short sync message\n"); -- toState(PTP_FAULTY, rtOpts, ptpClock); -+ toState(PTP_FAULTY, ptpClock); - return; - } - -@@ -450,6 +503,11 @@ void handleSync(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInternal - /* spec recommends handling a sync interval discrepancy as a fault */ - } - -+ /* -+ * TODO: Sync packets without hardware time stamp are rare, but might happen. -+ * Need to decide what to do with the bad default time stamp, similar to handleDelayReq(). -+ */ -+ - ptpClock->sync_receive_time.seconds = time->seconds; - ptpClock->sync_receive_time.nanoseconds = time->nanoseconds; - -@@ -459,8 +517,8 @@ void handleSync(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInternal - - toInternalTime(&originTimestamp, &sync->originTimestamp, &ptpClock->halfEpoch); - updateOffset(&originTimestamp, &ptpClock->sync_receive_time, -- &ptpClock->ofm_filt, rtOpts, ptpClock); -- updateClock(rtOpts, ptpClock); -+ &ptpClock->ofm_filt, ptpClock); -+ updateClock(ptpClock); - } - else - { -@@ -471,7 +529,7 @@ void handleSync(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInternal - - if(!(--ptpClock->R)) - { -- issueDelayReq(rtOpts, ptpClock); -+ issueDelayReq(ptpClock); - - ptpClock->Q = 0; - ptpClock->R = getRand(&ptpClock->random_seed)%(PTP_DELAY_REQ_INTERVAL - 2) + 2; -@@ -499,15 +557,15 @@ void handleSync(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInternal - } - else if(ptpClock->port_state == PTP_MASTER && ptpClock->clock_followup_capable) - { -- addTime(time, time, &rtOpts->outboundLatency); -- issueFollowup(time, rtOpts, ptpClock); -+ addTime(time, time, &ptpClock->runTimeOpts.outboundLatency); -+ issueFollowup(time, ptpClock); - } - } - break; - } - } - --void handleFollowUp(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void handleFollowUp(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean isFromSelf, PtpClock *ptpClock) - { - MsgFollowUp *follow; - TimeInternal preciseOriginTimestamp; -@@ -515,7 +573,7 @@ void handleFollowUp(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean i - if(length < FOLLOW_UP_PACKET_LENGTH) - { - ERROR("short folow up message\n"); -- toState(PTP_FAULTY, rtOpts, ptpClock); -+ toState(PTP_FAULTY, ptpClock); - return; - } - -@@ -548,8 +606,8 @@ void handleFollowUp(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean i - - toInternalTime(&preciseOriginTimestamp, &follow->preciseOriginTimestamp, &ptpClock->halfEpoch); - updateOffset(&preciseOriginTimestamp, &ptpClock->sync_receive_time, -- &ptpClock->ofm_filt, rtOpts, ptpClock); -- updateClock(rtOpts, ptpClock); -+ &ptpClock->ofm_filt, ptpClock); -+ updateClock(ptpClock); - } - else - { -@@ -563,12 +621,12 @@ void handleFollowUp(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean i - } - } - --void handleDelayReq(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInternal *time, Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void handleDelayReq(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInternal *time, Boolean badTime, Boolean isFromSelf, PtpClock *ptpClock) - { - if(length < DELAY_REQ_PACKET_LENGTH) - { - ERROR("short delay request message\n"); -- toState(PTP_FAULTY, rtOpts, ptpClock); -+ toState(PTP_FAULTY, ptpClock); - return; - } - -@@ -585,7 +643,10 @@ void handleDelayReq(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInter - || header->sourceCommunicationTechnology == PTP_DEFAULT - || ptpClock->clock_communication_technology == PTP_DEFAULT ) - { -- issueDelayResp(time, &ptpClock->msgTmpHeader, rtOpts, ptpClock); -+ if( badTime ) -+ NOTIFY("avoid inaccurate DelayResp because of bad time stamp\n"); -+ else -+ issueDelayResp(time, &ptpClock->msgTmpHeader, ptpClock); - } - - break; -@@ -598,12 +659,12 @@ void handleDelayReq(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInter - ptpClock->delay_req_send_time.seconds = time->seconds; - ptpClock->delay_req_send_time.nanoseconds = time->nanoseconds; - -- addTime(&ptpClock->delay_req_send_time, &ptpClock->delay_req_send_time, &rtOpts->outboundLatency); -+ addTime(&ptpClock->delay_req_send_time, &ptpClock->delay_req_send_time, &ptpClock->runTimeOpts.outboundLatency); - - if(ptpClock->delay_req_receive_time.seconds) - { - updateDelay(&ptpClock->delay_req_send_time, &ptpClock->delay_req_receive_time, -- &ptpClock->owd_filt, rtOpts, ptpClock); -+ &ptpClock->owd_filt, ptpClock); - - ptpClock->delay_req_send_time.seconds = 0; - ptpClock->delay_req_send_time.nanoseconds = 0; -@@ -619,14 +680,14 @@ void handleDelayReq(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInter - } - } - --void handleDelayResp(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void handleDelayResp(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean isFromSelf, PtpClock *ptpClock) - { - MsgDelayResp *resp; - - if(length < DELAY_RESP_PACKET_LENGTH) - { - ERROR("short delay request message\n"); -- toState(PTP_FAULTY, rtOpts, ptpClock); -+ toState(PTP_FAULTY, ptpClock); - return; - } - -@@ -658,7 +719,7 @@ void handleDelayResp(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean - if(ptpClock->delay_req_send_time.seconds) - { - updateDelay(&ptpClock->delay_req_send_time, &ptpClock->delay_req_receive_time, -- &ptpClock->owd_filt, rtOpts, ptpClock); -+ &ptpClock->owd_filt, ptpClock); - - ptpClock->delay_req_send_time.seconds = 0; - ptpClock->delay_req_send_time.nanoseconds = 0; -@@ -678,7 +739,7 @@ void handleDelayResp(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean - } - } - --void handleManagement(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void handleManagement(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean isFromSelf, PtpClock *ptpClock) - { - MsgManagement *manage; - -@@ -705,14 +766,14 @@ void handleManagement(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean - case PTP_MM_GET_PORT_DATA_SET: - case PTP_MM_GET_GLOBAL_TIME_DATA_SET: - case PTP_MM_GET_FOREIGN_DATA_SET: -- issueManagement(header, manage, rtOpts, ptpClock); -+ issueManagement(header, manage, ptpClock); - break; - - default: - ptpClock->record_update = TRUE; -- state = msgUnloadManagement(ptpClock->msgIbuf, manage, ptpClock, rtOpts); -+ state = msgUnloadManagement(ptpClock->msgIbuf, manage, ptpClock); - if(state != ptpClock->port_state) -- toState(state, rtOpts, ptpClock); -+ toState(state, ptpClock); - break; - } - } -@@ -723,25 +784,40 @@ void handleManagement(MsgHeader *header, Octet *msgIbuf, ssize_t length, Boolean - } - - /* pack and send various messages */ --void issueSync(RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void issueSync(PtpClock *ptpClock) - { - TimeInternal internalTime; - TimeRepresentation originTimestamp; - - ++ptpClock->last_sync_event_sequence_number; - ptpClock->grandmaster_sequence_number = ptpClock->last_sync_event_sequence_number; -- -- getTime(&internalTime); -+ -+ /* try to predict outgoing time stamp */ -+ getTime(&internalTime, ptpClock); - fromInternalTime(&internalTime, &originTimestamp, ptpClock->halfEpoch); -- msgPackSync(ptpClock->msgObuf, FALSE, &originTimestamp, ptpClock); -+ msgPackSync(ptpClock->msgObuf, FALSE, TRUE, &originTimestamp, ptpClock); - -- if(!netSendEvent(ptpClock->msgObuf, SYNC_PACKET_LENGTH, &ptpClock->netPath)) -- toState(PTP_FAULTY, rtOpts, ptpClock); -+ if(!netSendEvent(ptpClock->msgObuf, SYNC_PACKET_LENGTH, -+ ptpClock->delayedTiming ? &internalTime : NULL, -+ ptpClock)) -+ toState(PTP_FAULTY, ptpClock); - else -+ { - DBGV("sent sync message\n"); -+ if(ptpClock->delayedTiming) -+ { -+ if (internalTime.seconds || internalTime.nanoseconds) { -+ /* compensate with configurable latency, then tell client real time stamp */ -+ addTime(&internalTime, &internalTime, &ptpClock->runTimeOpts.outboundLatency); -+ issueFollowup(&internalTime, ptpClock); -+ } else { -+ NOTIFY("WARNING: sync message without hardware time stamp, skipped followup\n"); -+ } -+ } -+ } - } - --void issueFollowup(TimeInternal *time, RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void issueFollowup(TimeInternal *time, PtpClock *ptpClock) - { - TimeRepresentation preciseOriginTimestamp; - -@@ -750,46 +826,62 @@ void issueFollowup(TimeInternal *time, RunTimeOpts *rtOpts, PtpClock *ptpClock) - fromInternalTime(time, &preciseOriginTimestamp, ptpClock->halfEpoch); - msgPackFollowUp(ptpClock->msgObuf, ptpClock->last_sync_event_sequence_number, &preciseOriginTimestamp, ptpClock); - -- if(!netSendGeneral(ptpClock->msgObuf, FOLLOW_UP_PACKET_LENGTH, &ptpClock->netPath)) -- toState(PTP_FAULTY, rtOpts, ptpClock); -+ if(!netSendGeneral(ptpClock->msgObuf, FOLLOW_UP_PACKET_LENGTH, ptpClock)) -+ toState(PTP_FAULTY, ptpClock); - else - DBGV("sent followup message\n"); - } - --void issueDelayReq(RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void issueDelayReq(PtpClock *ptpClock) - { - TimeInternal internalTime; - TimeRepresentation originTimestamp; - - ptpClock->sentDelayReq = TRUE; - ptpClock->sentDelayReqSequenceId = ++ptpClock->last_sync_event_sequence_number; -- -- getTime(&internalTime); -+ -+ /* try to predict outgoing time stamp */ -+ getTime(&internalTime, ptpClock); - fromInternalTime(&internalTime, &originTimestamp, ptpClock->halfEpoch); -- msgPackDelayReq(ptpClock->msgObuf, FALSE, &originTimestamp, ptpClock); -+ msgPackDelayReq(ptpClock->msgObuf, FALSE, FALSE, &originTimestamp, ptpClock); - -- if(!netSendEvent(ptpClock->msgObuf, DELAY_REQ_PACKET_LENGTH, &ptpClock->netPath)) -- toState(PTP_FAULTY, rtOpts, ptpClock); -+ if(!netSendEvent(ptpClock->msgObuf, DELAY_REQ_PACKET_LENGTH, -+ ptpClock->delayedTiming ? &internalTime : NULL, -+ ptpClock)) -+ toState(PTP_FAULTY, ptpClock); - else -+ { - DBGV("sent delay request message\n"); -+ if(ptpClock->delayedTiming) -+ { -+ if (internalTime.seconds || internalTime.nanoseconds) { -+ /* compensate with configurable latency, then store for later use */ -+ addTime(&internalTime, &internalTime, &ptpClock->runTimeOpts.outboundLatency); -+ ptpClock->delay_req_send_time = internalTime; -+ } else { -+ NOTIFY("WARNING: delay request message without hardware time stamp, will skip response\n"); -+ ptpClock->sentDelayReq = FALSE; -+ } -+ } -+ } - } - --void issueDelayResp(TimeInternal *time, MsgHeader *header, RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void issueDelayResp(TimeInternal *time, MsgHeader *header, PtpClock *ptpClock) - { - TimeRepresentation delayReceiptTimestamp; - - ++ptpClock->last_general_event_sequence_number; -- -+ - fromInternalTime(time, &delayReceiptTimestamp, ptpClock->halfEpoch); - msgPackDelayResp(ptpClock->msgObuf, header, &delayReceiptTimestamp, ptpClock); - -- if(!netSendGeneral(ptpClock->msgObuf, DELAY_RESP_PACKET_LENGTH, &ptpClock->netPath)) -- toState(PTP_FAULTY, rtOpts, ptpClock); -+ if(!netSendGeneral(ptpClock->msgObuf, DELAY_RESP_PACKET_LENGTH, ptpClock)) -+ toState(PTP_FAULTY, ptpClock); - else - DBGV("sent delay response message\n"); - } - --void issueManagement(MsgHeader *header, MsgManagement *manage, RunTimeOpts *rtOpts, PtpClock *ptpClock) -+void issueManagement(MsgHeader *header, MsgManagement *manage, PtpClock *ptpClock) - { - UInteger16 length; - -@@ -798,8 +890,8 @@ void issueManagement(MsgHeader *header, MsgManagement *manage, RunTimeOpts *rtOp - if(!(length = msgPackManagementResponse(ptpClock->msgObuf, header, manage, ptpClock))) - return; - -- if(!netSendGeneral(ptpClock->msgObuf, length, &ptpClock->netPath)) -- toState(PTP_FAULTY, rtOpts, ptpClock); -+ if(!netSendGeneral(ptpClock->msgObuf, length, ptpClock)) -+ toState(PTP_FAULTY, ptpClock); - else - DBGV("sent management message\n"); - } -diff --git a/src/ptpd.c b/src/ptpd.c -index cb98438..b19ec63 100644 ---- a/src/ptpd.c -+++ b/src/ptpd.c -@@ -19,6 +19,7 @@ int main(int argc, char **argv) - rtOpts.inboundLatency.nanoseconds = DEFAULT_INBOUND_LATENCY; - rtOpts.outboundLatency.nanoseconds = DEFAULT_OUTBOUND_LATENCY; - rtOpts.noResetClock = DEFAULT_NO_RESET_CLOCK; -+ rtOpts.noAdjust = DEFAULT_NO_ADJUST_CLOCK; - rtOpts.s = DEFAULT_DELAY_S; - rtOpts.ap = DEFAULT_AP; - rtOpts.ai = DEFAULT_AI; -@@ -30,12 +31,12 @@ int main(int argc, char **argv) - - if(rtOpts.probe) - { -- probe(&rtOpts, ptpClock); -+ probe(ptpClock); - } - else - { - /* do the protocol engine */ -- protocol(&rtOpts, ptpClock); -+ protocol(ptpClock); - } - - ptpdShutdown(); -diff --git a/src/ptpd.h b/src/ptpd.h -index 55cc3d9..95915fa 100644 ---- a/src/ptpd.h -+++ b/src/ptpd.h -@@ -20,16 +20,16 @@ void addTime(TimeInternal*,TimeInternal*,TimeInternal*); - void subTime(TimeInternal*,TimeInternal*,TimeInternal*); - - /* bmc.c */ --UInteger8 bmc(ForeignMasterRecord*,RunTimeOpts*,PtpClock*); -+UInteger8 bmc(ForeignMasterRecord*,PtpClock*); - void m1(PtpClock*); - void s1(MsgHeader*,MsgSync*,PtpClock*); --void initData(RunTimeOpts*,PtpClock*); -+void initData(PtpClock*); - - /* probe.c */ --void probe(RunTimeOpts*,PtpClock*); -+void probe(PtpClock*); - - /* protocol.c */ --void protocol(RunTimeOpts*,PtpClock*); -+void protocol(PtpClock*); - - - #endif diff --git a/ptpd-1.0.0.tar.bz2 b/ptpd-1.0.0.tar.bz2 deleted file mode 100644 index 56c5893..0000000 --- a/ptpd-1.0.0.tar.bz2 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:179483ceda19b6d88ac0408e19759004ae80efcfadc44d06201a7d0c6fc42c27 -size 383700 diff --git a/ptpd-1.1.0.tar.gz b/ptpd-1.1.0.tar.gz new file mode 100644 index 0000000..5d57d9c --- /dev/null +++ b/ptpd-1.1.0.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a7c6ea83bd53da75ae04a7b7a25fe7c597b4e9ff1f93d46f4502e3fa8a2cb950 +size 401064 diff --git a/ptpd.changes b/ptpd.changes index 148a351..57145ce 100644 --- a/ptpd.changes +++ b/ptpd.changes @@ -1,3 +1,24 @@ +------------------------------------------------------------------- +Thu Oct 27 18:12:26 UTC 2011 - coolo@suse.com + +- use original tar +- do not mention runlevel 4 +- don't depend on octave for documentation +- remove unused patches + +------------------------------------------------------------------- +Mon May 9 14:13:08 UTC 2011 - dbahi@novell.com + +- update to release 1.1.0 (2010-10-12) + * 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 + * Add support for syslog. + * Add support for user configurable TTL. + * Clean up code formatting, headers, comments etc. + ------------------------------------------------------------------- Mon Oct 19 12:59:53 MDT 2009 - alext@suse.de diff --git a/ptpd.spec b/ptpd.spec index 077b1ce..293eaf2 100644 --- a/ptpd.spec +++ b/ptpd.spec @@ -1,7 +1,7 @@ # -# spec file for package ptpd (Version 1.0.0) +# spec file for package ptpd # -# Copyright (c) 2009 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2011 SUSE LINUX Products GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -20,16 +20,13 @@ Name: ptpd Summary: Implements the Precision Time protocol as defined by IEEE 1588 standard -Version: 1.0.0 -Release: 3 +Version: 1.1.0 +Release: 0 License: BSD 3-clause (or similar) Group: System/Daemons -Source0: %{name}-%{version}.tar.bz2 +Source0: http://downloads.sourceforge.net/project/%{name}/%{name}/%{version}/%{name}-%{version}.tar.gz Source1: conf.ptpd.init Source2: conf.sysconfig.ptpd -Patch0: ptpd-1.0.0-git599b03b.patch -Patch1: glibc210.patch -Patch2: append_to_cflags.patch Url: http://ptpd.sourceforge.net/ BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -52,17 +49,8 @@ be easy to port to other platforms. PTPd is free. Everyone is invited to use and contribute to PTPd. - - -Authors: --------- - Kendall Correll - %prep %setup -q -%patch0 -p1 -%patch1 -p1 -%patch2 %build cd src @@ -86,7 +74,8 @@ install -m 644 %{S:2} ${RPM_BUILD_ROOT}/var/adm/fillup-templates/sysconfig.ptpd # # documentation # -cp -r README COPYRIGHT doc RELEASE_NOTES tools ${RPM_BUILD_ROOT}/%{_defaultdocdir}/%{name}/. +cp -r README COPYRIGHT doc RELEASE_NOTES tools ${RPM_BUILD_ROOT}/%{_docdir} +find ${RPM_BUILD_ROOT}/%{_docdir} -type f -print0 | xargs -0 chmod 644 %clean rm -rf ${RPM_BUILD_ROOT} @@ -96,7 +85,7 @@ rm -rf ${RPM_BUILD_ROOT} %{_sbindir}/* %{_initrddir}/* /var/adm/fillup-templates/* -%{_defaultdocdir}/%{name} +%{_docdir} %preun %{stop_on_removal} ptpd