File ptpd-1.0.0-git599b03b.patch of Package ptpd

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 <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> 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 <mpi.h>
+
+#ifndef _WIN32
+# include <unistd.h>
+# include <sys/time.h>
+# include <syslog.h>
+#else
+# include <windows.h>
+# include <sys/timeb.h>
+# define sleep( sec ) Sleep( 1000 * (sec) )
+# define popen  _popen
+# define pclose _pclose
+# define pclose _pclose
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+#include <ctype.h>
+#include <time.h>
+
+#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 <libelf.h>
+/**
+ * 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 <options>\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 <limits.h>
+
 #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<errno.h>
 #include<signal.h>
 #include<fcntl.h>
+#include<syslog.h>
 #include<sys/stat.h>
 #include<time.h>
 #include<sys/time.h>
@@ -19,22 +20,58 @@
 #include<sys/ioctl.h>
 #include<arpa/inet.h>
 
+#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 <stdarg.h>
 
-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 <stdarg.h>
+
+#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(&currentTime, ptpClock);
+    subTime(&offset, time, &currentTime);
+    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, &timestamp, &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
openSUSE Build Service is sponsored by