diff options
| author | Yuval Adam <yuv.adm@gmail.com> | 2014-08-08 14:42:07 +0300 |
|---|---|---|
| committer | Yuval Adam <yuv.adm@gmail.com> | 2014-08-08 14:42:07 +0300 |
| commit | a6163888f3c56123b1db313743c6147ba498732c (patch) | |
| tree | ff7d15d991d1d09ba6cbc0cec80924f57445bfdd /third_party/ptpd-1.1.0/src | |
| parent | c3e4c9a25c2910d2d66d52215b3406b13d5b23d5 (diff) | |
Add third_party libs
Diffstat (limited to 'third_party/ptpd-1.1.0/src')
27 files changed, 7936 insertions, 0 deletions
diff --git a/third_party/ptpd-1.1.0/src/Doxyfile b/third_party/ptpd-1.1.0/src/Doxyfile new file mode 100644 index 0000000..6a2d4c4 --- /dev/null +++ b/third_party/ptpd-1.1.0/src/Doxyfile @@ -0,0 +1,1511 @@ +# Doxyfile 1.5.8
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = 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 = Doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
+# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene,
+# Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = YES
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT =
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <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 = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to FRAME, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature. Other possible values
+# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list;
+# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
+# disables this behavior completely. For backwards compatibility with previous
+# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
+# respectively.
+
+GENERATE_TREEVIEW = NONE
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = YES
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Options related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = YES
+
diff --git a/third_party/ptpd-1.1.0/src/Makefile b/third_party/ptpd-1.1.0/src/Makefile new file mode 100644 index 0000000..589981b --- /dev/null +++ b/third_party/ptpd-1.1.0/src/Makefile @@ -0,0 +1,35 @@ +# Makefile for ptpd
+
+RM = rm -f
+CFLAGS+= -Wall #-DBSD_INTERFACE_FUNCTIONS
+#CPPFLAGS = -DPTPD_DBG -DPTPD_NO_DAEMON
+
+PROG = ptpd
+SRCS = ptpd.c arith.c bmc.c probe.c protocol.c \
+ dep/msg.c dep/net.c dep/servo.c dep/startup.c dep/sys.c dep/timer.c
+OBJS = $(SRCS:.c=.o)
+
+HDRS = ptpd.h constants.h datatypes.h \
+ dep/ptpd_dep.h dep/constants_dep.h dep/datatypes_dep.h
+
+CSCOPE = cscope
+GTAGS = gtags
+DOXYGEN = doxygen
+
+TAGFILES = GPATH GRTAGS GSYMS GTAGS cscope.in.out cscope.out cscope.po.out
+
+.c.o:
+ $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(CC) $(LDFLAGS) -o $@ $(OBJS)
+
+tags:
+ $(CSCOPE) -R -q -b
+ $(GTAGS)
+ $(DOXYGEN) Doxyfile
+
+clean:
+ $(RM) $(PROG) $(OBJS) $(TAGFILES) make.out
diff --git a/third_party/ptpd-1.1.0/src/arith.c b/third_party/ptpd-1.1.0/src/arith.c new file mode 100644 index 0000000..a258a9e --- /dev/null +++ b/third_party/ptpd-1.1.0/src/arith.c @@ -0,0 +1,113 @@ +/**
+ * @file arith.c
+ * @date Tue Jul 20 16:12:51 2010
+ *
+ * @brief Time format conversion routines and additional math functions.
+ *
+ *
+ */
+
+#include "ptpd.h"
+
+/* from annex C of the spec */
+UInteger32
+crc_algorithm(Octet * buf, Integer16 length)
+{
+ Integer16 i;
+ UInteger8 data;
+ UInteger32 polynomial = 0xedb88320, crc = 0xffffffff;
+
+ while (length-- > 0) {
+ data = *(UInteger8 *) (buf++);
+
+ for (i = 0; i < 8; i++) {
+ if ((crc ^ data) & 1) {
+ crc = (crc >> 1);
+ crc ^= polynomial;
+ } else {
+ crc = (crc >> 1);
+ }
+ data >>= 1;
+ }
+ }
+
+ return crc ^ 0xffffffff;
+}
+
+UInteger32
+sum(Octet * buf, Integer16 length)
+{
+ UInteger32 sum = 0;
+
+ while (length-- > 0)
+ sum += *(UInteger8 *) (buf++);
+
+ return sum;
+}
+
+void
+fromInternalTime(TimeInternal * internal, TimeRepresentation * external, Boolean halfEpoch)
+{
+ external->seconds = labs(internal->seconds) + halfEpoch * INT_MAX;
+
+ if (internal->seconds < 0 || internal->nanoseconds < 0) {
+ external->nanoseconds = labs(internal->nanoseconds) | ~INT_MAX;
+ } else {
+ external->nanoseconds = labs(internal->nanoseconds);
+ }
+
+ DBGV("fromInternalTime: %10ds %11dns -> %10us %11dns\n",
+ internal->seconds, internal->nanoseconds,
+ external->seconds, external->nanoseconds);
+}
+
+void
+toInternalTime(TimeInternal * internal, TimeRepresentation * external, Boolean * halfEpoch)
+{
+ *halfEpoch = external->seconds / INT_MAX;
+
+ if (external->nanoseconds & ~INT_MAX) {
+ internal->seconds = -(external->seconds % INT_MAX);
+ internal->nanoseconds = -(external->nanoseconds & INT_MAX);
+ } else {
+ internal->seconds = external->seconds % INT_MAX;
+ internal->nanoseconds = external->nanoseconds;
+ }
+
+ DBGV("toInternalTime: %10ds %11dns <- %10us %11dns\n",
+ internal->seconds, internal->nanoseconds,
+ external->seconds, external->nanoseconds);
+}
+
+void
+normalizeTime(TimeInternal * r)
+{
+ r->seconds += r->nanoseconds / 1000000000;
+ r->nanoseconds -= r->nanoseconds / 1000000000 * 1000000000;
+
+ if (r->seconds > 0 && r->nanoseconds < 0) {
+ r->seconds -= 1;
+ r->nanoseconds += 1000000000;
+ } else if (r->seconds < 0 && r->nanoseconds > 0) {
+ r->seconds += 1;
+ r->nanoseconds -= 1000000000;
+ }
+}
+
+void
+addTime(TimeInternal * r, TimeInternal * x, TimeInternal * y)
+{
+ r->seconds = x->seconds + y->seconds;
+ r->nanoseconds = x->nanoseconds + y->nanoseconds;
+
+ normalizeTime(r);
+}
+
+void
+subTime(TimeInternal * r, TimeInternal * x, TimeInternal * y)
+{
+ r->seconds = x->seconds - y->seconds;
+ r->nanoseconds = x->nanoseconds - y->nanoseconds;
+
+ normalizeTime(r);
+}
diff --git a/third_party/ptpd-1.1.0/src/bmc.c b/third_party/ptpd-1.1.0/src/bmc.c new file mode 100644 index 0000000..0b3ce74 --- /dev/null +++ b/third_party/ptpd-1.1.0/src/bmc.c @@ -0,0 +1,330 @@ +/**
+ * @file bmc.c
+ * @date Wed Jun 23 09:36:09 2010
+ *
+ * @brief Best master clock selection code.
+ *
+ * The functions in this file are used by the daemon to select the
+ * best master clock from any number of possibilities.
+ */
+
+#include "ptpd.h"
+
+void
+initData(RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ DBG("initData\n");
+
+ if (rtOpts->slaveOnly)
+ rtOpts->clockStratum = 255;
+
+ /* Port configuration data set */
+ ptpClock->last_sync_event_sequence_number = 0;
+ ptpClock->last_general_event_sequence_number = 0;
+ ptpClock->port_id_field = 1;
+ ptpClock->burst_enabled = BURST_ENABLED;
+
+ /* Default data set */
+ 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_variance = rtOpts->clockVariance; /* see spec 7.7 */
+ ptpClock->clock_followup_capable = CLOCK_FOLLOWUP;
+ ptpClock->preferred = rtOpts->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);
+ ptpClock->number_ports = NUMBER_PORTS;
+ ptpClock->number_foreign_records = 0;
+ ptpClock->max_foreign_records = rtOpts->max_foreign_records;
+
+ /* Global time properties data set */
+ ptpClock->current_utc_offset = rtOpts->currentUtcOffset;
+ ptpClock->epoch_number = rtOpts->epochNumber;
+
+ /* other stuff */
+ ptpClock->random_seed = ptpClock->port_uuid_field[PTP_UUID_LENGTH - 1];
+}
+
+/* see spec table 18 */
+void
+m1(PtpClock * ptpClock)
+{
+ /* Default data set */
+ ptpClock->steps_removed = 0;
+ ptpClock->offset_from_master.seconds = 0;
+ ptpClock->offset_from_master.nanoseconds = 0;
+ ptpClock->one_way_delay.seconds = 0;
+ ptpClock->one_way_delay.nanoseconds = 0;
+
+ /* Parent data set */
+ ptpClock->parent_communication_technology = ptpClock->clock_communication_technology;
+ memcpy(ptpClock->parent_uuid, ptpClock->clock_uuid_field, PTP_UUID_LENGTH);
+ ptpClock->parent_port_id = ptpClock->clock_port_id_field;
+ ptpClock->parent_last_sync_sequence_number = 0;
+ ptpClock->parent_followup_capable = ptpClock->clock_followup_capable;
+ ptpClock->parent_external_timing = ptpClock->external_timing;
+ ptpClock->parent_variance = ptpClock->clock_variance;
+ ptpClock->grandmaster_communication_technology = ptpClock->clock_communication_technology;
+ memcpy(ptpClock->grandmaster_uuid_field, ptpClock->clock_uuid_field, PTP_UUID_LENGTH);
+ ptpClock->grandmaster_port_id_field = ptpClock->clock_port_id_field;
+ ptpClock->grandmaster_stratum = ptpClock->clock_stratum;
+ memcpy(ptpClock->grandmaster_identifier, ptpClock->clock_identifier, PTP_CODE_STRING_LENGTH);
+ ptpClock->grandmaster_variance = ptpClock->clock_variance;
+ ptpClock->grandmaster_preferred = ptpClock->preferred;
+ ptpClock->grandmaster_is_boundary_clock = ptpClock->is_boundary_clock;
+ ptpClock->grandmaster_sequence_number = ptpClock->last_sync_event_sequence_number;
+}
+
+/* see spec table 21 */
+void
+s1(MsgHeader * header, MsgSync * sync, PtpClock * ptpClock)
+{
+ /* Current data set */
+ ptpClock->steps_removed = sync->localStepsRemoved + 1;
+
+ /* Parent data set */
+ ptpClock->parent_communication_technology = header->sourceCommunicationTechnology;
+ memcpy(ptpClock->parent_uuid, header->sourceUuid, PTP_UUID_LENGTH);
+ ptpClock->parent_port_id = header->sourcePortId;
+ ptpClock->parent_last_sync_sequence_number = header->sequenceId;
+ ptpClock->parent_followup_capable = getFlag(header->flags, PTP_ASSIST);
+ ptpClock->parent_external_timing = getFlag(header->flags, PTP_EXT_SYNC);
+ ptpClock->parent_variance = sync->localClockVariance;
+ ptpClock->grandmaster_communication_technology = sync->grandmasterCommunicationTechnology;
+ memcpy(ptpClock->grandmaster_uuid_field, sync->grandmasterClockUuid, PTP_UUID_LENGTH);
+ ptpClock->grandmaster_port_id_field = sync->grandmasterPortId;
+ ptpClock->grandmaster_stratum = sync->grandmasterClockStratum;
+ memcpy(ptpClock->grandmaster_identifier, sync->grandmasterClockIdentifier, PTP_CODE_STRING_LENGTH);
+ ptpClock->grandmaster_variance = sync->grandmasterClockVariance;
+ ptpClock->grandmaster_preferred = sync->grandmasterPreferred;
+ ptpClock->grandmaster_is_boundary_clock = sync->grandmasterIsBoundaryClock;
+ ptpClock->grandmaster_sequence_number = sync->grandmasterSequenceId;
+
+ /* Global time properties data set */
+ ptpClock->current_utc_offset = sync->currentUTCOffset;
+ ptpClock->leap_59 = getFlag(header->flags, PTP_LI_59);
+ ptpClock->leap_61 = getFlag(header->flags, PTP_LI_61);
+ ptpClock->epoch_number = sync->epochNumber;
+}
+
+void
+copyD0(MsgHeader * header, MsgSync * sync, PtpClock * ptpClock)
+{
+ sync->grandmasterCommunicationTechnology = ptpClock->clock_communication_technology;
+ memcpy(sync->grandmasterClockUuid, ptpClock->port_uuid_field, PTP_UUID_LENGTH);
+ sync->grandmasterPortId = ptpClock->port_id_field;
+ sync->grandmasterClockStratum = ptpClock->clock_stratum;
+ memcpy(sync->grandmasterClockIdentifier, ptpClock->clock_identifier, PTP_CODE_STRING_LENGTH);
+ sync->grandmasterClockVariance = ptpClock->clock_variance;
+ sync->grandmasterIsBoundaryClock = ptpClock->is_boundary_clock;
+ sync->grandmasterPreferred = ptpClock->preferred;
+ sync->localStepsRemoved = ptpClock->steps_removed;
+ header->sourceCommunicationTechnology = ptpClock->clock_communication_technology;
+ memcpy(header->sourceUuid, ptpClock->port_uuid_field, PTP_UUID_LENGTH);
+ header->sourcePortId = ptpClock->port_id_field;
+ sync->grandmasterSequenceId = ptpClock->grandmaster_sequence_number;
+ header->sequenceId = ptpClock->grandmaster_sequence_number;
+}
+
+int
+getIdentifierOrder(Octet identifier[PTP_CODE_STRING_LENGTH])
+{
+ if (!memcmp(identifier, IDENTIFIER_ATOM, PTP_CODE_STRING_LENGTH))
+ return 1;
+ else if (!memcmp(identifier, IDENTIFIER_GPS, PTP_CODE_STRING_LENGTH))
+ return 1;
+ else if (!memcmp(identifier, IDENTIFIER_NTP, PTP_CODE_STRING_LENGTH))
+ return 2;
+ else if (!memcmp(identifier, IDENTIFIER_HAND, PTP_CODE_STRING_LENGTH))
+ return 3;
+ else if (!memcmp(identifier, IDENTIFIER_INIT, PTP_CODE_STRING_LENGTH))
+ return 4;
+ else if (!memcmp(identifier, IDENTIFIER_DFLT, PTP_CODE_STRING_LENGTH))
+ return 5;
+
+ return 6;
+}
+
+/* return similar to memcmp()s
+ note: communicationTechnology can be ignored because
+ if they differed they would not have made it here */
+Integer8
+bmcDataSetComparison(MsgHeader * headerA, MsgSync * syncA,
+ MsgHeader * headerB, MsgSync * syncB, PtpClock * ptpClock)
+{
+ DBGV("bmcDataSetComparison: start\n");
+ if (!(syncA->grandmasterPortId == syncB->grandmasterPortId
+ && !memcmp(syncA->grandmasterClockUuid, syncB->grandmasterClockUuid, PTP_UUID_LENGTH))) {
+ if (syncA->grandmasterClockStratum < syncB->grandmasterClockStratum)
+ goto A;
+ else if (syncA->grandmasterClockStratum > syncB->grandmasterClockStratum)
+ goto B;
+
+ /* grandmasterClockStratums same */
+ if (getIdentifierOrder(syncA->grandmasterClockIdentifier) < getIdentifierOrder(syncB->grandmasterClockIdentifier))
+ goto A;
+ if (getIdentifierOrder(syncA->grandmasterClockIdentifier) > getIdentifierOrder(syncB->grandmasterClockIdentifier))
+ goto B;
+
+ /* grandmasterClockIdentifiers same */
+ if (syncA->grandmasterClockStratum > 2) {
+ if (syncA->grandmasterClockVariance > syncB->grandmasterClockVariance + PTP_LOG_VARIANCE_THRESHOLD
+ || syncA->grandmasterClockVariance < syncB->grandmasterClockVariance - PTP_LOG_VARIANCE_THRESHOLD) {
+ /* grandmasterClockVariances are not similar */
+ if (syncA->grandmasterClockVariance < syncB->grandmasterClockVariance)
+ goto A;
+ else
+ goto B;
+ }
+ /* grandmasterClockVariances are similar */
+ if (!syncA->grandmasterIsBoundaryClock != !syncB->grandmasterIsBoundaryClock) { /* XOR */
+ if (syncA->grandmasterIsBoundaryClock)
+ goto A;
+ else
+ goto B;
+ }
+ /* neither is grandmasterIsBoundaryClock */
+ if (memcmp(syncA->grandmasterClockUuid, syncB->grandmasterClockUuid, PTP_UUID_LENGTH) < 0)
+ goto A;
+ else
+ goto B;
+ }
+ /* syncA->grandmasterClockStratum <= 2 */
+ if (!syncA->grandmasterPreferred != !syncB->grandmasterPreferred) { /* XOR */
+ if (syncA->grandmasterPreferred)
+ return 1; /* A1 */
+ else
+ return -1; /* B1 */
+ }
+ /* neither or both grandmasterPreferred */
+ }
+ DBGV("bmcDataSetComparison: X\n");
+ if (syncA->localStepsRemoved > syncB->localStepsRemoved + 1
+ || syncA->localStepsRemoved < syncB->localStepsRemoved - 1) {
+ /* localStepsRemoved not within 1 */
+ if (syncA->localStepsRemoved < syncB->localStepsRemoved)
+ return 1; /* A1 */
+ else
+ return -1; /* B1 */
+ }
+ /* localStepsRemoved within 1 */
+ if (syncA->localStepsRemoved < syncB->localStepsRemoved) {
+ DBGV("bmcDataSetComparison: A3\n");
+ if (memcmp(ptpClock->port_uuid_field, headerB->sourceUuid, PTP_UUID_LENGTH) < 0)
+ return 1; /* A1 */
+ else if (memcmp(ptpClock->port_uuid_field, headerB->sourceUuid, PTP_UUID_LENGTH) > 0)
+ return 2; /* A2 */
+
+ /* this port_uuid_field same as headerB->sourceUuid */
+ if (ptpClock->port_id_field < headerB->sourcePortId)
+ return 1; /* A1 */
+ else if (ptpClock->port_id_field > headerB->sourcePortId)
+ return 2; /* A2 */
+
+ /* this port_id_field same as headerB->sourcePortId */
+ return 0; /* same */
+ }
+ if (syncA->localStepsRemoved > syncB->localStepsRemoved) {
+ DBGV("bmcDataSetComparison: B3\n");
+ if (memcmp(ptpClock->port_uuid_field, headerA->sourceUuid, PTP_UUID_LENGTH) < 0)
+ return -1; /* B1 */
+ else if (memcmp(ptpClock->port_uuid_field, headerB->sourceUuid, PTP_UUID_LENGTH) > 0)
+ return -2; /* B2 */
+
+ /* this port_uuid_field same as headerA->sourceUuid */
+ if (ptpClock->port_id_field < headerA->sourcePortId)
+ return -1; /* B1 */
+ else if (ptpClock->port_id_field > headerA->sourcePortId)
+ return -2; /* B2 */
+
+ /* this port_id_field same as headerA->sourcePortId */
+ return 0; /* same */
+ }
+ /* localStepsRemoved same */
+ if (memcmp(headerA->sourceUuid, headerB->sourceUuid, PTP_UUID_LENGTH) < 0)
+ return 2; /* A2 */
+ else if (memcmp(headerA->sourceUuid, headerB->sourceUuid, PTP_UUID_LENGTH) > 0)
+ return -2; /* B2 */
+
+ /* sourceUuid same */
+ DBGV("bmcDataSetComparison: Z\n");
+ if (syncA->grandmasterSequenceId > syncB->grandmasterSequenceId)
+ return 3;
+ else if (syncA->grandmasterSequenceId < syncB->grandmasterSequenceId)
+ return -3;
+
+ /* grandmasterSequenceId same */
+ if (headerA->sequenceId > headerB->sequenceId)
+ return 3;
+ else if (headerA->sequenceId < headerB->sequenceId)
+ return -3;
+
+ /* sequenceId same */
+ return 0; /* same */
+
+ /* oh no, a goto label! the horror! */
+A:
+ if (!syncA->grandmasterPreferred && syncB->grandmasterPreferred)
+ return -1; /* B1 */
+ else
+ return 1; /* A1 */
+B:
+ if (syncA->grandmasterPreferred && !syncB->grandmasterPreferred)
+ return 1; /* A1 */
+ else
+ return -1; /* B1 */
+}
+
+UInteger8
+bmcStateDecision(MsgHeader * header, MsgSync * sync, RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ if (rtOpts->slaveOnly) {
+ s1(header, sync, ptpClock);
+ return PTP_SLAVE;
+ }
+ copyD0(&ptpClock->msgTmpHeader, &ptpClock->msgTmp.sync, ptpClock);
+
+ if (ptpClock->msgTmp.sync.grandmasterClockStratum < 3) {
+ if (bmcDataSetComparison(&ptpClock->msgTmpHeader, &ptpClock->msgTmp.sync, header, sync, ptpClock) > 0) {
+ m1(ptpClock);
+ return PTP_MASTER;
+ }
+ s1(header, sync, ptpClock);
+ return PTP_PASSIVE;
+ } else if (bmcDataSetComparison(&ptpClock->msgTmpHeader, &ptpClock->msgTmp.sync, header, sync, ptpClock) > 0
+ && ptpClock->msgTmp.sync.grandmasterClockStratum != 255) {
+ m1(ptpClock);
+ return PTP_MASTER;
+ } else {
+ s1(header, sync, ptpClock);
+ return PTP_SLAVE;
+ }
+}
+
+UInteger8
+bmc(ForeignMasterRecord * foreign, RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ Integer16 i, best;
+
+ if (!ptpClock->number_foreign_records) {
+ if (ptpClock->port_state == PTP_MASTER)
+ m1(ptpClock);
+ return ptpClock->port_state; /* no change */
+ }
+ for (i = 1, best = 0; i < ptpClock->number_foreign_records; ++i) {
+ if (bmcDataSetComparison(&foreign[i].header, &foreign[i].sync,
+ &foreign[best].header, &foreign[best].sync, ptpClock) > 0)
+ best = i;
+ }
+
+ DBGV("bmc: best record %d\n", best);
+ ptpClock->foreign_record_best = best;
+
+ return bmcStateDecision(&foreign[best].header, &foreign[best].sync, rtOpts, ptpClock);
+}
diff --git a/third_party/ptpd-1.1.0/src/constants.h b/third_party/ptpd-1.1.0/src/constants.h new file mode 100644 index 0000000..503aee8 --- /dev/null +++ b/third_party/ptpd-1.1.0/src/constants.h @@ -0,0 +1,120 @@ +/**
+ * @file constants.h
+ * @date Wed Jun 23 10:02:39 2010
+ *
+ * @brief All of the defined constants for the daemon.
+ *
+ *
+ */
+
+#ifndef CONSTANTS_H
+#define CONSTANTS_H
+
+/* implementation specific constants */
+#define MANUFACTURER_ID \
+ "PTPD;1.2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+
+#define DEFAULT_SYNC_INTERVAL 1
+#define DEFAULT_UTC_OFFSET 0
+#define DEFAULT_CLOCK_VARIANCE (-4000)
+#define DEFAULT_CLOCK_STRATUM 4
+#define DEFAULT_INBOUND_LATENCY 0 /* in nsec */
+#define DEFAULT_OUTBOUND_LATENCY 0 /* in nsec */
+#define DEFAULT_NO_RESET_CLOCK FALSE
+#define DEFAULT_AP 10
+#define DEFAULT_AI 1000
+#define DEFAULT_DELAY_S 6
+#define DEFAULT_MAX_FOREIGN_RECORDS 5
+
+/* features, only change to refelect changes in implementation */
+#define CLOCK_FOLLOWUP TRUE
+#define INITIALIZABLE TRUE
+#define BURST_ENABLED FALSE
+#define EXTERNAL_TIMING FALSE
+#define BOUNDARY_CLOCK FALSE
+#define NUMBER_PORTS 1
+#define VERSION_PTP 1
+#define VERSION_NETWORK 1
+
+/* spec defined constants */
+#define DEFAULT_PTP_DOMAIN_NAME "_DFLT\0\0\0\0\0\0\0\0\0\0\0"
+#define ALTERNATE_PTP_DOMAIN1_NAME "_ALT1\0\0\0\0\0\0\0\0\0\0\0"
+#define ALTERNATE_PTP_DOMAIN2_NAME "_ALT2\0\0\0\0\0\0\0\0\0\0\0"
+#define ALTERNATE_PTP_DOMAIN3_NAME "_ALT3\0\0\0\0\0\0\0\0\0\0\0"
+
+#define IDENTIFIER_ATOM "ATOM"
+#define IDENTIFIER_GPS "GPS\0"
+#define IDENTIFIER_NTP "NTP\0"
+#define IDENTIFIER_HAND "HAND"
+#define IDENTIFIER_INIT "INIT"
+#define IDENTIFIER_DFLT "DFLT"
+
+/* ptp constants */
+#define PTP_UUID_LENGTH 6
+#define PTP_CODE_STRING_LENGTH 4
+#define PTP_SUBDOMAIN_NAME_LENGTH 16
+#define PTP_MAX_MANAGEMENT_PAYLOAD_SIZE 90
+/* no support for intervals less than one */
+#define PTP_SYNC_INTERVAL_TIMEOUT(x) (1<<((x)<0?1:(x)))
+#define PTP_SYNC_RECEIPT_TIMEOUT(x) (10*(1<<((x)<0?0:(x))))
+#define PTP_DELAY_REQ_INTERVAL 30
+#define PTP_FOREIGN_MASTER_THRESHOLD 2
+#define PTP_FOREIGN_MASTER_TIME_WINDOW(x) (4*(1<<((x)<0?0:(x))))
+#define PTP_RANDOMIZING_SLOTS 18
+#define PTP_LOG_VARIANCE_THRESHOLD 256
+#define PTP_LOG_VARIANCE_HYSTERESIS 128
+/* used in spec but not named */
+#define MANUFACTURER_ID_LENGTH 48
+
+/* ptp data enums */
+enum {
+ PTP_CLOSED = 0, PTP_ETHER, PTP_FFBUS = 4,
+ PTP_PROFIBUS, PTP_LON, PTP_DNET,
+ PTP_SDS, PTP_CONTROLNET, PTP_CANOPEN,
+ PTP_IEEE1394 = 243, PTP_IEEE802_11A, PTP_IEEE_WIRELESS,
+ PTP_INFINIBAND, PTP_BLUETOOTH, PTP_IEEE802_15_1,
+ PTP_IEEE1451_2, PTP_IEEE1451_5, PTP_USB,
+ PTP_ISA, PTP_PCI, PTP_VXI, PTP_DEFAULT
+};
+
+enum {
+ PTP_INITIALIZING = 0, PTP_FAULTY, PTP_DISABLED,
+ PTP_LISTENING, PTP_PRE_MASTER, PTP_MASTER,
+ PTP_PASSIVE, PTP_UNCALIBRATED, PTP_SLAVE
+};
+
+enum {
+ PTP_SYNC_MESSAGE = 0, PTP_DELAY_REQ_MESSAGE, PTP_FOLLOWUP_MESSAGE,
+ PTP_DELAY_RESP_MESSAGE, PTP_MANAGEMENT_MESSAGE,
+ PTP_SYNC_MESSAGE_BURST, PTP_DELAY_REQ_MESSAGE_BURST
+};
+
+enum {
+ PTP_LI_61 = 0, PTP_LI_59, PTP_BOUNDARY_CLOCK,
+ PTP_ASSIST, PTP_EXT_SYNC, PARENT_STATS, PTP_SYNC_BURST
+};
+
+enum {
+ PTP_MM_NULL = 0, PTP_MM_OBTAIN_IDENTITY, PTP_MM_CLOCK_IDENTITY,
+ PTP_MM_INITIALIZE_CLOCK, PTP_MM_SET_SUBDOMAIN,
+ PTP_MM_CLEAR_DESIGNATED_PREFERRED_MASTER,
+ PTP_MM_SET_DESIGNATED_PREFERRED_MASTER,
+ PTP_MM_GET_DEFAULT_DATA_SET, PTP_MM_DEFAULT_DATA_SET,
+ PTP_MM_UPDATE_DEFAULT_DATA_SET, PTP_MM_GET_CURRENT_DATA_SET,
+ PTP_MM_CURRENT_DATA_SET, PTP_MM_GET_PARENT_DATA_SET,
+ PTP_MM_PARENT_DATA_SET, PTP_MM_GET_PORT_DATA_SET,
+ PTP_MM_PORT_DATA_SET, PTP_MM_GET_GLOBAL_TIME_DATA_SET,
+ PTP_MM_GLOBAL_TIME_DATA_SET, PTP_MM_UPDATE_GLOBAL_TIME_PROPERTIES,
+ PTP_MM_GOTO_FAULTY_STATE, PTP_MM_GET_FOREIGN_DATA_SET,
+ PTP_MM_FOREIGN_DATA_SET, PTP_MM_SET_SYNC_INTERVAL,
+ PTP_MM_DISABLE_PORT, PTP_MM_ENABLE_PORT,
+ PTP_MM_DISABLE_BURST, PTP_MM_ENABLE_BURST, PTP_MM_SET_TIME
+};
+
+/* enum used by this implementation */
+enum {
+ SYNC_RECEIPT_TIMER = 0, SYNC_INTERVAL_TIMER, QUALIFICATION_TIMER,
+ TIMER_ARRAY_SIZE /* these two are non-spec */
+};
+
+#endif
diff --git a/third_party/ptpd-1.1.0/src/datatypes.h b/third_party/ptpd-1.1.0/src/datatypes.h new file mode 100644 index 0000000..1f0e21e --- /dev/null +++ b/third_party/ptpd-1.1.0/src/datatypes.h @@ -0,0 +1,362 @@ +/**
+ * @file datatypes.h
+ * @date Wed Jun 23 09:37:14 2010
+ *
+ * @brief All of the datatypes used by the daemon.
+ *
+ *
+ */
+
+
+#ifndef DATATYPES_H
+#define DATATYPES_H
+
+#include <stdio.h>
+
+typedef struct {
+ UInteger32 seconds;
+ Integer32 nanoseconds;
+} TimeRepresentation;
+
+typedef struct {
+ Integer32 seconds;
+ Integer32 nanoseconds;
+} TimeInternal;
+
+typedef struct {
+ Integer32 interval;
+ Integer32 left;
+ Boolean expire;
+} IntervalTimer;
+
+/* Message header */
+typedef struct {
+ UInteger16 versionPTP;
+ UInteger16 versionNetwork;
+ Octet subdomain[PTP_SUBDOMAIN_NAME_LENGTH];
+ UInteger8 messageType;
+ UInteger8 sourceCommunicationTechnology;
+ Octet sourceUuid[PTP_UUID_LENGTH];
+ UInteger16 sourcePortId;
+ UInteger16 sequenceId;
+ UInteger8 control;
+ Octet flags[2];
+
+} MsgHeader;
+
+/* Sync or Delay_Req message */
+typedef struct {
+ TimeRepresentation originTimestamp;
+ UInteger16 epochNumber;
+ Integer16 currentUTCOffset;
+ UInteger8 grandmasterCommunicationTechnology;
+ Octet grandmasterClockUuid[PTP_UUID_LENGTH];
+ UInteger16 grandmasterPortId;
+ UInteger16 grandmasterSequenceId;
+ UInteger8 grandmasterClockStratum;
+ Octet grandmasterClockIdentifier[PTP_CODE_STRING_LENGTH];
+ Integer16 grandmasterClockVariance;
+ Boolean grandmasterPreferred;
+ Boolean grandmasterIsBoundaryClock;
+ Integer8 syncInterval;
+ Integer16 localClockVariance;
+ UInteger16 localStepsRemoved;
+ UInteger8 localClockStratum;
+ Octet localClockIdentifer[PTP_CODE_STRING_LENGTH];
+ UInteger8 parentCommunicationTechnology;
+ Octet parentUuid[PTP_UUID_LENGTH];
+ UInteger16 parentPortField;
+ Integer16 estimatedMasterVariance;
+ Integer32 estimatedMasterDrift;
+ Boolean utcReasonable;
+
+} MsgSync;
+
+typedef MsgSync MsgDelayReq;
+
+/* Follow_Up message */
+typedef struct {
+ UInteger16 associatedSequenceId;
+ TimeRepresentation preciseOriginTimestamp;
+
+} MsgFollowUp;
+
+/* Delay_Resp message */
+typedef struct {
+ TimeRepresentation delayReceiptTimestamp;
+ UInteger8 requestingSourceCommunicationTechnology;
+ Octet requestingSourceUuid[PTP_UUID_LENGTH];
+ UInteger16 requestingSourcePortId;
+ UInteger16 requestingSourceSequenceId;
+
+} MsgDelayResp;
+
+/* Management message */
+typedef union {
+ struct ClockIdentity {
+ UInteger8 clockCommunicationTechnology;
+ Octet clockUuidField[PTP_UUID_LENGTH];
+ UInteger16 clockPortField;
+ Octet manufacturerIdentity[MANUFACTURER_ID_LENGTH];
+ } clockIdentity;
+
+ struct DefaultData {
+ UInteger8 clockCommunicationTechnology;
+ Octet clockUuidField[PTP_UUID_LENGTH];
+ UInteger16 clockPortField;
+ UInteger8 clockStratum;
+ Octet clockIdentifier[PTP_CODE_STRING_LENGTH];
+ Integer16 clockVariance;
+ Boolean clockFollowupCapable;
+ Boolean preferred;
+ Boolean initializable;
+ Boolean externalTiming;
+ Boolean isBoundaryClock;
+ Integer8 syncInterval;
+ Octet subdomainName[PTP_SUBDOMAIN_NAME_LENGTH];
+ UInteger16 numberPorts;
+ UInteger16 numberForeignRecords;
+ } defaultData;
+
+ struct Current {
+ UInteger16 stepsRemoved;
+ TimeRepresentation offsetFromMaster;
+ TimeRepresentation oneWayDelay;
+ } current;
+
+ struct Parent {
+ UInteger8 parentCommunicationTechnology;
+ Octet parentUuid[PTP_UUID_LENGTH];
+ UInteger16 parentPortId;
+ UInteger16 parentLastSyncSequenceNumber;
+ Boolean parentFollowupCapable;
+ Boolean parentExternalTiming;
+ Integer16 parentVariance;
+ Boolean parentStats;
+ Integer16 observedVariance;
+ Integer32 observedDrift;
+ Boolean utcReasonable;
+ UInteger8 grandmasterCommunicationTechnology;
+ Octet grandmasterUuidField[PTP_UUID_LENGTH];
+ UInteger16 grandmasterPortIdField;
+ UInteger8 grandmasterStratum;
+ Octet grandmasterIdentifier[PTP_CODE_STRING_LENGTH];
+ Integer16 grandmasterVariance;
+ Boolean grandmasterPreferred;
+ Boolean grandmasterIsBoundaryClock;
+ UInteger16 grandmasterSequenceNumber;
+ } parent;
+
+ struct Port {
+ UInteger16 returnedPortNumber;
+ UInteger8 portState;
+ UInteger16 lastSyncEventSequenceNumber;
+ UInteger16 lastGeneralEventSequenceNumber;
+ UInteger8 portCommunicationTechnology;
+ Octet portUuidField[PTP_UUID_LENGTH];
+ UInteger16 portIdField;
+ Boolean burstEnabled;
+ UInteger8 subdomainAddressOctets;
+ UInteger8 eventPortAddressOctets;
+ UInteger8 generalPortAddressOctets;
+ Octet subdomainAddress[SUBDOMAIN_ADDRESS_LENGTH];
+ Octet eventPortAddress[PORT_ADDRESS_LENGTH];
+ Octet generalPortAddress[PORT_ADDRESS_LENGTH];
+ } port;
+
+ struct GlobalTime {
+ TimeRepresentation localTime;
+ Integer16 currentUtcOffset;
+ Boolean leap59;
+ Boolean leap61;
+ UInteger16 epochNumber;
+ } globalTime;
+
+ struct Foreign {
+ UInteger16 returnedPortNumber;
+ UInteger16 returnedRecordNumber;
+ UInteger8 foreignMasterCommunicationTechnology;
+ Octet foreignMasterUuid[PTP_UUID_LENGTH];
+ UInteger16 foreignMasterPortId;
+ UInteger16 foreignMasterSyncs;
+ } foreign;
+
+} MsgManagementPayload;
+
+typedef struct {
+ UInteger8 targetCommunicationTechnology;
+ Octet targetUuid[PTP_UUID_LENGTH];
+ UInteger16 targetPortId;
+ Integer16 startingBoundaryHops;
+ Integer16 boundaryHops;
+ UInteger8 managementMessageKey;
+ UInteger16 parameterLength;
+ UInteger16 recordKey;
+
+ MsgManagementPayload payload;
+} MsgManagement;
+
+typedef struct {
+ UInteger8 foreign_master_communication_technology;
+ Octet foreign_master_uuid[PTP_UUID_LENGTH];
+ UInteger16 foreign_master_port_id;
+ UInteger16 foreign_master_syncs;
+
+ MsgHeader header;
+ MsgSync sync;
+} ForeignMasterRecord;
+
+/* main program data structure */
+typedef struct {
+ /* Default data set */
+ UInteger8 clock_communication_technology;
+ Octet clock_uuid_field[PTP_UUID_LENGTH];
+ UInteger16 clock_port_id_field;
+ UInteger8 clock_stratum;
+ Octet clock_identifier[PTP_CODE_STRING_LENGTH];
+ Integer16 clock_variance;
+ Boolean clock_followup_capable;
+ Boolean preferred;
+ Boolean initializable;
+ Boolean external_timing;
+ Boolean is_boundary_clock;
+ Integer8 sync_interval;
+ Octet subdomain_name[PTP_SUBDOMAIN_NAME_LENGTH];
+ UInteger16 number_ports;
+ UInteger16 number_foreign_records;
+
+ /* Current data set */
+ UInteger16 steps_removed;
+ TimeInternal offset_from_master;
+ TimeInternal one_way_delay;
+
+ /* Parent data set */
+ UInteger8 parent_communication_technology;
+ Octet parent_uuid[PTP_UUID_LENGTH];
+ UInteger16 parent_port_id;
+ UInteger16 parent_last_sync_sequence_number;
+ Boolean parent_followup_capable;
+ Boolean parent_external_timing;
+ Integer16 parent_variance;
+ Boolean parent_stats;
+ Integer16 observed_variance;
+ Integer32 observed_drift;
+ Boolean utc_reasonable;
+ UInteger8 grandmaster_communication_technology;
+ Octet grandmaster_uuid_field[PTP_UUID_LENGTH];
+ UInteger16 grandmaster_port_id_field;
+ UInteger8 grandmaster_stratum;
+ Octet grandmaster_identifier[PTP_CODE_STRING_LENGTH];
+ Integer16 grandmaster_variance;
+ Boolean grandmaster_preferred;
+ Boolean grandmaster_is_boundary_clock;
+ UInteger16 grandmaster_sequence_number;
+
+ /* Global time properties data set */
+ Integer16 current_utc_offset;
+ Boolean leap_59;
+ Boolean leap_61;
+ UInteger16 epoch_number;
+
+ /* Port configuration data set */
+ UInteger8 port_state;
+ UInteger16 last_sync_event_sequence_number;
+ UInteger16 last_general_event_sequence_number;
+ Octet subdomain_address[SUBDOMAIN_ADDRESS_LENGTH];
+ Octet event_port_address[PORT_ADDRESS_LENGTH];
+ Octet general_port_address[PORT_ADDRESS_LENGTH];
+ UInteger8 port_communication_technology;
+ Octet port_uuid_field[PTP_UUID_LENGTH];
+ UInteger16 port_id_field;
+ Boolean burst_enabled;
+
+ /* Foreign master data set */
+ ForeignMasterRecord *foreign;
+
+ /* Other things we need for the protocol */
+ Boolean halfEpoch;
+
+ Integer16 max_foreign_records;
+ Integer16 foreign_record_i;
+ Integer16 foreign_record_best;
+ Boolean record_update;
+ UInteger32 random_seed;
+
+ MsgHeader msgTmpHeader;
+
+ union {
+ MsgSync sync;
+ MsgFollowUp follow;
+ MsgDelayReq req;
+ MsgDelayResp resp;
+ MsgManagement manage;
+ } msgTmp;
+
+ Octet msgObuf[PACKET_SIZE];
+ Octet msgIbuf[PACKET_SIZE];
+
+ TimeInternal master_to_slave_delay;
+ TimeInternal slave_to_master_delay;
+
+ TimeInternal delay_req_receive_time;
+ TimeInternal delay_req_send_time;
+ TimeInternal sync_receive_time;
+
+ UInteger16 Q;
+ UInteger16 R;
+
+ Boolean sentDelayReq;
+ UInteger16 sentDelayReqSequenceId;
+ Boolean waitingForFollow;
+
+ offset_from_master_filter ofm_filt;
+ one_way_delay_filter owd_filt;
+
+ Boolean message_activity;
+
+ 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;
+ Integer32 maxReset; /* Maximum number of nanoseconds to reset */
+ Integer32 maxDelay; /* Maximum number of nanoseconds of delay */
+ 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;
+#ifndef NO_FILE_SYSTEM
+ char file[PATH_MAX];
+#endif
+ int logFd;
+ Boolean useSysLog;
+ int ttl;
+#ifndef NO_FILE_SYSTEM
+ char recordFile[PATH_MAX];
+ FILE *recordFP;
+#endif
+} RunTimeOpts;
+
+#endif
diff --git a/third_party/ptpd-1.1.0/src/dep-tiva/constants_dep.h b/third_party/ptpd-1.1.0/src/dep-tiva/constants_dep.h new file mode 100644 index 0000000..952bddd --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep-tiva/constants_dep.h @@ -0,0 +1,87 @@ +/* constants_dep.h */
+
+#ifndef CONSTANTS_DEP_H
+#define CONSTANTS_DEP_H
+
+/* platform dependent */
+
+#ifdef IF_NAMESIZE
+ #define IFACE_NAME_LENGTH IF_NAMESIZE
+#else
+ #define IFACE_NAME_LENGTH 16
+#endif
+
+#ifdef INET_ADDRSTRLEN
+ #define NET_ADDRESS_LENGTH INET_ADDRSTRLEN
+#else
+ #define NET_ADDRESS_LENGTH 16
+#endif
+
+#define IFCONF_LENGTH 10
+
+#define PTPD_LSBF
+
+#define ADJ_MAX 10000000
+
+/* UDP/IPv4 dependent */
+
+#define SUBDOMAIN_ADDRESS_LENGTH 4
+#define PORT_ADDRESS_LENGTH 2
+
+#define PACKET_SIZE 300
+
+#define PTP_EVENT_PORT 319
+#define PTP_GENERAL_PORT 320
+
+#define DEFAULT_PTP_DOMAIN_ADDRESS "224.0.1.129"
+#define ALTERNATE_PTP_DOMAIN1_ADDRESS "224.0.1.130"
+#define ALTERNATE_PTP_DOMAIN2_ADDRESS "224.0.1.131"
+#define ALTERNATE_PTP_DOMAIN3_ADDRESS "224.0.1.132"
+
+#define HEADER_LENGTH 40
+#define SYNC_PACKET_LENGTH 124
+#define DELAY_REQ_PACKET_LENGTH 124
+#define FOLLOW_UP_PACKET_LENGTH 52
+#define DELAY_RESP_PACKET_LENGTH 60
+#define MANAGEMENT_PACKET_LENGTH 136
+
+#define MM_STARTING_BOUNDARY_HOPS 0x7fff
+
+/* others */
+
+#define SCREEN_BUFSZ 128
+#define SCREEN_MAXSZ 80
+
+#define PBUF_QUEUE_SIZE 16
+
+/* override default values */
+#ifdef DEFAULT_AP
+# undef DEFAULT_AP
+# define DEFAULT_AP 2
+#endif
+
+#ifdef DEFAULT_AI
+# undef DEFAULT_AI
+# define DEFAULT_AI 200
+#endif
+
+#define MAX_AP 10
+#define MAX_AI 1000
+
+#ifdef DEFAULT_INBOUND_LATENCY
+# undef DEFAULT_INBOUND_LATENCY
+# define DEFAULT_INBOUND_LATENCY 16500
+#endif
+
+#ifdef DEFAULT_OUTBOUND_LATENCY
+# undef DEFAULT_OUTBOUND_LATENCY
+# define DEFAULT_OUTBOUND_LATENCY 16500
+#endif
+
+#ifdef CLOCK_FOLLOWUP
+# undef CLOCK_FOLLOWUP
+# define CLOCK_FOLLOWUP FALSE
+#endif
+
+#endif /* #ifndef CONSTANTS_DEP_H */
+
diff --git a/third_party/ptpd-1.1.0/src/dep-tiva/datatypes_dep.h b/third_party/ptpd-1.1.0/src/dep-tiva/datatypes_dep.h new file mode 100644 index 0000000..af9f67d --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep-tiva/datatypes_dep.h @@ -0,0 +1,42 @@ +/* datatypes_dep.h */
+
+#ifndef DATATYPES_DEP_H
+#define DATATYPES_DEP_H
+
+typedef enum {FALSE=0, TRUE} Boolean;
+typedef char Octet;
+typedef signed char Integer8;
+typedef signed short Integer16;
+typedef signed int Integer32;
+typedef unsigned char UInteger8;
+typedef unsigned short UInteger16;
+typedef unsigned int UInteger32;
+
+typedef struct {
+ Integer32 nsec_prev, y;
+} offset_from_master_filter;
+
+typedef struct {
+ Integer32 nsec_prev, y;
+ Integer32 s_exp;
+} one_way_delay_filter;
+
+typedef struct {
+ void *pbuf[PBUF_QUEUE_SIZE];
+ Integer32 get;
+ Integer32 put;
+ Integer32 count;
+} BufQueue;
+
+typedef struct {
+ Integer32 multicastAddr;
+ Integer32 unicastAddr;
+ void *eventPcb;
+ void *generalPcb;
+ BufQueue eventQ;
+ BufQueue generalQ;
+ void *eventTxBuf;
+ void *generalTxBuf;
+} NetPath;
+
+#endif
diff --git a/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_dep.h b/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_dep.h new file mode 100644 index 0000000..10b297e --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_dep.h @@ -0,0 +1,138 @@ +/* ptpd_dep.h */
+
+#ifndef PTPD_DEP_H
+#define PTPD_DEP_H
+
+#include <string.h>
+
+/*
+ * Remove the following #if 0 and its matching #endif to enable PTPd debug
+ * output on UART0. This is disabled by default even in debug builds due to
+ * the volume of information transmitted.
+ */
+#if 0
+#ifdef DEBUG
+#include "utils/uartstdio.h"
+#define PTPD_DBG
+/*
+ * Define the following to enable verbose debug.
+ */
+#undef PTPD_DBGV
+#endif
+#endif
+
+#ifndef INT_MAX
+#define INT_MAX 2147483647
+#endif
+#ifndef size_t
+#define size_t long
+#endif
+#ifndef ssize_t
+#define ssize_t long
+#endif
+
+/* system messages */
+#ifndef PTPD_DBG
+#define ERROR(...)
+#define PERROR(...)
+#define NOTIFY(...)
+#else
+#define ERROR(x, ...) UARTprintf("(ptpd error) " x, ##__VA_ARGS__)
+#define PERROR(x, ...) UARTprintf("(ptpd error) " x ": %m\n", ##__VA_ARGS__)
+#define NOTIFY(x, ...) UARTprintf("(ptpd notice) " x, ##__VA_ARGS__)
+#endif
+
+/* debug messages */
+#ifdef PTPD_DBGV
+#define PTPD_DBG
+#define DBGV(x, ...) UARTprintf("(ptpd debug) " x, ##__VA_ARGS__)
+#else
+#define DBGV(...)
+#endif
+
+#ifdef PTPD_DBG
+#define DBG(x, ...) UARTprintf("(ptpd debug) " x, ##__VA_ARGS__)
+#else
+#define DBG(...)
+#endif
+
+/* endian corrections */
+#if defined(PTPD_MSBF)
+#define shift8(x,y) ( (x) << ((3-y)<<3) )
+#define shift16(x,y) ( (x) << ((1-y)<<4) )
+#define flip16(x) (x)
+#define flip32(x) (x)
+
+#elif defined(PTPD_LSBF)
+#define shift8(x,y) ( (x) << ((y)<<3) )
+#define shift16(x,y) ( (x) << ((y)<<4) )
+#define flip16(x) ((((x) >> 8) & 0x00ff) | (((x) << 8) & 0xff00))
+#define flip32(x) ((((x) >> 24) & 0x000000ff) | \
+ (((x) >> 8 ) & 0x0000ff00) | \
+ (((x) << 8 ) & 0x00ff0000) | \
+ (((x) << 24) & 0xff000000))
+#endif
+
+
+/* bit array manipulation */
+#define getFlag(x,y) !!( *(UInteger8*)((x)+((y)<8?1:0)) & (1<<((y)<8?(y):(y)-8)) )
+#define setFlag(x,y) ( *(UInteger8*)((x)+((y)<8?1:0)) |= 1<<((y)<8?(y):(y)-8) )
+#define clearFlag(x,y) ( *(UInteger8*)((x)+((y)<8?1:0)) &= ~(1<<((y)<8?(y):(y)-8)) )
+
+
+#define labs(x) (((x) >= 0) ? (x) : -(x))
+
+/* msg.c */
+Boolean msgPeek(char*,size_t);
+void msgUnpackHeader(char*,MsgHeader*);
+void msgUnpackSync(char*,MsgSync*);
+void msgUnpackDelayReq(char*,MsgDelayReq*);
+void msgUnpackFollowUp(char*,MsgFollowUp*);
+void msgUnpackDelayResp(char*,MsgDelayResp*);
+void msgUnpackManagement(char*,MsgManagement*);
+UInteger8 msgUnloadManagement(char*,MsgManagement*,PtpClock*,RunTimeOpts*);
+void msgUnpackManagementPayload(char *buf, MsgManagement *manage);
+void msgPackHeader(char*,PtpClock*);
+void msgPackSync(char*,Boolean,TimeRepresentation*,PtpClock*);
+void msgPackDelayReq(char*,Boolean,TimeRepresentation*,PtpClock*);
+void msgPackFollowUp(char*,UInteger16,TimeRepresentation*,PtpClock*);
+void msgPackDelayResp(char*,MsgHeader*,TimeRepresentation*,PtpClock*);
+UInteger16 msgPackManagement(char*,MsgManagement*,PtpClock*);
+UInteger16 msgPackManagementResponse(char*,MsgHeader*,MsgManagement*,PtpClock*);
+
+/* net.c */
+Boolean netInit(NetPath*,RunTimeOpts*,PtpClock*);
+Boolean netShutdown(NetPath*);
+int netSelect(TimeInternal*,NetPath*);
+size_t netRecvEvent(Octet*,TimeInternal*,NetPath*);
+size_t netRecvGeneral(Octet*,NetPath*);
+size_t netSendEvent(Octet*,UInteger16,NetPath*);
+size_t netSendGeneral(Octet*,UInteger16,NetPath*);
+
+/* servo.c */
+void initClock(RunTimeOpts*,PtpClock*);
+void updateDelay(TimeInternal*,TimeInternal*,
+ one_way_delay_filter*,RunTimeOpts*,PtpClock*);
+void updateOffset(TimeInternal*,TimeInternal*,
+ offset_from_master_filter*,RunTimeOpts*,PtpClock*);
+void updateClock(RunTimeOpts*,PtpClock*);
+
+/* sys.c */
+void displayStats(RunTimeOpts*,PtpClock*);
+Boolean nanoSleep(TimeInternal*);
+void getTime(TimeInternal*);
+void setTime(TimeInternal*);
+UInteger16 getRand(UInteger32*);
+Boolean adjFreq(Integer32);
+
+/* timer.c */
+void initTimer(void);
+void timerTick(int);
+void timerUpdate(IntervalTimer*);
+void timerStop(UInteger16,IntervalTimer*);
+void timerStart(UInteger16,UInteger16,IntervalTimer*);
+Boolean timerExpired(UInteger16,IntervalTimer*);
+
+
+#endif
+
diff --git a/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_msg.c b/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_msg.c new file mode 100644 index 0000000..6827ec0 --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_msg.c @@ -0,0 +1,655 @@ +/* ptpd_msg.c */
+/* see spec annex d */
+
+#include "../ptpd.h"
+
+Boolean msgPeek(char *buf, size_t length)
+{
+ /* not imlpemented yet */
+ return TRUE;
+}
+
+void msgUnpackHeader(char *buf, MsgHeader *header)
+{
+ header->versionPTP = flip16(*(UInteger16*)(buf + 0));
+ header->versionNetwork = flip16(*(UInteger16*)(buf + 2));
+ DBGV("msgUnpackHeader: versionPTP %d\n", header->versionPTP);
+ DBGV("msgUnpackHeader: versionNetwork %d\n", header->versionNetwork);
+
+ memcpy(header->subdomain, (buf + 4), 16);
+ DBGV("msgUnpackHeader: subdomain %s\n", header->subdomain);
+
+ header->messageType = *(UInteger8*)(buf + 20);
+ header->sourceCommunicationTechnology = *(UInteger8*)(buf + 21);
+ DBGV("msgUnpackHeader: messageType %d\n", header->messageType);
+ DBGV("msgUnpackHeader: sourceCommunicationTechnology %d\n", header->sourceCommunicationTechnology);
+
+ memcpy(header->sourceUuid, (buf + 22), 6);
+ DBGV("msgUnpackHeader: sourceUuid %02x:%02x:%02x:%02x:%02x:%02x\n",
+ header->sourceUuid[0], header->sourceUuid[1], header->sourceUuid[2],
+ header->sourceUuid[3], header->sourceUuid[4], header->sourceUuid[5]);
+
+ header->sourcePortId = flip16(*(UInteger16*)(buf + 28));
+ header->sequenceId = flip16(*(UInteger16*)(buf + 30));
+ DBGV("msgUnpackHeader: sourcePortId %d\n", header->sourcePortId);
+ DBGV("msgUnpackHeader: sequenceId %d\n", header->sequenceId);
+
+ header->control = *(UInteger8*)(buf + 32);
+ DBGV("msgUnpackHeader: control %d\n", header->control);
+
+ memcpy(header->flags, (buf + 34), 2);
+ DBGV("msgUnpackHeader: flags %02x %02x\n", header->flags[0], header->flags[1]);
+}
+
+void msgUnpackSync(char *buf, MsgSync *sync)
+{
+ sync->originTimestamp.seconds = flip32(*(UInteger32*)(buf + 40));
+ DBG("msgUnpackSync: originTimestamp.seconds %u\n", sync->originTimestamp.seconds);
+ sync->originTimestamp.nanoseconds = flip32(*(Integer32*)(buf + 44));
+ DBG("msgUnpackSync: originTimestamp.nanoseconds %d\n", sync->originTimestamp.nanoseconds);
+ sync->epochNumber = flip16(*(UInteger16*)(buf + 48));
+ DBGV("msgUnpackSync: epochNumber %d\n", sync->epochNumber);
+ sync->currentUTCOffset = flip16(*(Integer16*)(buf + 50));
+ DBGV("msgUnpackSync: currentUTCOffset %d\n", sync->currentUTCOffset);
+ sync->grandmasterCommunicationTechnology = *(UInteger8*)(buf + 53);
+ DBGV("msgUnpackSync: grandmasterCommunicationTechnology %d\n", sync->grandmasterCommunicationTechnology);
+ memcpy(sync->grandmasterClockUuid, (buf + 54), 6);
+ DBGV("msgUnpackSync: grandmasterClockUuid %02x:%02x:%02x:%02x:%02x:%02x\n",
+ sync->grandmasterClockUuid[0], sync->grandmasterClockUuid[1], sync->grandmasterClockUuid[2],
+ sync->grandmasterClockUuid[3], sync->grandmasterClockUuid[4], sync->grandmasterClockUuid[5]);
+ sync->grandmasterPortId = flip16(*(UInteger16*)(buf + 60));
+ DBGV("msgUnpackSync: grandmasterPortId %d\n", sync->grandmasterPortId);
+ sync->grandmasterSequenceId = flip16(*(UInteger16*)(buf + 62));
+ DBGV("msgUnpackSync: grandmasterSequenceId %d\n", sync->grandmasterSequenceId);
+ sync->grandmasterClockStratum = *(UInteger8*)(buf + 67);
+ DBGV("msgUnpackSync: grandmasterClockStratum %d\n", sync->grandmasterClockStratum);
+ memcpy(sync->grandmasterClockIdentifier, (buf + 68), 4);
+ DBGV("msgUnpackSync: grandmasterClockIdentifier %c%c%c%c\n",
+ sync->grandmasterClockIdentifier[0], sync->grandmasterClockIdentifier[1],
+ sync->grandmasterClockIdentifier[2], sync->grandmasterClockIdentifier[3]);
+ sync->grandmasterClockVariance = flip16(*(Integer16*)(buf + 74));
+ DBGV("msgUnpackSync: grandmasterClockVariance %d\n", sync->grandmasterClockVariance);
+ sync->grandmasterPreferred = *(UInteger8*)(buf + 77);
+ DBGV("msgUnpackSync: grandmasterPreferred %d\n", sync->grandmasterPreferred);
+ sync->grandmasterIsBoundaryClock = *(UInteger8*)(buf + 79);
+ DBGV("msgUnpackSync: grandmasterIsBoundaryClock %d\n", sync->grandmasterIsBoundaryClock);
+ sync->syncInterval = *(Integer8*)(buf + 83);
+ DBGV("msgUnpackSync: syncInterval %d\n", sync->syncInterval);
+ sync->localClockVariance = flip16(*(Integer16*)(buf + 86));
+ DBGV("msgUnpackSync: localClockVariance %d\n", sync->localClockVariance);
+ sync->localStepsRemoved = flip16(*(UInteger16*)(buf + 90));
+ DBGV("msgUnpackSync: localStepsRemoved %d\n", sync->localStepsRemoved);
+ sync->localClockStratum = *(UInteger8*)(buf + 95);
+ DBGV("msgUnpackSync: localClockStratum %d\n", sync->localClockStratum);
+ memcpy(sync->localClockIdentifer, (buf + 96), PTP_CODE_STRING_LENGTH);
+ DBGV("msgUnpackSync: localClockIdentifer %c%c%c%c\n",
+ sync->localClockIdentifer[0], sync->localClockIdentifer[1],
+ sync->localClockIdentifer[2], sync->localClockIdentifer[3]);
+ sync->parentCommunicationTechnology = *(UInteger8*)(buf + 101);
+ DBGV("msgUnpackSync: parentCommunicationTechnology %d\n", sync->parentCommunicationTechnology);
+ memcpy(sync->parentUuid, (buf + 102), PTP_UUID_LENGTH);
+ DBGV("msgUnpackSync: parentUuid %02x:%02x:%02x:%02x:%02x:%02x\n",
+ sync->parentUuid[0], sync->parentUuid[1], sync->parentUuid[2],
+ sync->parentUuid[3], sync->parentUuid[4], sync->parentUuid[5]);
+ sync->parentPortField = flip16(*(UInteger16*)(buf + 110));
+ DBGV("msgUnpackSync: parentPortField %d\n", sync->parentPortField);
+ sync->estimatedMasterVariance = flip16(*(Integer16*)(buf + 114));
+ DBGV("msgUnpackSync: estimatedMasterVariance %d\n", sync->estimatedMasterVariance);
+ sync->estimatedMasterDrift = flip32(*(Integer32*)(buf + 116));
+ DBGV("msgUnpackSync: estimatedMasterDrift %d\n", sync->estimatedMasterDrift);
+ sync->utcReasonable = *(UInteger8*)(buf + 123);
+ DBGV("msgUnpackSync: utcReasonable %d\n", sync->utcReasonable);
+}
+
+void msgUnpackDelayReq(char *buf, MsgDelayReq *req) {}
+
+void msgUnpackFollowUp(char *buf, MsgFollowUp *follow)
+{
+ follow->associatedSequenceId = flip16(*(UInteger16*)(buf + 42));
+ DBGV("msgUnpackFollowUp: associatedSequenceId %u\n", follow->associatedSequenceId);
+ follow->preciseOriginTimestamp.seconds = flip32(*(UInteger32*)(buf + 44));
+ DBG("msgUnpackFollowUp: preciseOriginTimestamp.seconds %u\n", follow->preciseOriginTimestamp.seconds);
+ follow->preciseOriginTimestamp.nanoseconds = flip32(*(Integer32*)(buf + 48));
+ DBG("msgUnpackFollowUp: preciseOriginTimestamp.nanoseconds %d\n", follow->preciseOriginTimestamp.nanoseconds);
+}
+
+void msgUnpackDelayResp(char *buf, MsgDelayResp *resp)
+{
+ resp->delayReceiptTimestamp.seconds = flip32(*(UInteger32*)(buf + 40));
+ DBGV("msgUnpackDelayResp: delayReceiptTimestamp.seconds %u\n", resp->delayReceiptTimestamp.seconds);
+ resp->delayReceiptTimestamp.nanoseconds = flip32(*(Integer32*)(buf + 44));
+ DBGV("msgUnpackDelayResp: delayReceiptTimestamp.nanoseconds %d\n", resp->delayReceiptTimestamp.nanoseconds);
+ resp->requestingSourceCommunicationTechnology = *(UInteger8*)(buf + 49);
+ DBGV("msgUnpackDelayResp: requestingSourceCommunicationTechnology %d\n", resp->requestingSourceCommunicationTechnology);
+ memcpy(resp->requestingSourceUuid, (buf + 50), 6);
+ DBGV("msgUnpackDelayResp: requestingSourceUuid %02x:%02x:%02x:%02x:%02x:%02x\n",
+ resp->requestingSourceUuid[0], resp->requestingSourceUuid[1], resp->requestingSourceUuid[2],
+ resp->requestingSourceUuid[3], resp->requestingSourceUuid[4], resp->requestingSourceUuid[5]);
+ resp->requestingSourcePortId = flip16(*(UInteger16*)(buf + 56));
+ DBGV("msgUnpackDelayResp: requestingSourcePortId %d\n", resp->requestingSourcePortId);
+ resp->requestingSourceSequenceId = flip16(*(UInteger16*)(buf + 58));
+ DBGV("msgUnpackDelayResp: requestingSourceSequenceId %d\n", resp->requestingSourceSequenceId);
+}
+
+void msgUnpackManagement(char *buf, MsgManagement *manage)
+{
+ manage->targetCommunicationTechnology = *(UInteger8*)(buf + 41);
+ DBGV("msgUnpackManagement: targetCommunicationTechnology %d\n", manage->targetCommunicationTechnology);
+ memcpy(manage->targetUuid, (buf + 42), 6);
+ DBGV("msgUnpackManagement: targetUuid %02x:%02x:%02x:%02x:%02x:%02x\n",
+ manage->targetUuid[0], manage->targetUuid[1], manage->targetUuid[2],
+ manage->targetUuid[3], manage->targetUuid[4], manage->targetUuid[5]);
+ manage->targetPortId = flip16(*(UInteger16*)(buf + 48));
+ DBGV("msgUnpackManagement: targetPortId %d\n", manage->targetPortId);
+ manage->startingBoundaryHops = flip16(*(Integer16*)(buf + 50));
+ DBGV("msgUnpackManagement: startingBoundaryHops %d\n", manage->startingBoundaryHops);
+ manage->boundaryHops = flip16(*(Integer16*)(buf + 52));
+ DBGV("msgUnpackManagement: boundaryHops %d\n", manage->boundaryHops);
+ manage->managementMessageKey = *(UInteger8*)(buf + 55);
+ DBGV("msgUnpackManagement: managementMessageKey %d\n", manage->managementMessageKey);
+ manage->parameterLength = flip16(*(UInteger16*)(buf + 58));
+ DBGV("msgUnpackManagement: parameterLength %d\n", manage->parameterLength);
+
+ if(manage->managementMessageKey == PTP_MM_GET_FOREIGN_DATA_SET)
+ manage->recordKey = flip16(*(UInteger16*)(buf + 62));
+}
+
+UInteger8 msgUnloadManagement(char *buf, MsgManagement *manage,
+ PtpClock *ptpClock, RunTimeOpts *rtOpts)
+{
+ TimeInternal internalTime;
+ TimeRepresentation externalTime;
+
+ switch(manage->managementMessageKey)
+ {
+ case PTP_MM_INITIALIZE_CLOCK:
+ if(ptpClock->initializable)
+ return PTP_INITIALIZING;
+ break;
+
+ case PTP_MM_GOTO_FAULTY_STATE:
+ DBG("event FAULT_DETECTED (forced by management message)\n");
+ return PTP_FAULTY;
+ break;
+
+ case PTP_MM_DISABLE_PORT:
+ if(manage->targetPortId == 1)
+ {
+ DBG("event DESIGNATED_DISABLED\n");
+ return PTP_DISABLED;
+ }
+ break;
+
+ case PTP_MM_ENABLE_PORT:
+ if(manage->targetPortId == 1)
+ {
+ DBG("event DESIGNATED_ENABLED\n");
+ return PTP_INITIALIZING;
+ }
+ break;
+
+ case PTP_MM_CLEAR_DESIGNATED_PREFERRED_MASTER:
+ ptpClock->preferred = FALSE;
+ break;
+
+ case PTP_MM_SET_DESIGNATED_PREFERRED_MASTER:
+ ptpClock->preferred = TRUE;
+ break;
+
+ case PTP_MM_DISABLE_BURST:
+ break;
+
+ case PTP_MM_ENABLE_BURST:
+ break;
+
+ case PTP_MM_SET_SYNC_INTERVAL:
+ rtOpts->syncInterval = *(Integer8*)(buf + 63);
+ break;
+
+ case PTP_MM_SET_SUBDOMAIN:
+ memcpy(rtOpts->subdomainName, buf + 60, 16);
+ DBG("set subdomain to %s\n", rtOpts->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);
+ break;
+
+ case PTP_MM_UPDATE_DEFAULT_DATA_SET:
+ if(!rtOpts->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);
+ break;
+
+ case PTP_MM_UPDATE_GLOBAL_TIME_PROPERTIES:
+ ptpClock->current_utc_offset = flip16(*(Integer16*)(buf + 62));
+ ptpClock->leap_59 = *(UInteger8*)(buf + 67);
+ ptpClock->leap_61 = *(UInteger8*)(buf + 71);
+ ptpClock->epoch_number = flip16(*(UInteger16*)(buf + 74));
+ break;
+
+ default:
+ break;
+ }
+
+ return ptpClock->port_state;
+}
+
+void msgUnpackManagementPayload(char *buf, MsgManagement *manage)
+{
+ switch(manage->managementMessageKey)
+ {
+ case PTP_MM_CLOCK_IDENTITY:
+ DBGV("msgUnloadManagementPayload: managementMessageKey PTP_MM_CLOCK_IDENTITY\n");
+ manage->payload.clockIdentity.clockCommunicationTechnology = *(UInteger8*)(buf + 63);
+ memcpy(manage->payload.clockIdentity.clockUuidField, buf + 64, PTP_UUID_LENGTH);
+ manage->payload.clockIdentity.clockPortField = flip16(*(UInteger16*)(buf + 74));
+ memcpy(manage->payload.clockIdentity.manufacturerIdentity, buf + 76, MANUFACTURER_ID_LENGTH);
+ break;
+
+ case PTP_MM_DEFAULT_DATA_SET:
+ DBGV("msgUnloadManagementPayload: managementMessageKey PTP_MM_DEFAULT_DATA_SET\n");
+ manage->payload.defaultData.clockCommunicationTechnology = *(UInteger8*)(buf + 63);
+ memcpy(manage->payload.defaultData.clockUuidField, buf + 64, PTP_UUID_LENGTH);
+ manage->payload.defaultData.clockPortField = flip16(*(UInteger16*)(buf + 74));
+ manage->payload.defaultData.clockStratum = *(UInteger16*)(buf + 79);
+ memcpy(manage->payload.defaultData.clockIdentifier, buf + 80, PTP_CODE_STRING_LENGTH);
+ manage->payload.defaultData.clockVariance = flip16(*(UInteger16*)(buf + 86));
+ manage->payload.defaultData.clockFollowupCapable = *(UInteger8*)(buf + 91);
+ manage->payload.defaultData.preferred = *(UInteger8*)(buf + 95);
+ manage->payload.defaultData.initializable = *(UInteger8*)(buf + 99);
+ manage->payload.defaultData.externalTiming = *(UInteger8*)(buf + 103);
+ manage->payload.defaultData.isBoundaryClock = *(UInteger8*)(buf + 107);
+ manage->payload.defaultData.syncInterval = *(UInteger8*)(buf + 111);
+ memcpy(manage->payload.defaultData.subdomainName, buf + 112, PTP_SUBDOMAIN_NAME_LENGTH);
+ manage->payload.defaultData.numberPorts = flip16(*(UInteger16*)(buf + 130));
+ manage->payload.defaultData.numberForeignRecords = flip16(*(UInteger16*)(buf + 134));
+ break;
+
+ case PTP_MM_CURRENT_DATA_SET:
+ DBGV("msgUnloadManagementPayload: managementMessageKey PTP_MM_CURRENT_DATA_SET\n");
+ manage->payload.current.stepsRemoved = flip16(*(UInteger16*)(buf + 62));
+ manage->payload.current.offsetFromMaster.seconds = flip32(*(UInteger32*)(buf + 64));
+ manage->payload.current.offsetFromMaster.nanoseconds = flip32(*(UInteger32*)(buf + 68));
+ manage->payload.current.oneWayDelay.seconds = flip32(*(UInteger32*)(buf + 72));
+ manage->payload.current.oneWayDelay.nanoseconds = flip32(*(Integer32*)(buf + 76));
+ break;
+
+ case PTP_MM_PARENT_DATA_SET:
+ DBGV("msgUnloadManagementPayload: managementMessageKey PTP_MM_PORT_DATA_SET\n");
+ manage->payload.parent.parentCommunicationTechnology = *(UInteger8*)(buf + 63);
+ memcpy(manage->payload.parent.parentUuid, buf + 64, PTP_UUID_LENGTH);
+ manage->payload.parent.parentPortId = flip16(*(UInteger16*)(buf + 74));
+ manage->payload.parent.parentLastSyncSequenceNumber = flip16(*(UInteger16*)(buf + 74));
+ manage->payload.parent.parentFollowupCapable = *(UInteger8*)(buf + 83);
+ manage->payload.parent.parentExternalTiming = *(UInteger8*)(buf + 87);
+ manage->payload.parent.parentVariance = flip16(*(UInteger16*)(buf + 90));
+ manage->payload.parent.parentStats = *(UInteger8*)(buf + 85);
+ manage->payload.parent.observedVariance = flip16(*(Integer16*)(buf + 98));
+ manage->payload.parent.observedDrift = flip32(*(Integer32*)(buf + 100));
+ manage->payload.parent.utcReasonable = *(UInteger8*)(buf + 107);
+ manage->payload.parent.grandmasterCommunicationTechnology = *(UInteger8*)(buf + 111);
+ memcpy(manage->payload.parent.grandmasterUuidField, buf + 112, PTP_UUID_LENGTH);
+ manage->payload.parent.grandmasterPortIdField = flip16(*(UInteger16*)(buf + 122));
+ manage->payload.parent.grandmasterStratum = *(UInteger8*)(buf + 127);
+ memcpy(manage->payload.parent.grandmasterIdentifier, buf + 128, PTP_CODE_STRING_LENGTH);
+ manage->payload.parent.grandmasterVariance = flip16(*(Integer16*)(buf + 134));
+ manage->payload.parent.grandmasterPreferred = *(UInteger8*)(buf + 139);
+ manage->payload.parent.grandmasterIsBoundaryClock = *(UInteger8*)(buf + 144);
+ manage->payload.parent.grandmasterSequenceNumber = flip16(*(UInteger16*)(buf + 146));
+ break;
+
+ case PTP_MM_PORT_DATA_SET:
+ DBGV("msgUnloadManagementPayload: managementMessageKey PTP_MM_FOREIGN_DATA_SET\n");
+ manage->payload.port.returnedPortNumber = flip16(*(UInteger16*)(buf + 62));
+ manage->payload.port.portState = *(UInteger8*)(buf + 67);
+ manage->payload.port.lastSyncEventSequenceNumber = flip16(*(UInteger16*)(buf + 70));
+ manage->payload.port.lastGeneralEventSequenceNumber = flip16(*(UInteger16*)(buf + 74));
+ manage->payload.port.portCommunicationTechnology = *(UInteger8*)(buf + 79);
+ memcpy(manage->payload.port.portUuidField, buf + 80, PTP_UUID_LENGTH);
+ manage->payload.port.portIdField = flip16(*(UInteger16*)(buf + 90));
+ manage->payload.port.burstEnabled = *(UInteger8*)(buf + 95);
+ manage->payload.port.subdomainAddressOctets = *(UInteger8*)(buf + 97);
+ manage->payload.port.eventPortAddressOctets = *(UInteger8*)(buf + 98);
+ manage->payload.port.generalPortAddressOctets = *(UInteger8*)(buf + 99);
+ memcpy(manage->payload.port.subdomainAddress, buf + 100, SUBDOMAIN_ADDRESS_LENGTH);
+ memcpy(manage->payload.port.eventPortAddress, buf + 106, PORT_ADDRESS_LENGTH);
+ memcpy(manage->payload.port.generalPortAddress, buf + 110, PORT_ADDRESS_LENGTH);
+ break;
+
+ case PTP_MM_GLOBAL_TIME_DATA_SET:
+ DBGV("msgUnloadManagementPayload: managementMessageKey PTP_MM_GLOBAL_TIME_DATA_SET\n");
+ manage->payload.globalTime.localTime.seconds = flip32(*(UInteger32*)(buf + 60));
+ manage->payload.globalTime.localTime.nanoseconds = flip32(*(Integer32*)(buf + 64));
+ manage->payload.globalTime.currentUtcOffset = flip16(*(Integer16*)(buf + 70));
+ manage->payload.globalTime.leap59 = *(UInteger8*)(buf + 75);
+ manage->payload.globalTime.leap61 = *(UInteger8*)(buf + 79);
+ manage->payload.globalTime.epochNumber = flip16(*(UInteger16*)(buf + 82));
+ break;
+
+
+ case PTP_MM_FOREIGN_DATA_SET:
+ DBGV("msgUnloadManagementPayload: managementMessageKey PTP_MM_FOREIGN_DATA_SET\n");
+ manage->payload.foreign.returnedPortNumber = flip16(*(UInteger16*)(buf + 62));
+ manage->payload.foreign.returnedRecordNumber = flip16(*(UInteger16*)(buf + 68));
+ manage->payload.foreign.foreignMasterCommunicationTechnology = *(UInteger8*)(buf + 71);
+ memcpy(manage->payload.foreign.foreignMasterUuid, buf + 72, PTP_UUID_LENGTH);
+ manage->payload.foreign.foreignMasterPortId = flip16(*(UInteger16*)(buf + 82));
+ manage->payload.foreign.foreignMasterSyncs = flip16(*(UInteger16*)(buf + 66));
+ break;
+
+ case PTP_MM_NULL:
+ DBGV("msgUnloadManagementPayload: managementMessageKey NULL\n");
+ break;
+
+ default:
+ DBGV("msgUnloadManagementPayload: managementMessageKey ?\n");
+ break;
+ }
+
+ return;
+}
+
+void msgPackHeader(char *buf, PtpClock *ptpClock)
+{
+ *(Integer32*)(buf + 0) = shift16(flip16(VERSION_PTP), 0) | shift16(flip16(VERSION_NETWORK), 1);
+ memcpy((buf + 4), ptpClock->subdomain_name, 16);
+ *(Integer32*)(buf + 20) = shift8(ptpClock->port_communication_technology, 1);
+ memcpy((buf + 22), ptpClock->port_uuid_field, 6);
+
+ if(ptpClock->external_timing)
+ setFlag((buf + 34), PTP_EXT_SYNC);
+ if(ptpClock->clock_followup_capable)
+ setFlag((buf + 34), PTP_ASSIST);
+ if(ptpClock->is_boundary_clock)
+ setFlag((buf + 34), PTP_BOUNDARY_CLOCK);
+}
+
+void msgPackSync(char *buf, Boolean burst,
+ TimeRepresentation *originTimestamp, PtpClock *ptpClock)
+{
+ *(UInteger8*)(buf +20) = 1; /* messageType */
+ *(Integer32*)(buf + 28) = shift16(flip16(ptpClock->port_id_field), 0) | shift16(flip16(ptpClock->last_sync_event_sequence_number), 1);
+ *(UInteger8*)(buf +32) = PTP_SYNC_MESSAGE; /* control */
+ if(ptpClock->burst_enabled && burst)
+ setFlag((buf + 34), PTP_SYNC_BURST);
+ else
+ clearFlag((buf + 34), PTP_SYNC_BURST);
+ if(ptpClock->parent_stats)
+ setFlag((buf + 34), PARENT_STATS);
+ else
+ clearFlag((buf + 34), PARENT_STATS);
+
+
+ *(Integer32*)(buf + 40) = flip32(originTimestamp->seconds);
+ *(Integer32*)(buf + 44) = flip32(originTimestamp->nanoseconds);
+ *(Integer32*)(buf + 48) = shift16(flip16(ptpClock->epoch_number), 0) | shift16(flip16(ptpClock->current_utc_offset), 1);
+ *(Integer32*)(buf + 52) = shift8(ptpClock->grandmaster_communication_technology, 1);
+ memcpy((buf + 54), ptpClock->grandmaster_uuid_field, 6);
+ *(Integer32*)(buf + 60) = shift16(flip16(ptpClock->grandmaster_port_id_field), 0) | shift16(flip16(ptpClock->grandmaster_sequence_number), 1);
+ *(Integer32*)(buf + 64) = shift8(ptpClock->grandmaster_stratum, 3);
+ memcpy((buf + 68), ptpClock->grandmaster_identifier, 4);
+ *(Integer32*)(buf + 72) = shift16(flip16(ptpClock->grandmaster_variance), 1);
+ *(Integer32*)(buf + 76) = shift16(flip16(ptpClock->grandmaster_preferred), 0) | shift16(flip16(ptpClock->grandmaster_is_boundary_clock), 1);
+ *(Integer32*)(buf + 80) = shift16(flip16(ptpClock->sync_interval), 1);
+ *(Integer32*)(buf + 84) = shift16(flip16(ptpClock->clock_variance), 1);
+ *(Integer32*)(buf + 88) = shift16(flip16(ptpClock->steps_removed), 1);
+ *(Integer32*)(buf + 92) = shift8(ptpClock->clock_stratum, 3);
+ memcpy((buf + 96), ptpClock->clock_identifier, 4);
+ *(Integer32*)(buf + 100) = shift8(ptpClock->parent_communication_technology, 1);
+ memcpy((buf + 102), ptpClock->parent_uuid, 6);
+ *(Integer32*)(buf + 108) = shift16(flip16(ptpClock->parent_port_id), 1);
+ *(Integer32*)(buf + 112) = shift16(flip16(ptpClock->observed_variance), 1);
+ *(Integer32*)(buf + 116) = flip32(ptpClock->observed_drift);
+ *(Integer32*)(buf + 120) = shift8(ptpClock->utc_reasonable, 3);
+}
+
+void msgPackDelayReq(char *buf, Boolean burst,
+ TimeRepresentation *originTimestamp, PtpClock *ptpClock)
+{
+ *(UInteger8*)(buf + 20) = 1; /* messageType */
+ *(Integer32*)(buf + 28) = shift16(flip16(ptpClock->port_id_field), 0) | shift16(flip16(ptpClock->last_sync_event_sequence_number), 1);
+ *(UInteger8*)(buf + 32) = PTP_DELAY_REQ_MESSAGE; /* control */
+ if(ptpClock->burst_enabled && burst)
+ setFlag((buf + 34), PTP_SYNC_BURST);
+ else
+ clearFlag((buf + 34), PTP_SYNC_BURST);
+ if(ptpClock->parent_stats)
+ setFlag((buf + 34), PARENT_STATS);
+ else
+ clearFlag((buf + 34), PARENT_STATS);
+
+ *(Integer32*)(buf + 40) = flip32(originTimestamp->seconds);
+ *(Integer32*)(buf + 44) = flip32(originTimestamp->nanoseconds);
+ *(Integer32*)(buf + 48) = shift16(flip16(ptpClock->epoch_number), 0) | shift16(flip16(ptpClock->current_utc_offset), 1);
+ *(Integer32*)(buf + 52) = shift8(ptpClock->grandmaster_communication_technology, 1);
+ memcpy((buf + 54), ptpClock->grandmaster_uuid_field, 6);
+ *(Integer32*)(buf + 60) = shift16(flip16(ptpClock->grandmaster_port_id_field), 0) | shift16(flip16(ptpClock->grandmaster_sequence_number), 1);
+ *(Integer32*)(buf + 64) = shift8(ptpClock->grandmaster_stratum, 3);
+ memcpy((buf + 68), ptpClock->grandmaster_identifier, 4);
+ *(Integer32*)(buf + 72) = shift16(flip16(ptpClock->grandmaster_variance), 1);
+ *(Integer32*)(buf + 76) = shift16(flip16(ptpClock->grandmaster_preferred), 0) | shift16(flip16(ptpClock->grandmaster_is_boundary_clock), 1);
+ *(Integer32*)(buf + 80) = shift16(flip16(ptpClock->sync_interval), 1);
+ *(Integer32*)(buf + 84) = shift16(flip16(ptpClock->clock_variance), 1);
+ *(Integer32*)(buf + 88) = shift16(flip16(ptpClock->steps_removed), 1);
+ *(Integer32*)(buf + 92) = shift8(ptpClock->clock_stratum, 3);
+ memcpy((buf + 96), ptpClock->clock_identifier, 4);
+ *(Integer32*)(buf + 100) = shift8(ptpClock->parent_communication_technology, 1);
+ memcpy((buf + 102), ptpClock->parent_uuid, 6);
+ *(Integer32*)(buf + 108) = shift16(flip16(ptpClock->parent_port_id), 1);
+ *(Integer32*)(buf + 112) = shift16(flip16(ptpClock->observed_variance), 1);
+ *(Integer32*)(buf + 116) = flip32(ptpClock->observed_drift);
+ *(Integer32*)(buf + 120) = shift8(ptpClock->utc_reasonable, 3);
+}
+
+void msgPackFollowUp(char *buf, UInteger16 associatedSequenceId,
+ TimeRepresentation *preciseOriginTimestamp, PtpClock *ptpClock)
+{
+ *(UInteger8*)(buf + 20) = 2; /* messageType */
+ *(Integer32*)(buf + 28) = shift16(flip16(ptpClock->port_id_field), 0) | shift16(flip16(ptpClock->last_general_event_sequence_number), 1);
+ *(UInteger8*)(buf + 32) = PTP_FOLLOWUP_MESSAGE; /* control */
+ clearFlag((buf + 34), PTP_SYNC_BURST);
+ clearFlag((buf + 34), PARENT_STATS);
+
+ *(Integer32*)(buf + 40) = shift16(flip16(associatedSequenceId), 1);
+ *(Integer32*)(buf + 44) = flip32(preciseOriginTimestamp->seconds);
+ *(Integer32*)(buf + 48) = flip32(preciseOriginTimestamp->nanoseconds);
+}
+
+void msgPackDelayResp(char *buf, MsgHeader *header,
+ TimeRepresentation *delayReceiptTimestamp, PtpClock *ptpClock)
+{
+ *(UInteger8*)(buf + 20) = 2; /* messageType */
+ *(Integer32*)(buf + 28) = shift16(flip16(ptpClock->port_id_field), 0) | shift16(flip16(ptpClock->last_general_event_sequence_number), 1);
+ *(UInteger8*)(buf + 32) = PTP_DELAY_RESP_MESSAGE; /* control */
+ clearFlag((buf + 34), PTP_SYNC_BURST);
+ clearFlag((buf + 34), PARENT_STATS);
+
+ *(Integer32*)(buf + 40) = flip32(delayReceiptTimestamp->seconds);
+ *(Integer32*)(buf + 44) = flip32(delayReceiptTimestamp->nanoseconds);
+ *(Integer32*)(buf + 48) = shift8(header->sourceCommunicationTechnology, 1);
+ memcpy(buf + 50, header->sourceUuid, 6);
+ *(Integer32*)(buf + 56) = shift16(flip16(header->sourcePortId), 0) | shift16(flip16(header->sequenceId), 1);
+}
+
+UInteger16 msgPackManagement(char *buf, MsgManagement *manage, PtpClock *ptpClock)
+{
+ *(UInteger8*)(buf + 20) = 2; /* messageType */
+ *(Integer32*)(buf + 28) = shift16(flip16(ptpClock->port_id_field), 0) | shift16(flip16(ptpClock->last_general_event_sequence_number), 1);
+ *(UInteger8*)(buf + 32) = PTP_MANAGEMENT_MESSAGE; /* control */
+ clearFlag((buf + 34), PTP_SYNC_BURST);
+ clearFlag((buf + 34), PARENT_STATS);
+ *(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);
+ *(Integer32*)(buf + 52) = shift16(flip16(MM_STARTING_BOUNDARY_HOPS), 0);
+
+ *(UInteger8*)(buf + 55) = manage->managementMessageKey;
+
+ switch(manage->managementMessageKey)
+ {
+ case PTP_MM_GET_FOREIGN_DATA_SET:
+ *(UInteger16*)(buf + 62) = manage->recordKey;
+ *(Integer32*)(buf + 56) = shift16(flip16(4), 1);
+ return 64;
+
+ default:
+ *(Integer32*)(buf + 56) = shift16(flip16(0), 1);
+ return 60;
+ }
+}
+
+UInteger16 msgPackManagementResponse(char *buf, MsgHeader *header, MsgManagement *manage, PtpClock *ptpClock)
+{
+ TimeInternal internalTime;
+ TimeRepresentation externalTime;
+
+ *(UInteger8*)(buf + 20) = 2; /* messageType */
+ *(Integer32*)(buf + 28) = shift16(flip16(ptpClock->port_id_field), 0) | shift16(flip16(ptpClock->last_general_event_sequence_number), 1);
+ *(UInteger8*)(buf + 32) = PTP_MANAGEMENT_MESSAGE; /* control */
+ clearFlag((buf + 34), PTP_SYNC_BURST);
+ clearFlag((buf + 34), PARENT_STATS);
+ *(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);
+ *(Integer32*)(buf + 52) = shift16(flip16(manage->startingBoundaryHops - manage->boundaryHops + 1), 0);
+
+ switch(manage->managementMessageKey)
+ {
+ case PTP_MM_OBTAIN_IDENTITY:
+ *(UInteger8*)(buf + 55) = PTP_MM_CLOCK_IDENTITY;
+ *(Integer32*)(buf + 56) = shift16(flip16(64), 1);
+ *(Integer32*)(buf + 60) = shift8(ptpClock->clock_communication_technology, 3);
+ memcpy(buf + 64, ptpClock->clock_uuid_field, 6);
+ *(Integer32*)(buf + 72) = shift16(flip16(ptpClock->clock_port_id_field), 1);
+ memcpy((buf + 76), MANUFACTURER_ID, 48);
+ return 124;
+
+ case PTP_MM_GET_DEFAULT_DATA_SET:
+ *(UInteger8*)(buf + 55) = PTP_MM_DEFAULT_DATA_SET;
+ *(Integer32*)(buf + 56) = shift16(flip16(76), 1);
+ *(Integer32*)(buf + 60) = shift8(ptpClock->clock_communication_technology, 3);
+ memcpy(buf + 64, ptpClock->clock_uuid_field, 6);
+ *(Integer32*)(buf + 72) = shift16(flip16(ptpClock->clock_port_id_field), 1);
+ *(Integer32*)(buf + 76) = shift8(ptpClock->clock_stratum, 3);
+ memcpy(buf + 80, ptpClock->clock_identifier, 4);
+ *(Integer32*)(buf + 84) = shift16(flip16(ptpClock->clock_variance), 1);
+ *(Integer32*)(buf + 88) = shift8(ptpClock->clock_followup_capable, 3);
+ *(Integer32*)(buf + 92) = shift8(ptpClock->preferred, 3);
+ *(Integer32*)(buf + 96) = shift8(ptpClock->initializable, 3);
+ *(Integer32*)(buf + 100) = shift8(ptpClock->external_timing, 3);
+ *(Integer32*)(buf + 104) = shift8(ptpClock->is_boundary_clock, 3);
+ *(Integer32*)(buf + 108) = shift8(ptpClock->sync_interval, 3);
+ memcpy(buf + 112, ptpClock->subdomain_name, 16);
+ *(Integer32*)(buf + 128) = shift16(flip16(ptpClock->number_ports), 1);
+ *(Integer32*)(buf + 132) = shift16(flip16(ptpClock->number_foreign_records), 1);
+ return 136;
+
+ case PTP_MM_GET_CURRENT_DATA_SET:
+ *(UInteger8*)(buf + 55) = PTP_MM_CURRENT_DATA_SET;
+ *(Integer32*)(buf + 56) = shift16(flip16(20), 1);
+ *(Integer32*)(buf + 60) = shift16(flip16(ptpClock->steps_removed), 1);
+
+ fromInternalTime(&ptpClock->offset_from_master, &externalTime, 0);
+ *(Integer32*)(buf + 64) = flip32(externalTime.seconds);
+ *(Integer32*)(buf + 68) = flip32(externalTime.nanoseconds);
+
+ fromInternalTime(&ptpClock->one_way_delay, &externalTime, 0);
+ *(Integer32*)(buf + 72) = flip32(externalTime.seconds);
+ *(Integer32*)(buf + 76) = flip32(externalTime.nanoseconds);
+ return 80;
+
+ case PTP_MM_GET_PARENT_DATA_SET:
+ *(UInteger8*)(buf + 55) = PTP_MM_PARENT_DATA_SET;
+ *(Integer32*)(buf + 56) = shift16(flip16(90), 1);
+ *(Integer32*)(buf + 60) = shift8(ptpClock->parent_communication_technology, 3);
+ memcpy(buf + 64, ptpClock->parent_uuid, 6);
+ *(Integer32*)(buf + 72) = shift16(flip16(ptpClock->parent_port_id), 1);
+ *(Integer32*)(buf + 76) = shift16(flip16(ptpClock->parent_last_sync_sequence_number), 1);
+ *(Integer32*)(buf + 80) = shift8(ptpClock->parent_followup_capable, 1);
+ *(Integer32*)(buf + 84) = shift8(ptpClock->parent_external_timing, 3);
+ *(Integer32*)(buf + 88) = shift16(flip16(ptpClock->parent_variance), 1);
+ *(Integer32*)(buf + 92) = shift8(ptpClock->parent_stats, 3);
+ *(Integer32*)(buf + 96) = shift16(flip16(ptpClock->observed_variance), 1);
+ *(Integer32*)(buf + 100) = flip32(ptpClock->observed_drift);
+ *(Integer32*)(buf + 104) = shift8(ptpClock->utc_reasonable, 3);
+ *(Integer32*)(buf + 108) = shift8(ptpClock->grandmaster_communication_technology, 3);
+ memcpy(buf + 112, ptpClock->grandmaster_uuid_field, 6);
+ *(Integer32*)(buf + 120) = shift16(flip16(ptpClock->grandmaster_port_id_field), 1);
+ *(Integer32*)(buf + 124) = shift8(ptpClock->grandmaster_stratum, 3);
+ memcpy(buf + 128, ptpClock->grandmaster_identifier, 4);
+ *(Integer32*)(buf + 132) = shift16(flip16(ptpClock->grandmaster_variance), 1);
+ *(Integer32*)(buf + 136) = shift8(ptpClock->grandmaster_preferred, 3);
+ *(Integer32*)(buf + 140) = shift8(ptpClock->grandmaster_is_boundary_clock, 3);
+ *(Integer32*)(buf + 144) = shift16(flip16(ptpClock->grandmaster_sequence_number), 1);
+ return 148;
+
+ case PTP_MM_GET_PORT_DATA_SET:
+ if(manage->targetPortId && manage->targetPortId != ptpClock->port_id_field)
+ {
+ *(UInteger8*)(buf + 55) = PTP_MM_NULL;
+ *(Integer32*)(buf + 56) = shift16(flip16(0), 1);
+ return 0;
+ }
+
+ *(UInteger8*)(buf + 55) = PTP_MM_PORT_DATA_SET;
+ *(Integer32*)(buf + 56) = shift16(flip16(52), 1);
+ *(Integer32*)(buf + 60) = shift16(flip16(ptpClock->port_id_field), 1);
+ *(Integer32*)(buf + 64) = shift8(ptpClock->port_state, 3);
+ *(Integer32*)(buf + 68) = shift16(flip16(ptpClock->last_sync_event_sequence_number), 1);
+ *(Integer32*)(buf + 72) = shift16(flip16(ptpClock->last_general_event_sequence_number), 1);
+ *(Integer32*)(buf + 76) = shift8(ptpClock->port_communication_technology, 3);
+ memcpy(buf + 80, ptpClock->port_uuid_field, 6);
+ *(Integer32*)(buf + 88) = shift16(flip16(ptpClock->port_id_field), 1);
+ *(Integer32*)(buf + 92) = shift8(ptpClock->burst_enabled, 3);
+ *(Integer32*)(buf + 96) = shift8(4, 1) | shift8(2, 2) | shift8(2, 3);
+ memcpy(buf + 100, ptpClock->subdomain_address, 4);
+ memcpy(buf + 106, ptpClock->event_port_address, 2);
+ memcpy(buf + 110, ptpClock->general_port_address, 2);
+ return 112;
+
+ case PTP_MM_GET_GLOBAL_TIME_DATA_SET:
+ *(UInteger8*)(buf + 55) = PTP_MM_GLOBAL_TIME_DATA_SET;
+ *(Integer32*)(buf + 56) = shift16(flip16(24), 1);
+
+ getTime(&internalTime);
+ fromInternalTime(&internalTime, &externalTime, ptpClock->halfEpoch);
+ *(Integer32*)(buf + 60) = flip32(externalTime.seconds);
+ *(Integer32*)(buf + 64) = flip32(externalTime.nanoseconds);
+
+ *(Integer32*)(buf + 68) = shift16(flip16(ptpClock->current_utc_offset), 1);
+ *(Integer32*)(buf + 72) = shift8(ptpClock->leap_59, 3);
+ *(Integer32*)(buf + 76) = shift8(ptpClock->leap_61, 3);
+ *(Integer32*)(buf + 80) = shift16(flip16(ptpClock->epoch_number), 1);
+ return 84;
+
+ case PTP_MM_GET_FOREIGN_DATA_SET:
+ if((manage->targetPortId && manage->targetPortId != ptpClock->port_id_field)
+ || !manage->recordKey || manage->recordKey > ptpClock->number_foreign_records)
+ {
+ *(UInteger8*)(buf + 55) = PTP_MM_NULL;
+ *(Integer32*)(buf + 56) = shift16(flip16(0), 1);
+ return 0;
+ }
+
+ *(UInteger8*)(buf + 55) = PTP_MM_FOREIGN_DATA_SET;
+ *(Integer32*)(buf + 56) = shift16(flip16(28), 1);
+ *(Integer32*)(buf + 60) = shift16(flip16(ptpClock->port_id_field), 1);
+ *(Integer32*)(buf + 64) = shift16(flip16(manage->recordKey - 1), 1);
+ *(Integer32*)(buf + 68) = shift8(ptpClock->foreign[manage->recordKey - 1].foreign_master_communication_technology, 3);
+ memcpy(buf + 72, ptpClock->foreign[manage->recordKey - 1].foreign_master_uuid, 6);
+ *(Integer32*)(buf + 80) = shift16(flip16(ptpClock->foreign[manage->recordKey - 1].foreign_master_port_id), 1);
+ *(Integer32*)(buf + 84) = shift16(flip16(ptpClock->foreign[manage->recordKey - 1].foreign_master_syncs), 1);
+ return 88;
+
+ default:
+ return 0;
+ }
+}
diff --git a/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_net.c b/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_net.c new file mode 100644 index 0000000..5826ebc --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_net.c @@ -0,0 +1,401 @@ +/* net.c */
+
+#include "utils/lwiplib.h"
+#include "utils/ptpdlib.h"
+#include "lwip/inet.h"
+
+/* Network Buffer Queue Functions. */
+static void netQInit(BufQueue *pQ)
+{
+ pQ->get = 0;
+ pQ->put = 0;
+ pQ->count = 0;
+}
+
+static Integer32 netQPut(BufQueue *pQ, void *pbuf)
+{
+ if(pQ->count >= PBUF_QUEUE_SIZE)
+ return FALSE;
+ pQ->pbuf[pQ->put] = pbuf;
+ pQ->put = (pQ->put + 1) % PBUF_QUEUE_SIZE;
+ pQ->count++;
+ return TRUE;
+}
+
+void *netQGet(BufQueue *pQ)
+{
+ void *pbuf;
+
+ if(!pQ->count)
+ return NULL;
+ pbuf = pQ->pbuf[pQ->get];
+ pQ->get = (pQ->get + 1) % PBUF_QUEUE_SIZE;
+ pQ->count--;
+ return pbuf;
+}
+
+static Integer32 netQCheck(BufQueue *pQ)
+{
+ if(!pQ->count)
+ return FALSE;
+ return TRUE;
+}
+
+/* Processing an incoming message on the Event port. */
+static void eventRecv(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+ struct ip_addr *addr, u16_t port)
+{
+ NetPath *netPath = (NetPath *)arg;
+
+ /* prevent warnings about unused arguments */
+ (void)pcb; (void)addr; (void)port;
+
+ /* Place the incoming message on the Event Port QUEUE. */
+ if(!netQPut(&netPath->eventQ, p))
+ PERROR("Event Queue Full!\n");
+}
+
+/* Processing an incoming message on the Event port. */
+static void generalRecv(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+ struct ip_addr *addr, u16_t port)
+{
+ NetPath *netPath = (NetPath *)arg;
+
+ /* prevent warnings about unused arguments */
+ (void)pcb; (void)addr; (void)port;
+
+ /* Place the incoming message on the Event Port QUEUE. */
+ if(!netQPut(&netPath->generalQ, p))
+ {
+ PERROR("Event Queue Full!\n");
+ }
+}
+
+Boolean lookupSubdomainAddress(Octet *subdomainName, Octet *subdomainAddress)
+{
+ UInteger32 h;
+
+ /* set multicast group address based on subdomainName */
+ if (!memcmp(subdomainName, DEFAULT_PTP_DOMAIN_NAME, PTP_SUBDOMAIN_NAME_LENGTH))
+ memcpy(subdomainAddress, DEFAULT_PTP_DOMAIN_ADDRESS, NET_ADDRESS_LENGTH);
+ else if(!memcmp(subdomainName, ALTERNATE_PTP_DOMAIN1_NAME, PTP_SUBDOMAIN_NAME_LENGTH))
+ memcpy(subdomainAddress, ALTERNATE_PTP_DOMAIN1_ADDRESS, NET_ADDRESS_LENGTH);
+ else if(!memcmp(subdomainName, ALTERNATE_PTP_DOMAIN2_NAME, PTP_SUBDOMAIN_NAME_LENGTH))
+ memcpy(subdomainAddress, ALTERNATE_PTP_DOMAIN2_ADDRESS, NET_ADDRESS_LENGTH);
+ else if(!memcmp(subdomainName, ALTERNATE_PTP_DOMAIN3_NAME, PTP_SUBDOMAIN_NAME_LENGTH))
+ memcpy(subdomainAddress, ALTERNATE_PTP_DOMAIN3_ADDRESS, NET_ADDRESS_LENGTH);
+ else
+ {
+ h = crc_algorithm(subdomainName, PTP_SUBDOMAIN_NAME_LENGTH) % 3;
+ switch(h)
+ {
+ case 0:
+ memcpy(subdomainAddress, ALTERNATE_PTP_DOMAIN1_ADDRESS, NET_ADDRESS_LENGTH);
+ break;
+ case 1:
+ memcpy(subdomainAddress, ALTERNATE_PTP_DOMAIN2_ADDRESS, NET_ADDRESS_LENGTH);
+ break;
+ case 2:
+ memcpy(subdomainAddress, ALTERNATE_PTP_DOMAIN3_ADDRESS, NET_ADDRESS_LENGTH);
+ break;
+ default:
+ ERROR("handle out of range for '%s'!\n", subdomainName);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+Boolean netInit(NetPath *netPath, RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ int i;
+ struct in_addr netAddr;
+ char addrStr[NET_ADDRESS_LENGTH];
+
+ DBG("netInit\n");
+
+ /* Allocate tx buffer for the event port. */
+ netPath->eventTxBuf = pbuf_alloc(PBUF_TRANSPORT, PACKET_SIZE, PBUF_RAM);
+ if(netPath->eventTxBuf == NULL)
+ {
+ PERROR("Failed to allocate Event Tx Buffer\n");
+ return FALSE;
+ }
+
+ /* Allocate tx buffer for the general port. */
+ netPath->generalTxBuf = pbuf_alloc(PBUF_TRANSPORT, PACKET_SIZE, PBUF_RAM);
+ if(netPath->generalTxBuf == NULL)
+ {
+ PERROR("Failed to allocate Event Tx Buffer\n");
+ pbuf_free(netPath->eventTxBuf);
+ return FALSE;
+ }
+
+ /* Open lwIP raw udp interfaces for the event port. */
+ netPath->eventPcb = udp_new();
+ if(netPath->eventPcb == NULL)
+ {
+ PERROR("Failed to open Event UDP PCB\n");
+ pbuf_free(netPath->eventTxBuf);
+ pbuf_free(netPath->generalTxBuf);
+ return FALSE;
+ }
+
+ /* Open lwIP raw udp interfaces for the general port. */
+ netPath->generalPcb = udp_new();
+ if(netPath->generalPcb == NULL)
+ {
+ PERROR("Failed to open General UDP PCB\n");
+ udp_remove(netPath->eventPcb);
+ pbuf_free(netPath->eventTxBuf);
+ pbuf_free(netPath->generalTxBuf);
+ return FALSE;
+ }
+
+ /* Initialize the buffer queues. */
+ netQInit(&netPath->eventQ);
+ netQInit(&netPath->generalQ);
+
+ /* Configure network (broadcast/unicast) addresses. */
+ netPath->unicastAddr = 0;
+ if(!lookupSubdomainAddress(rtOpts->subdomainName, addrStr))
+ {
+ udp_disconnect(netPath->eventPcb);
+ udp_disconnect(netPath->generalPcb);
+ udp_remove(netPath->eventPcb);
+ udp_remove(netPath->generalPcb);
+ pbuf_free(netPath->eventTxBuf);
+ pbuf_free(netPath->generalTxBuf);
+ return FALSE;
+ }
+ if(!inet_aton(addrStr, &netAddr))
+ {
+ ERROR("failed to encode multi-cast address: %s\n", addrStr);
+ udp_disconnect(netPath->eventPcb);
+ udp_disconnect(netPath->generalPcb);
+ udp_remove(netPath->eventPcb);
+ udp_remove(netPath->generalPcb);
+ pbuf_free(netPath->eventTxBuf);
+ pbuf_free(netPath->generalTxBuf);
+ return FALSE;
+ }
+ netPath->multicastAddr = netAddr.s_addr;
+
+ /* Setup subdomain address string. */
+ for(i = 0; i < SUBDOMAIN_ADDRESS_LENGTH; ++i)
+ {
+ ptpClock->subdomain_address[i] = (netAddr.s_addr >> (i * 8)) & 0xff;
+ }
+
+ /* Establish the appropriate UDP bindings/connections for events. */
+
+ udp_recv(netPath->eventPcb, eventRecv, netPath);
+ udp_bind(netPath->eventPcb, IP_ADDR_ANY, PTP_EVENT_PORT);
+// udp_connect(netPath->eventPcb, IP_ADDR_ANY, PTP_EVENT_PORT);
+ *(Integer16*)ptpClock->event_port_address = PTP_EVENT_PORT;
+
+ /* Establish the appropriate UDP bindings/connections for general. */
+ udp_recv(netPath->generalPcb, generalRecv, netPath);
+ udp_bind(netPath->generalPcb, IP_ADDR_ANY, PTP_GENERAL_PORT);
+// udp_connect(netPath->generalPcb, IP_ADDR_ANY, PTP_GENERAL_PORT);
+ *(Integer16*)ptpClock->general_port_address = PTP_GENERAL_PORT;
+
+ /* Return a success code. */
+ return TRUE;
+}
+
+/* shut down the UDP stuff */
+Boolean netShutdown(NetPath *netPath)
+{
+ /* Disconnect and close the Event UDP interface */
+ if(netPath->eventPcb)
+ {
+ udp_disconnect(netPath->eventPcb);
+ udp_remove(netPath->eventPcb);
+ }
+
+ /* Disconnect and close the General UDP interface */
+ if(netPath->generalPcb)
+ {
+ udp_disconnect(netPath->generalPcb);
+ udp_remove(netPath->generalPcb);
+ }
+
+ /* Free up the Event and General Tx PBUFs. */
+ if(netPath->eventTxBuf)
+ {
+ pbuf_free(netPath->eventTxBuf);
+ }
+ if(netPath->generalTxBuf)
+ {
+ pbuf_free(netPath->generalTxBuf);
+ }
+
+ /* Clear the network addresses. */
+ netPath->multicastAddr = 0;
+ netPath->unicastAddr = 0;
+
+ /* Return a success code. */
+ return TRUE;
+}
+
+/* Wait for a packet to come in on either port. For now, there is no wait.
+ * Simply check to see if a packet is available on either port and return 1,
+ * otherwise return 0. */
+int netSelect(TimeInternal *timeout, NetPath *netPath)
+{
+ /* Check the packet queues. If there is data, return TRUE. */
+ if(netQCheck(&netPath->eventQ) || netQCheck(&netPath->generalQ))
+ return 1;
+ return 0;
+}
+
+/* Pop a message off of the event queue and copy it to the passed in buffer. */
+size_t netRecvEvent(Octet *buf, TimeInternal *time, NetPath *netPath)
+{
+ int i, j;
+ int iPacketSize;
+ struct pbuf *p, *pcopy;
+
+ /* Attempt to get a buffer from the Q. If none is available,
+ * return length of 0. */
+
+ p = netQGet(&netPath->eventQ);
+ if(p == NULL)
+ return 0;
+ pcopy = p;
+
+
+ /* Here, p points to a valid PBUF structure. Verify that we have
+ * enough space to store the contents. */
+ if(p->tot_len > PACKET_SIZE)
+ {
+ ERROR("received truncated message\n");
+ return 0;
+ }
+
+ /* Copy the PBUF payload into the buffer. */
+ j = 0;
+ iPacketSize = p->tot_len;
+ for(i = 0; i < iPacketSize; i++)
+ {
+ buf[i] = ((u8_t *)pcopy->payload)[j++];
+ if(j == pcopy->len)
+ {
+ pcopy = pcopy->next;
+ j = 0;
+ }
+ }
+
+ /* Get the timestamp information. */
+ time->seconds = p->time_s;
+ time->nanoseconds = p->time_ns;
+
+ /* Free up the pbuf (chain). */
+ pbuf_free(p);
+
+ /* Return the length of data copied. */
+ return iPacketSize;
+}
+
+/* Pop a message off of the general queue and copy to the passed in buffer. */
+size_t netRecvGeneral(Octet *buf, NetPath *netPath)
+{
+ int i, j;
+ int iPacketSize;
+ struct pbuf *p, *pcopy;
+
+
+ /* Attempt to get a buffer from the Q. If none is available,
+ * return length of 0. */
+ p = netQGet(&netPath->generalQ);
+ if(p == NULL)
+ return 0;
+ pcopy = p;
+
+ /* Here, p points to a valid PBUF structure. Verify that we have
+ * enough space to store the contents. */
+ if(p->tot_len > PACKET_SIZE)
+ {
+ ERROR("received truncated message\n");
+ return 0;
+ }
+
+ /* Copy the PBUF payload into the buffer. */
+ j = 0;
+ iPacketSize = p->tot_len;
+ for(i = 0; i < iPacketSize; i++)
+ {
+ buf[i] = ((u8_t *)pcopy->payload)[j++];
+ if(j == pcopy->len)
+ {
+ pcopy = pcopy->next;
+ j = 0;
+ }
+ }
+
+ /* Free up the pbuf (chain). */
+ pbuf_free(p);
+
+ /* Return the length of data copied. */
+ return iPacketSize;
+}
+
+/* Transmit a packet on the Event Port. */
+size_t netSendEvent(Octet *buf, UInteger16 length, NetPath *netPath)
+{
+ int i, j;
+ struct pbuf *pcopy;
+
+ /* Reallocate the tx pbuf based on the current size. */
+ pbuf_realloc(netPath->eventTxBuf, length);
+ pcopy = netPath->eventTxBuf;
+
+ /* Copy the incoming data into the pbuf payload. */
+ j = 0;
+ for(i = 0; i < length; i++)
+ {
+ ((u8_t *)pcopy->payload)[j++] = buf[i];
+ if(j == pcopy->len)
+ {
+ pcopy = pcopy->next;
+ j = 0;
+ }
+ }
+
+ /* send the buffer. */
+ udp_sendto(netPath->eventPcb, netPath->eventTxBuf,
+ (void *)&netPath->multicastAddr, PTP_EVENT_PORT);
+
+ return(length);
+}
+
+/* Transmit a packet on the General Port. */
+size_t netSendGeneral(Octet *buf, UInteger16 length, NetPath *netPath)
+{
+ int i, j;
+ struct pbuf *pcopy;
+
+ /* Reallocate the tx pbuf based on the current size. */
+ pbuf_realloc(netPath->generalTxBuf, length);
+ pcopy = netPath->generalTxBuf;
+
+ /* Copy the incoming data into the pbuf payload. */
+ j = 0;
+ for(i = 0; i < length; i++)
+ {
+ ((u8_t *)pcopy->payload)[j++] = buf[i];
+ if(j == pcopy->len)
+ {
+ pcopy = pcopy->next;
+ j = 0;
+ }
+ }
+
+ /* send the buffer. */
+ udp_sendto(netPath->eventPcb, netPath->generalTxBuf,
+ (void *)&netPath->multicastAddr, PTP_GENERAL_PORT);
+
+ return(length);
+}
diff --git a/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_servo.c b/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_servo.c new file mode 100644 index 0000000..c6a3eae --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_servo.c @@ -0,0 +1,179 @@ +/* ptpd_servo.c */
+
+#include "../ptpd.h"
+
+void initClock(RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ DBG("initClock\n");
+
+ /* clear vars */
+ ptpClock->master_to_slave_delay.seconds = ptpClock->master_to_slave_delay.nanoseconds = 0;
+ ptpClock->slave_to_master_delay.seconds = ptpClock->slave_to_master_delay.nanoseconds = 0;
+ 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;
+
+ rtOpts->ai = DEFAULT_AI;
+ rtOpts->ap = DEFAULT_AP;
+
+ /* level clock */
+ if(!rtOpts->noAdjust)
+ adjFreq(0);
+}
+
+void updateDelay(TimeInternal *send_time, TimeInternal *recv_time,
+ one_way_delay_filter *owd_filt, RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ Integer16 s;
+
+ DBGV("updateDelay\n");
+
+ /* calc 'slave_to_master_delay' */
+ subTime(&ptpClock->slave_to_master_delay, recv_time, send_time);
+
+ /* update 'one_way_delay' */
+ addTime(&ptpClock->one_way_delay, &ptpClock->master_to_slave_delay, &ptpClock->slave_to_master_delay);
+
+ //
+ // TODO: This looks wrong. If seconds is odd, surely we need to add half
+ // a second's worth of nanoseconds to the result? I'm assuming that
+ // these two lines intend to divide the one_way_delay as a whole by 2.
+ //
+ ptpClock->one_way_delay.seconds /= 2;
+ ptpClock->one_way_delay.nanoseconds /= 2;
+
+ if(ptpClock->one_way_delay.seconds)
+ {
+ /* cannot filter with secs, clear filter */
+ owd_filt->s_exp = owd_filt->nsec_prev = 0;
+ return;
+ }
+
+ /* avoid overflowing filter */
+ s = rtOpts->s;
+ while(labs(owd_filt->y)>>(31-s))
+ --s;
+
+ /* crank down filter cutoff by increasing 's_exp' */
+ if(owd_filt->s_exp < 1)
+ owd_filt->s_exp = 1;
+ else if(owd_filt->s_exp < 1<<s)
+ ++owd_filt->s_exp;
+ else if(owd_filt->s_exp > 1<<s)
+ owd_filt->s_exp = 1<<s;
+
+ /* filter 'one_way_delay' */
+ owd_filt->y = (owd_filt->s_exp-1)*owd_filt->y/owd_filt->s_exp +
+ (ptpClock->one_way_delay.nanoseconds/2 + owd_filt->nsec_prev/2)/owd_filt->s_exp;
+
+ 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);
+}
+
+void updateOffset(TimeInternal *send_time, TimeInternal *recv_time,
+ offset_from_master_filter *ofm_filt, RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ DBGV("updateOffset\n");
+
+ /* calc 'master_to_slave_delay' */
+ subTime(&ptpClock->master_to_slave_delay, recv_time, send_time);
+
+ /* update 'offset_from_master' */
+ subTime(&ptpClock->offset_from_master, &ptpClock->master_to_slave_delay, &ptpClock->one_way_delay);
+
+ if(ptpClock->offset_from_master.seconds)
+ {
+ /* cannot filter with secs, clear filter */
+ ofm_filt->nsec_prev = 0;
+ return;
+ }
+
+ /* filter 'offset_from_master' */
+ ofm_filt->y = ptpClock->offset_from_master.nanoseconds/2 + ofm_filt->nsec_prev/2;
+ 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);
+}
+
+void updateClock(RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ Integer32 adj;
+ TimeInternal timeTmp;
+ static unsigned long ulCount = 0;
+
+ DBGV("updateClock\n");
+
+ if(ptpClock->offset_from_master.seconds)
+ {
+ /* if secs, reset clock or set freq adjustment to max */
+ if(!rtOpts->noAdjust)
+ {
+ if(!rtOpts->noResetClock)
+ {
+ getTime(&timeTmp);
+ subTime(&timeTmp, &timeTmp, &ptpClock->offset_from_master);
+ setTime(&timeTmp);
+ initClock(rtOpts, ptpClock);
+ }
+ else
+ {
+ adj = ptpClock->offset_from_master.nanoseconds > 0 ? ADJ_MAX : -ADJ_MAX;
+ adjFreq(-adj);
+ }
+ }
+ }
+ else
+ {
+ /* the PI controller */
+
+ /* no negative or zero attenuation */
+ if(rtOpts->ap < 1)
+ rtOpts->ap = 1;
+ if(rtOpts->ai < 1)
+ rtOpts->ai = 1;
+
+ /* adjust the filter components each time, till we reach the max */
+ ulCount++;
+ if(!ulCount % 2)
+ {
+ rtOpts->ai++;
+ if(rtOpts->ai > MAX_AI)
+ {
+ rtOpts->ai = MAX_AI;
+ }
+ rtOpts->ap = rtOpts->ai / 100;
+ if(rtOpts->ap < DEFAULT_AP)
+ {
+ rtOpts->ap = DEFAULT_AP;
+ }
+ }
+
+ /* the accumulator for the I component */
+ ptpClock->observed_drift += ptpClock->offset_from_master.nanoseconds/rtOpts->ai;
+
+ adj = ptpClock->offset_from_master.nanoseconds/rtOpts->ap + ptpClock->observed_drift;
+
+ /* apply controller output as a clock tick rate adjustment */
+ if(!rtOpts->noAdjust)
+ adjFreq(-adj);
+ }
+
+ if(rtOpts->displayStats)
+ displayStats(rtOpts, ptpClock);
+
+ DBGV("master-to-slave delay: %10ds %11dns\n",
+ ptpClock->master_to_slave_delay.seconds, ptpClock->master_to_slave_delay.nanoseconds);
+ DBGV("slave-to-master delay: %10ds %11dns\n",
+ ptpClock->slave_to_master_delay.seconds, ptpClock->slave_to_master_delay.nanoseconds);
+ DBGV("one-way delay: %10ds %11dns\n",
+ ptpClock->one_way_delay.seconds, ptpClock->one_way_delay.nanoseconds);
+ DBG("offset from master: %10ds %11dns\n",
+ ptpClock->offset_from_master.seconds, ptpClock->offset_from_master.nanoseconds);
+ DBG("observed drift: %10d\n", ptpClock->observed_drift);
+}
+
diff --git a/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_timer.c b/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_timer.c new file mode 100644 index 0000000..47b0bb5 --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep-tiva/ptpd_timer.c @@ -0,0 +1,75 @@ +/* ptpd_timer.c */
+
+#include "../ptpd.h"
+
+static int iElapsedMilliSeconds = 0;
+
+void initTimer(void)
+{
+ DBG("initTimer\n");
+
+ iElapsedMilliSeconds = 0;
+}
+
+void timerTick(int iTickMilliSeconds)
+{
+ iElapsedMilliSeconds += iTickMilliSeconds;
+}
+
+void timerUpdate(IntervalTimer *itimer)
+{
+ int i, delta;
+
+ delta = iElapsedMilliSeconds / 1000;
+
+ if(delta <= 0)
+ return;
+
+ iElapsedMilliSeconds %= 1000;
+
+ for(i = 0; i < TIMER_ARRAY_SIZE; ++i)
+ {
+ if(itimer[i].interval > 0 && (itimer[i].left -= delta) <= 0)
+ {
+ itimer[i].left = itimer[i].interval;
+ itimer[i].expire = TRUE;
+ DBGV("timerUpdate: timer %u expired\n", i);
+ }
+ }
+}
+
+void timerStop(UInteger16 index, IntervalTimer *itimer)
+{
+ if(index >= TIMER_ARRAY_SIZE)
+ return;
+
+ itimer[index].interval = 0;
+}
+
+void timerStart(UInteger16 index, UInteger16 interval, IntervalTimer *itimer)
+{
+ if(index >= TIMER_ARRAY_SIZE)
+ return;
+
+ itimer[index].expire = FALSE;
+ itimer[index].left = interval;
+ itimer[index].interval = itimer[index].left;
+
+ DBGV("timerStart: set timer %d to %d\n", index, interval);
+}
+
+Boolean timerExpired(UInteger16 index, IntervalTimer *itimer)
+{
+ timerUpdate(itimer);
+
+ if(index >= TIMER_ARRAY_SIZE)
+ return FALSE;
+
+ if(!itimer[index].expire)
+ return FALSE;
+
+ itimer[index].expire = FALSE;
+
+ return TRUE;
+}
+
diff --git a/third_party/ptpd-1.1.0/src/dep/constants_dep.h b/third_party/ptpd-1.1.0/src/dep/constants_dep.h new file mode 100644 index 0000000..54afaf1 --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep/constants_dep.h @@ -0,0 +1,99 @@ +/**
+ * @file constants_dep.h
+ * @date Tue Jul 20 16:16:23 2010
+ *
+ * @brief Platform dependent constants
+ *
+ *
+ */
+
+#ifndef CONSTANTS_DEP_H
+#define CONSTANTS_DEP_H
+
+#include <limits.h>
+
+#if !defined(linux) && !defined(__NetBSD__) && !defined(__FreeBSD__)
+#error Not ported to this architecture, please update.
+#endif
+
+#ifdef linux
+#include<netinet/in.h>
+#include<net/if.h>
+#include<net/if_arp.h>
+#define IFACE_NAME_LENGTH IF_NAMESIZE
+#define NET_ADDRESS_LENGTH INET_ADDRSTRLEN
+
+#define IFCONF_LENGTH 10
+
+#include<endian.h>
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define PTPD_LSBF
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define PTPD_MSBF
+#endif
+#endif /* linux */
+
+
+#if defined(__NetBSD__) || defined(__FreeBSD__)
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#if defined(__FreeBSD__)
+#include <net/ethernet.h>
+#include <sys/uio.h>
+#else
+#include <net/if_ether.h>
+#endif
+#include <ifaddrs.h>
+#define IFACE_NAME_LENGTH IF_NAMESIZE
+#define NET_ADDRESS_LENGTH INET_ADDRSTRLEN
+
+#define IFCONF_LENGTH 10
+
+#define adjtimex ntp_adjtime
+
+#include <machine/endian.h>
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define PTPD_LSBF
+#elif BYTE_ORDER == BIG_ENDIAN
+#define PTPD_MSBF
+#endif
+#endif
+
+
+#define ADJ_FREQ_MAX 512000
+
+/* UDP/IPv4 dependent */
+
+#define SUBDOMAIN_ADDRESS_LENGTH 4
+#define PORT_ADDRESS_LENGTH 2
+
+#define PACKET_SIZE 300
+
+#define PTP_EVENT_PORT 319
+#define PTP_GENERAL_PORT 320
+
+#define DEFAULT_PTP_DOMAIN_ADDRESS "224.0.1.129"
+#define ALTERNATE_PTP_DOMAIN1_ADDRESS "224.0.1.130"
+#define ALTERNATE_PTP_DOMAIN2_ADDRESS "224.0.1.131"
+#define ALTERNATE_PTP_DOMAIN3_ADDRESS "224.0.1.132"
+
+#define HEADER_LENGTH 40
+#define SYNC_PACKET_LENGTH 124
+#define DELAY_REQ_PACKET_LENGTH 124
+#define FOLLOW_UP_PACKET_LENGTH 52
+#define DELAY_RESP_PACKET_LENGTH 60
+#define MANAGEMENT_PACKET_LENGTH 136
+
+#define MM_STARTING_BOUNDARY_HOPS 0x7fff
+
+/* others */
+
+#define SCREEN_BUFSZ 128
+#define SCREEN_MAXSZ 80
+
+#define MAXTIMESTR 32
+#endif
diff --git a/third_party/ptpd-1.1.0/src/dep/datatypes_dep.h b/third_party/ptpd-1.1.0/src/dep/datatypes_dep.h new file mode 100644 index 0000000..b105130 --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep/datatypes_dep.h @@ -0,0 +1,37 @@ +/**
+ * @file datatypes_dep.h
+ * @date Tue Jul 20 16:16:48 2010
+ *
+ * @brief Platform dependent datatypes.
+ *
+ *
+ */
+
+#ifndef DATATYPES_DEP_H
+#define DATATYPES_DEP_H
+
+typedef enum {
+ FALSE = 0, TRUE
+} Boolean;
+typedef char Octet;
+typedef signed char Integer8;
+typedef signed short Integer16;
+typedef signed int Integer32;
+typedef unsigned char UInteger8;
+typedef unsigned short UInteger16;
+typedef unsigned int UInteger32;
+
+typedef struct {
+ Integer32 nsec_prev, y;
+} offset_from_master_filter;
+
+typedef struct {
+ Integer32 nsec_prev, y;
+ Integer32 s_exp;
+} one_way_delay_filter;
+
+typedef struct {
+ Integer32 eventSock, generalSock, multicastAddr, unicastAddr;
+} NetPath;
+
+#endif
diff --git a/third_party/ptpd-1.1.0/src/dep/msg.c b/third_party/ptpd-1.1.0/src/dep/msg.c new file mode 100644 index 0000000..cd2dbb9 --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep/msg.c @@ -0,0 +1,671 @@ +/**
+ * @file msg.c
+ * @author George Neville-Neil <gnn@neville-neil.com>
+ * @date Tue Jul 20 16:17:05 2010
+ *
+ * @brief Functions to pack and unpack messages.
+ *
+ * See spec annex d
+ */
+
+
+#include "../ptpd.h"
+
+Boolean
+msgPeek(void *buf, ssize_t length)
+{
+ /* not imlpemented yet */
+ return TRUE;
+}
+
+void
+msgUnpackHeader(void *buf, MsgHeader * header)
+{
+ header->versionPTP = flip16(*(UInteger16 *) (buf + 0));
+ header->versionNetwork = flip16(*(UInteger16 *) (buf + 2));
+ DBGV("msgUnpackHeader: versionPTP %d\n", header->versionPTP);
+ DBGV("msgUnpackHeader: versionNetwork %d\n", header->versionNetwork);
+
+ memcpy(header->subdomain, (buf + 4), 16);
+ DBGV("msgUnpackHeader: subdomain %s\n", header->subdomain);
+
+ header->messageType = *(UInteger8 *) (buf + 20);
+ header->sourceCommunicationTechnology = *(UInteger8 *) (buf + 21);
+ DBGV("msgUnpackHeader: messageType %d\n", header->messageType);
+ DBGV("msgUnpackHeader: sourceCommunicationTechnology %d\n", header->sourceCommunicationTechnology);
+
+ memcpy(header->sourceUuid, (buf + 22), 6);
+ DBGV("msgUnpackHeader: sourceUuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ header->sourceUuid[0], header->sourceUuid[1], header->sourceUuid[2],
+ header->sourceUuid[3], header->sourceUuid[4], header->sourceUuid[5]);
+
+ header->sourcePortId = flip16(*(UInteger16 *) (buf + 28));
+ header->sequenceId = flip16(*(UInteger16 *) (buf + 30));
+ DBGV("msgUnpackHeader: sourcePortId %d\n", header->sourcePortId);
+ DBGV("msgUnpackHeader: sequenceId %d\n", header->sequenceId);
+
+ header->control = *(UInteger8 *) (buf + 32);
+ DBGV("msgUnpackHeader: control %d\n", header->control);
+
+ memcpy(header->flags, (buf + 34), 2);
+ DBGV("msgUnpackHeader: flags %02hhx %02hhx\n", header->flags[0], header->flags[1]);
+}
+
+void
+msgUnpackSync(void *buf, MsgSync * sync)
+{
+ sync->originTimestamp.seconds = flip32(*(UInteger32 *) (buf + 40));
+ DBGV("msgUnpackSync: originTimestamp.seconds %u\n", sync->originTimestamp.seconds);
+ sync->originTimestamp.nanoseconds = flip32(*(Integer32 *) (buf + 44));
+ DBGV("msgUnpackSync: originTimestamp.nanoseconds %d\n", sync->originTimestamp.nanoseconds);
+ sync->epochNumber = flip16(*(UInteger16 *) (buf + 48));
+ DBGV("msgUnpackSync: epochNumber %d\n", sync->epochNumber);
+ sync->currentUTCOffset = flip16(*(Integer16 *) (buf + 50));
+ DBGV("msgUnpackSync: currentUTCOffset %d\n", sync->currentUTCOffset);
+ sync->grandmasterCommunicationTechnology = *(UInteger8 *) (buf + 53);
+ DBGV("msgUnpackSync: grandmasterCommunicationTechnology %d\n", sync->grandmasterCommunicationTechnology);
+ memcpy(sync->grandmasterClockUuid, (buf + 54), 6);
+ DBGV("msgUnpackSync: grandmasterClockUuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ sync->grandmasterClockUuid[0], sync->grandmasterClockUuid[1], sync->grandmasterClockUuid[2],
+ sync->grandmasterClockUuid[3], sync->grandmasterClockUuid[4], sync->grandmasterClockUuid[5]);
+ sync->grandmasterPortId = flip16(*(UInteger16 *) (buf + 60));
+ DBGV("msgUnpackSync: grandmasterPortId %d\n", sync->grandmasterPortId);
+ sync->grandmasterSequenceId = flip16(*(UInteger16 *) (buf + 62));
+ DBGV("msgUnpackSync: grandmasterSequenceId %d\n", sync->grandmasterSequenceId);
+ sync->grandmasterClockStratum = *(UInteger8 *) (buf + 67);
+ DBGV("msgUnpackSync: grandmasterClockStratum %d\n", sync->grandmasterClockStratum);
+ memcpy(sync->grandmasterClockIdentifier, (buf + 68), 4);
+ DBGV("msgUnpackSync: grandmasterClockIdentifier %c%c%c%c\n",
+ sync->grandmasterClockIdentifier[0], sync->grandmasterClockIdentifier[1],
+ sync->grandmasterClockIdentifier[2], sync->grandmasterClockIdentifier[3]);
+ sync->grandmasterClockVariance = flip16(*(Integer16 *) (buf + 74));
+ DBGV("msgUnpackSync: grandmasterClockVariance %d\n", sync->grandmasterClockVariance);
+ sync->grandmasterPreferred = *(UInteger8 *) (buf + 77);
+ DBGV("msgUnpackSync: grandmasterPreferred %d\n", sync->grandmasterPreferred);
+ sync->grandmasterIsBoundaryClock = *(UInteger8 *) (buf + 79);
+ DBGV("msgUnpackSync: grandmasterIsBoundaryClock %d\n", sync->grandmasterIsBoundaryClock);
+ sync->syncInterval = *(Integer8 *) (buf + 83);
+ DBGV("msgUnpackSync: syncInterval %d\n", sync->syncInterval);
+ sync->localClockVariance = flip16(*(Integer16 *) (buf + 86));
+ DBGV("msgUnpackSync: localClockVariance %d\n", sync->localClockVariance);
+ sync->localStepsRemoved = flip16(*(UInteger16 *) (buf + 90));
+ DBGV("msgUnpackSync: localStepsRemoved %d\n", sync->localStepsRemoved);
+ sync->localClockStratum = *(UInteger8 *) (buf + 95);
+ DBGV("msgUnpackSync: localClockStratum %d\n", sync->localClockStratum);
+ memcpy(sync->localClockIdentifer, (buf + 96), PTP_CODE_STRING_LENGTH);
+ DBGV("msgUnpackSync: localClockIdentifer %c%c%c%c\n",
+ sync->localClockIdentifer[0], sync->localClockIdentifer[1],
+ sync->localClockIdentifer[2], sync->localClockIdentifer[3]);
+ sync->parentCommunicationTechnology = *(UInteger8 *) (buf + 101);
+ DBGV("msgUnpackSync: parentCommunicationTechnology %d\n", sync->parentCommunicationTechnology);
+ memcpy(sync->parentUuid, (buf + 102), PTP_UUID_LENGTH);
+ DBGV("msgUnpackSync: parentUuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ sync->parentUuid[0], sync->parentUuid[1], sync->parentUuid[2],
+ sync->parentUuid[3], sync->parentUuid[4], sync->parentUuid[5]);
+ sync->parentPortField = flip16(*(UInteger16 *) (buf + 110));
+ DBGV("msgUnpackSync: parentPortField %d\n", sync->parentPortField);
+ sync->estimatedMasterVariance = flip16(*(Integer16 *) (buf + 114));
+ DBGV("msgUnpackSync: estimatedMasterVariance %d\n", sync->estimatedMasterVariance);
+ sync->estimatedMasterDrift = flip32(*(Integer32 *) (buf + 116));
+ DBGV("msgUnpackSync: estimatedMasterDrift %d\n", sync->estimatedMasterDrift);
+ sync->utcReasonable = *(UInteger8 *) (buf + 123);
+ DBGV("msgUnpackSync: utcReasonable %d\n", sync->utcReasonable);
+}
+
+void
+msgUnpackDelayReq(void *buf, MsgDelayReq * req)
+{
+}
+
+void
+msgUnpackFollowUp(void *buf, MsgFollowUp * follow)
+{
+ follow->associatedSequenceId = flip16(*(UInteger16 *) (buf + 42));
+ DBGV("msgUnpackFollowUp: associatedSequenceId %u\n", follow->associatedSequenceId);
+ follow->preciseOriginTimestamp.seconds = flip32(*(UInteger32 *) (buf + 44));
+ DBGV("msgUnpackFollowUp: preciseOriginTimestamp.seconds %u\n", follow->preciseOriginTimestamp.seconds);
+ follow->preciseOriginTimestamp.nanoseconds = flip32(*(Integer32 *) (buf + 48));
+ DBGV("msgUnpackFollowUp: preciseOriginTimestamp.nanoseconds %d\n", follow->preciseOriginTimestamp.nanoseconds);
+}
+
+void
+msgUnpackDelayResp(void *buf, MsgDelayResp * resp)
+{
+ resp->delayReceiptTimestamp.seconds = flip32(*(UInteger32 *) (buf + 40));
+ DBGV("msgUnpackDelayResp: delayReceiptTimestamp.seconds %u\n", resp->delayReceiptTimestamp.seconds);
+ resp->delayReceiptTimestamp.nanoseconds = flip32(*(Integer32 *) (buf + 44));
+ DBGV("msgUnpackDelayResp: delayReceiptTimestamp.nanoseconds %d\n", resp->delayReceiptTimestamp.nanoseconds);
+ resp->requestingSourceCommunicationTechnology = *(UInteger8 *) (buf + 49);
+ DBGV("msgUnpackDelayResp: requestingSourceCommunicationTechnology %d\n", resp->requestingSourceCommunicationTechnology);
+ memcpy(resp->requestingSourceUuid, (buf + 50), 6);
+ DBGV("msgUnpackDelayResp: requestingSourceUuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ resp->requestingSourceUuid[0], resp->requestingSourceUuid[1], resp->requestingSourceUuid[2],
+ resp->requestingSourceUuid[3], resp->requestingSourceUuid[4], resp->requestingSourceUuid[5]);
+ resp->requestingSourcePortId = flip16(*(UInteger16 *) (buf + 56));
+ DBGV("msgUnpackDelayResp: requestingSourcePortId %d\n", resp->requestingSourcePortId);
+ resp->requestingSourceSequenceId = flip16(*(UInteger16 *) (buf + 58));
+ DBGV("msgUnpackDelayResp: requestingSourceSequenceId %d\n", resp->requestingSourceSequenceId);
+}
+
+void
+msgUnpackManagement(void *buf, MsgManagement * manage)
+{
+ manage->targetCommunicationTechnology = *(UInteger8 *) (buf + 41);
+ DBGV("msgUnpackManagement: targetCommunicationTechnology %d\n", manage->targetCommunicationTechnology);
+ memcpy(manage->targetUuid, (buf + 42), 6);
+ DBGV("msgUnpackManagement: targetUuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ manage->targetUuid[0], manage->targetUuid[1], manage->targetUuid[2],
+ manage->targetUuid[3], manage->targetUuid[4], manage->targetUuid[5]);
+ manage->targetPortId = flip16(*(UInteger16 *) (buf + 48));
+ DBGV("msgUnpackManagement: targetPortId %d\n", manage->targetPortId);
+ manage->startingBoundaryHops = flip16(*(Integer16 *) (buf + 50));
+ DBGV("msgUnpackManagement: startingBoundaryHops %d\n", manage->startingBoundaryHops);
+ manage->boundaryHops = flip16(*(Integer16 *) (buf + 52));
+ DBGV("msgUnpackManagement: boundaryHops %d\n", manage->boundaryHops);
+ manage->managementMessageKey = *(UInteger8 *) (buf + 55);
+ DBGV("msgUnpackManagement: managementMessageKey %d\n", manage->managementMessageKey);
+ manage->parameterLength = flip16(*(UInteger16 *) (buf + 58));
+ DBGV("msgUnpackManagement: parameterLength %d\n", manage->parameterLength);
+
+ if (manage->managementMessageKey == PTP_MM_GET_FOREIGN_DATA_SET)
+ manage->recordKey = flip16(*(UInteger16 *) (buf + 62));
+}
+
+UInteger8
+msgUnloadManagement(void *buf, MsgManagement * manage,
+ PtpClock * ptpClock, RunTimeOpts * rtOpts)
+{
+ TimeInternal internalTime;
+ TimeRepresentation externalTime;
+
+ switch (manage->managementMessageKey) {
+ case PTP_MM_INITIALIZE_CLOCK:
+ if (ptpClock->initializable)
+ return PTP_INITIALIZING;
+ break;
+
+ case PTP_MM_GOTO_FAULTY_STATE:
+ DBG("event FAULT_DETECTED (forced by management message)\n");
+ return PTP_FAULTY;
+ break;
+
+ case PTP_MM_DISABLE_PORT:
+ if (manage->targetPortId == 1) {
+ DBG("event DESIGNATED_DISABLED\n");
+ return PTP_DISABLED;
+ }
+ break;
+
+ case PTP_MM_ENABLE_PORT:
+ if (manage->targetPortId == 1) {
+ DBG("event DESIGNATED_ENABLED\n");
+ return PTP_INITIALIZING;
+ }
+ break;
+
+ case PTP_MM_CLEAR_DESIGNATED_PREFERRED_MASTER:
+ ptpClock->preferred = FALSE;
+ break;
+
+ case PTP_MM_SET_DESIGNATED_PREFERRED_MASTER:
+ ptpClock->preferred = TRUE;
+ break;
+
+ case PTP_MM_DISABLE_BURST:
+ break;
+
+ case PTP_MM_ENABLE_BURST:
+ break;
+
+ case PTP_MM_SET_SYNC_INTERVAL:
+ rtOpts->syncInterval = *(Integer8 *) (buf + 63);
+ break;
+
+ case PTP_MM_SET_SUBDOMAIN:
+ memcpy(rtOpts->subdomainName, buf + 60, 16);
+ DBG("set subdomain to %s\n", rtOpts->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);
+ break;
+
+ case PTP_MM_UPDATE_DEFAULT_DATA_SET:
+ if (!rtOpts->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);
+ break;
+
+ case PTP_MM_UPDATE_GLOBAL_TIME_PROPERTIES:
+ ptpClock->current_utc_offset = flip16(*(Integer16 *) (buf + 62));
+ ptpClock->leap_59 = *(UInteger8 *) (buf + 67);
+ ptpClock->leap_61 = *(UInteger8 *) (buf + 71);
+ ptpClock->epoch_number = flip16(*(UInteger16 *) (buf + 74));
+ break;
+
+ default:
+ break;
+ }
+
+ return ptpClock->port_state;
+}
+
+void
+msgUnpackManagementPayload(void *buf, MsgManagement * manage)
+{
+ switch (manage->managementMessageKey) {
+ case PTP_MM_CLOCK_IDENTITY:
+ DBGV("msgUnloadManagementPayload: managementMessageKey PTP_MM_CLOCK_IDENTITY\n");
+ manage->payload.clockIdentity.clockCommunicationTechnology = *(UInteger8 *) (buf + 63);
+ memcpy(manage->payload.clockIdentity.clockUuidField, buf + 64, PTP_UUID_LENGTH);
+ manage->payload.clockIdentity.clockPortField = flip16(*(UInteger16 *) (buf + 74));
+ memcpy(manage->payload.clockIdentity.manufacturerIdentity, buf + 76, MANUFACTURER_ID_LENGTH);
+ break;
+
+ case PTP_MM_DEFAULT_DATA_SET:
+ DBGV("msgUnloadManagementPayload: managementMessageKey PTP_MM_DEFAULT_DATA_SET\n");
+ manage->payload.defaultData.clockCommunicationTechnology = *(UInteger8 *) (buf + 63);
+ memcpy(manage->payload.defaultData.clockUuidField, buf + 64, PTP_UUID_LENGTH);
+ manage->payload.defaultData.clockPortField = flip16(*(UInteger16 *) (buf + 74));
+ manage->payload.defaultData.clockStratum = *(UInteger16 *) (buf + 79);
+ memcpy(manage->payload.defaultData.clockIdentifier, buf + 80, PTP_CODE_STRING_LENGTH);
+ manage->payload.defaultData.clockVariance = flip16(*(UInteger16 *) (buf + 86));
+ manage->payload.defaultData.clockFollowupCapable = *(UInteger8 *) (buf + 91);
+ manage->payload.defaultData.preferred = *(UInteger8 *) (buf + 95);
+ manage->payload.defaultData.initializable = *(UInteger8 *) (buf + 99);
+ manage->payload.defaultData.externalTiming = *(UInteger8 *) (buf + 103);
+ manage->payload.defaultData.isBoundaryClock = *(UInteger8 *) (buf + 107);
+ manage->payload.defaultData.syncInterval = *(UInteger8 *) (buf + 111);
+ memcpy(manage->payload.defaultData.subdomainName, buf + 112, PTP_SUBDOMAIN_NAME_LENGTH);
+ manage->payload.defaultData.numberPorts = flip16(*(UInteger16 *) (buf + 130));
+ manage->payload.defaultData.numberForeignRecords = flip16(*(UInteger16 *) (buf + 134));
+ break;
+
+ case PTP_MM_CURRENT_DATA_SET:
+ DBGV("msgUnloadManagementPayload: managementMessageKey PTP_MM_CURRENT_DATA_SET\n");
+ manage->payload.current.stepsRemoved = flip16(*(UInteger16 *) (buf + 62));
+ manage->payload.current.offsetFromMaster.seconds = flip32(*(UInteger32 *) (buf + 64));
+ manage->payload.current.offsetFromMaster.nanoseconds = flip32(*(UInteger32 *) (buf + 68));
+ manage->payload.current.oneWayDelay.seconds = flip32(*(UInteger32 *) (buf + 72));
+ manage->payload.current.oneWayDelay.nanoseconds = flip32(*(Integer32 *) (buf + 76));
+ break;
+
+ case PTP_MM_PARENT_DATA_SET:
+ DBGV("msgUnloadManagementPayload: managementMessageKey PTP_MM_PORT_DATA_SET\n");
+ manage->payload.parent.parentCommunicationTechnology = *(UInteger8 *) (buf + 63);
+ memcpy(manage->payload.parent.parentUuid, buf + 64, PTP_UUID_LENGTH);
+ manage->payload.parent.parentPortId = flip16(*(UInteger16 *) (buf + 74));
+ manage->payload.parent.parentLastSyncSequenceNumber = flip16(*(UInteger16 *) (buf + 74));
+ manage->payload.parent.parentFollowupCapable = *(UInteger8 *) (buf + 83);
+ manage->payload.parent.parentExternalTiming = *(UInteger8 *) (buf + 87);
+ manage->payload.parent.parentVariance = flip16(*(UInteger16 *) (buf + 90));
+ manage->payload.parent.parentStats = *(UInteger8 *) (buf + 85);
+ manage->payload.parent.observedVariance = flip16(*(Integer16 *) (buf + 98));
+ manage->payload.parent.observedDrift = flip32(*(Integer32 *) (buf + 100));
+ manage->payload.parent.utcReasonable = *(UInteger8 *) (buf + 107);
+ manage->payload.parent.grandmasterCommunicationTechnology = *(UInteger8 *) (buf + 111);
+ memcpy(manage->payload.parent.grandmasterUuidField, buf + 112, PTP_UUID_LENGTH);
+ manage->payload.parent.grandmasterPortIdField = flip16(*(UInteger16 *) (buf + 122));
+ manage->payload.parent.grandmasterStratum = *(UInteger8 *) (buf + 127);
+ memcpy(manage->payload.parent.grandmasterIdentifier, buf + 128, PTP_CODE_STRING_LENGTH);
+ manage->payload.parent.grandmasterVariance = flip16(*(Integer16 *) (buf + 134));
+ manage->payload.parent.grandmasterPreferred = *(UInteger8 *) (buf + 139);
+ manage->payload.parent.grandmasterIsBoundaryClock = *(UInteger8 *) (buf + 144);
+ manage->payload.parent.grandmasterSequenceNumber = flip16(*(UInteger16 *) (buf + 146));
+ break;
+
+ case PTP_MM_PORT_DATA_SET:
+ DBGV("msgUnloadManagementPayload: managementMessageKey PTP_MM_FOREIGN_DATA_SET\n");
+ manage->payload.port.returnedPortNumber = flip16(*(UInteger16 *) (buf + 62));
+ manage->payload.port.portState = *(UInteger8 *) (buf + 67);
+ manage->payload.port.lastSyncEventSequenceNumber = flip16(*(UInteger16 *) (buf + 70));
+ manage->payload.port.lastGeneralEventSequenceNumber = flip16(*(UInteger16 *) (buf + 74));
+ manage->payload.port.portCommunicationTechnology = *(UInteger8 *) (buf + 79);
+ memcpy(manage->payload.port.portUuidField, buf + 80, PTP_UUID_LENGTH);
+ manage->payload.port.portIdField = flip16(*(UInteger16 *) (buf + 90));
+ manage->payload.port.burstEnabled = *(UInteger8 *) (buf + 95);
+ manage->payload.port.subdomainAddressOctets = *(UInteger8 *) (buf + 97);
+ manage->payload.port.eventPortAddressOctets = *(UInteger8 *) (buf + 98);
+ manage->payload.port.generalPortAddressOctets = *(UInteger8 *) (buf + 99);
+ memcpy(manage->payload.port.subdomainAddress, buf + 100, SUBDOMAIN_ADDRESS_LENGTH);
+ memcpy(manage->payload.port.eventPortAddress, buf + 106, PORT_ADDRESS_LENGTH);
+ memcpy(manage->payload.port.generalPortAddress, buf + 110, PORT_ADDRESS_LENGTH);
+ break;
+
+ case PTP_MM_GLOBAL_TIME_DATA_SET:
+ DBGV("msgUnloadManagementPayload: managementMessageKey PTP_MM_GLOBAL_TIME_DATA_SET\n");
+ manage->payload.globalTime.localTime.seconds = flip32(*(UInteger32 *) (buf + 60));
+ manage->payload.globalTime.localTime.nanoseconds = flip32(*(Integer32 *) (buf + 64));
+ manage->payload.globalTime.currentUtcOffset = flip16(*(Integer16 *) (buf + 70));
+ manage->payload.globalTime.leap59 = *(UInteger8 *) (buf + 75);
+ manage->payload.globalTime.leap61 = *(UInteger8 *) (buf + 79);
+ manage->payload.globalTime.epochNumber = flip16(*(UInteger16 *) (buf + 82));
+ break;
+
+
+ case PTP_MM_FOREIGN_DATA_SET:
+ DBGV("msgUnloadManagementPayload: managementMessageKey PTP_MM_FOREIGN_DATA_SET\n");
+ manage->payload.foreign.returnedPortNumber = flip16(*(UInteger16 *) (buf + 62));
+ manage->payload.foreign.returnedRecordNumber = flip16(*(UInteger16 *) (buf + 68));
+ manage->payload.foreign.foreignMasterCommunicationTechnology = *(UInteger8 *) (buf + 71);
+ memcpy(manage->payload.foreign.foreignMasterUuid, buf + 72, PTP_UUID_LENGTH);
+ manage->payload.foreign.foreignMasterPortId = flip16(*(UInteger16 *) (buf + 82));
+ manage->payload.foreign.foreignMasterSyncs = flip16(*(UInteger16 *) (buf + 66));
+ break;
+
+ case PTP_MM_NULL:
+ DBGV("msgUnloadManagementPayload: managementMessageKey NULL\n");
+ break;
+
+ default:
+ DBGV("msgUnloadManagementPayload: managementMessageKey ?\n");
+ break;
+ }
+
+ return;
+}
+
+void
+msgPackHeader(void *buf, PtpClock * ptpClock)
+{
+ *(Integer32 *) (buf + 0) = shift16(flip16(VERSION_PTP), 0) | shift16(flip16(VERSION_NETWORK), 1);
+ memcpy((buf + 4), ptpClock->subdomain_name, 16);
+ *(Integer32 *) (buf + 20) = shift8(ptpClock->port_communication_technology, 1);
+ memcpy((buf + 22), ptpClock->port_uuid_field, 6);
+
+ if (ptpClock->external_timing)
+ setFlag((buf + 34), PTP_EXT_SYNC);
+ if (ptpClock->clock_followup_capable)
+ setFlag((buf + 34), PTP_ASSIST);
+ if (ptpClock->is_boundary_clock)
+ setFlag((buf + 34), PTP_BOUNDARY_CLOCK);
+}
+
+void
+msgPackSync(void *buf, Boolean burst,
+ TimeRepresentation * originTimestamp, PtpClock * ptpClock)
+{
+ *(UInteger8 *) (buf + 20) = 1; /* messageType */
+ *(Integer32 *) (buf + 28) = shift16(flip16(ptpClock->port_id_field), 0) | shift16(flip16(ptpClock->last_sync_event_sequence_number), 1);
+ *(UInteger8 *) (buf + 32) = PTP_SYNC_MESSAGE; /* control */
+ if (ptpClock->burst_enabled && burst)
+ setFlag((buf + 34), PTP_SYNC_BURST);
+ else
+ clearFlag((buf + 34), PTP_SYNC_BURST);
+ if (ptpClock->parent_stats)
+ setFlag((buf + 34), PARENT_STATS);
+ else
+ clearFlag((buf + 34), PARENT_STATS);
+
+
+ *(Integer32 *) (buf + 40) = flip32(originTimestamp->seconds);
+ *(Integer32 *) (buf + 44) = flip32(originTimestamp->nanoseconds);
+ *(Integer32 *) (buf + 48) = shift16(flip16(ptpClock->epoch_number), 0) | shift16(flip16(ptpClock->current_utc_offset), 1);
+ *(Integer32 *) (buf + 52) = shift8(ptpClock->grandmaster_communication_technology, 1);
+ memcpy((buf + 54), ptpClock->grandmaster_uuid_field, 6);
+ *(Integer32 *) (buf + 60) = shift16(flip16(ptpClock->grandmaster_port_id_field), 0) | shift16(flip16(ptpClock->grandmaster_sequence_number), 1);
+ *(Integer32 *) (buf + 64) = shift8(ptpClock->grandmaster_stratum, 3);
+ memcpy((buf + 68), ptpClock->grandmaster_identifier, 4);
+ *(Integer32 *) (buf + 72) = shift16(flip16(ptpClock->grandmaster_variance), 1);
+ *(Integer32 *) (buf + 76) = shift16(flip16(ptpClock->grandmaster_preferred), 0) | shift16(flip16(ptpClock->grandmaster_is_boundary_clock), 1);
+ *(Integer32 *) (buf + 80) = shift16(flip16(ptpClock->sync_interval), 1);
+ *(Integer32 *) (buf + 84) = shift16(flip16(ptpClock->clock_variance), 1);
+ *(Integer32 *) (buf + 88) = shift16(flip16(ptpClock->steps_removed), 1);
+ *(Integer32 *) (buf + 92) = shift8(ptpClock->clock_stratum, 3);
+ memcpy((buf + 96), ptpClock->clock_identifier, 4);
+ *(Integer32 *) (buf + 100) = shift8(ptpClock->parent_communication_technology, 1);
+ memcpy((buf + 102), ptpClock->parent_uuid, 6);
+ *(Integer32 *) (buf + 108) = shift16(flip16(ptpClock->parent_port_id), 1);
+ *(Integer32 *) (buf + 112) = shift16(flip16(ptpClock->observed_variance), 1);
+ *(Integer32 *) (buf + 116) = flip32(ptpClock->observed_drift);
+ *(Integer32 *) (buf + 120) = shift8(ptpClock->utc_reasonable, 3);
+}
+
+void
+msgPackDelayReq(void *buf, Boolean burst,
+ TimeRepresentation * originTimestamp, PtpClock * ptpClock)
+{
+ *(UInteger8 *) (buf + 20) = 1; /* messageType */
+ *(Integer32 *) (buf + 28) = shift16(flip16(ptpClock->port_id_field), 0) | shift16(flip16(ptpClock->last_sync_event_sequence_number), 1);
+ *(UInteger8 *) (buf + 32) = PTP_DELAY_REQ_MESSAGE; /* control */
+ if (ptpClock->burst_enabled && burst)
+ setFlag((buf + 34), PTP_SYNC_BURST);
+ else
+ clearFlag((buf + 34), PTP_SYNC_BURST);
+ if (ptpClock->parent_stats)
+ setFlag((buf + 34), PARENT_STATS);
+ else
+ clearFlag((buf + 34), PARENT_STATS);
+
+ *(Integer32 *) (buf + 40) = flip32(originTimestamp->seconds);
+ *(Integer32 *) (buf + 44) = flip32(originTimestamp->nanoseconds);
+ *(Integer32 *) (buf + 48) = shift16(flip16(ptpClock->epoch_number), 0) | shift16(flip16(ptpClock->current_utc_offset), 1);
+ *(Integer32 *) (buf + 52) = shift8(ptpClock->grandmaster_communication_technology, 1);
+ memcpy((buf + 54), ptpClock->grandmaster_uuid_field, 6);
+ *(Integer32 *) (buf + 60) = shift16(flip16(ptpClock->grandmaster_port_id_field), 0) | shift16(flip16(ptpClock->grandmaster_sequence_number), 1);
+ *(Integer32 *) (buf + 64) = shift8(ptpClock->grandmaster_stratum, 3);
+ memcpy((buf + 68), ptpClock->grandmaster_identifier, 4);
+ *(Integer32 *) (buf + 72) = shift16(flip16(ptpClock->grandmaster_variance), 1);
+ *(Integer32 *) (buf + 76) = shift16(flip16(ptpClock->grandmaster_preferred), 0) | shift16(flip16(ptpClock->grandmaster_is_boundary_clock), 1);
+ *(Integer32 *) (buf + 80) = shift16(flip16(ptpClock->sync_interval), 1);
+ *(Integer32 *) (buf + 84) = shift16(flip16(ptpClock->clock_variance), 1);
+ *(Integer32 *) (buf + 88) = shift16(flip16(ptpClock->steps_removed), 1);
+ *(Integer32 *) (buf + 92) = shift8(ptpClock->clock_stratum, 3);
+ memcpy((buf + 96), ptpClock->clock_identifier, 4);
+ *(Integer32 *) (buf + 100) = shift8(ptpClock->parent_communication_technology, 1);
+ memcpy((buf + 102), ptpClock->parent_uuid, 6);
+ *(Integer32 *) (buf + 108) = shift16(flip16(ptpClock->parent_port_id), 1);
+ *(Integer32 *) (buf + 112) = shift16(flip16(ptpClock->observed_variance), 1);
+ *(Integer32 *) (buf + 116) = flip32(ptpClock->observed_drift);
+ *(Integer32 *) (buf + 120) = shift8(ptpClock->utc_reasonable, 3);
+}
+
+void
+msgPackFollowUp(void *buf, UInteger16 associatedSequenceId,
+ TimeRepresentation * preciseOriginTimestamp, PtpClock * ptpClock)
+{
+ *(UInteger8 *) (buf + 20) = 2; /* messageType */
+ *(Integer32 *) (buf + 28) = shift16(flip16(ptpClock->port_id_field), 0) | shift16(flip16(ptpClock->last_general_event_sequence_number), 1);
+ *(UInteger8 *) (buf + 32) = PTP_FOLLOWUP_MESSAGE; /* control */
+ clearFlag((buf + 34), PTP_SYNC_BURST);
+ clearFlag((buf + 34), PARENT_STATS);
+
+ *(Integer32 *) (buf + 40) = shift16(flip16(associatedSequenceId), 1);
+ *(Integer32 *) (buf + 44) = flip32(preciseOriginTimestamp->seconds);
+ *(Integer32 *) (buf + 48) = flip32(preciseOriginTimestamp->nanoseconds);
+}
+
+void
+msgPackDelayResp(void *buf, MsgHeader * header,
+ TimeRepresentation * delayReceiptTimestamp, PtpClock * ptpClock)
+{
+ *(UInteger8 *) (buf + 20) = 2; /* messageType */
+ *(Integer32 *) (buf + 28) = shift16(flip16(ptpClock->port_id_field), 0) | shift16(flip16(ptpClock->last_general_event_sequence_number), 1);
+ *(UInteger8 *) (buf + 32) = PTP_DELAY_RESP_MESSAGE; /* control */
+ clearFlag((buf + 34), PTP_SYNC_BURST);
+ clearFlag((buf + 34), PARENT_STATS);
+
+ *(Integer32 *) (buf + 40) = flip32(delayReceiptTimestamp->seconds);
+ *(Integer32 *) (buf + 44) = flip32(delayReceiptTimestamp->nanoseconds);
+ *(Integer32 *) (buf + 48) = shift8(header->sourceCommunicationTechnology, 1);
+ memcpy(buf + 50, header->sourceUuid, 6);
+ *(Integer32 *) (buf + 56) = shift16(flip16(header->sourcePortId), 0) | shift16(flip16(header->sequenceId), 1);
+}
+
+UInteger16
+msgPackManagement(void *buf, MsgManagement * manage, PtpClock * ptpClock)
+{
+ *(UInteger8 *) (buf + 20) = 2; /* messageType */
+ *(Integer32 *) (buf + 28) = shift16(flip16(ptpClock->port_id_field), 0) | shift16(flip16(ptpClock->last_general_event_sequence_number), 1);
+ *(UInteger8 *) (buf + 32) = PTP_MANAGEMENT_MESSAGE; /* control */
+ clearFlag((buf + 34), PTP_SYNC_BURST);
+ clearFlag((buf + 34), PARENT_STATS);
+ *(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);
+ *(Integer32 *) (buf + 52) = shift16(flip16(MM_STARTING_BOUNDARY_HOPS), 0);
+
+ *(UInteger8 *) (buf + 55) = manage->managementMessageKey;
+
+ switch (manage->managementMessageKey) {
+ case PTP_MM_GET_FOREIGN_DATA_SET:
+ *(UInteger16 *) (buf + 62) = manage->recordKey;
+ *(Integer32 *) (buf + 56) = shift16(flip16(4), 1);
+ return 64;
+
+ default:
+ *(Integer32 *) (buf + 56) = shift16(flip16(0), 1);
+ return 60;
+ }
+}
+
+UInteger16
+msgPackManagementResponse(void *buf, MsgHeader * header, MsgManagement * manage, PtpClock * ptpClock)
+{
+ TimeInternal internalTime;
+ TimeRepresentation externalTime;
+
+ *(UInteger8 *) (buf + 20) = 2; /* messageType */
+ *(Integer32 *) (buf + 28) = shift16(flip16(ptpClock->port_id_field), 0) | shift16(flip16(ptpClock->last_general_event_sequence_number), 1);
+ *(UInteger8 *) (buf + 32) = PTP_MANAGEMENT_MESSAGE; /* control */
+ clearFlag((buf + 34), PTP_SYNC_BURST);
+ clearFlag((buf + 34), PARENT_STATS);
+ *(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);
+ *(Integer32 *) (buf + 52) = shift16(flip16(manage->startingBoundaryHops - manage->boundaryHops + 1), 0);
+
+ switch (manage->managementMessageKey) {
+ case PTP_MM_OBTAIN_IDENTITY:
+ *(UInteger8 *) (buf + 55) = PTP_MM_CLOCK_IDENTITY;
+ *(Integer32 *) (buf + 56) = shift16(flip16(64), 1);
+ *(Integer32 *) (buf + 60) = shift8(ptpClock->clock_communication_technology, 3);
+ memcpy(buf + 64, ptpClock->clock_uuid_field, 6);
+ *(Integer32 *) (buf + 72) = shift16(flip16(ptpClock->clock_port_id_field), 1);
+ memcpy((buf + 76), MANUFACTURER_ID, 48);
+ return 124;
+
+ case PTP_MM_GET_DEFAULT_DATA_SET:
+ *(UInteger8 *) (buf + 55) = PTP_MM_DEFAULT_DATA_SET;
+ *(Integer32 *) (buf + 56) = shift16(flip16(76), 1);
+ *(Integer32 *) (buf + 60) = shift8(ptpClock->clock_communication_technology, 3);
+ memcpy(buf + 64, ptpClock->clock_uuid_field, 6);
+ *(Integer32 *) (buf + 72) = shift16(flip16(ptpClock->clock_port_id_field), 1);
+ *(Integer32 *) (buf + 76) = shift8(ptpClock->clock_stratum, 3);
+ memcpy(buf + 80, ptpClock->clock_identifier, 4);
+ *(Integer32 *) (buf + 84) = shift16(flip16(ptpClock->clock_variance), 1);
+ *(Integer32 *) (buf + 88) = shift8(ptpClock->clock_followup_capable, 3);
+ *(Integer32 *) (buf + 92) = shift8(ptpClock->preferred, 3);
+ *(Integer32 *) (buf + 96) = shift8(ptpClock->initializable, 3);
+ *(Integer32 *) (buf + 100) = shift8(ptpClock->external_timing, 3);
+ *(Integer32 *) (buf + 104) = shift8(ptpClock->is_boundary_clock, 3);
+ *(Integer32 *) (buf + 108) = shift8(ptpClock->sync_interval, 3);
+ memcpy(buf + 112, ptpClock->subdomain_name, 16);
+ *(Integer32 *) (buf + 128) = shift16(flip16(ptpClock->number_ports), 1);
+ *(Integer32 *) (buf + 132) = shift16(flip16(ptpClock->number_foreign_records), 1);
+ return 136;
+
+ case PTP_MM_GET_CURRENT_DATA_SET:
+ *(UInteger8 *) (buf + 55) = PTP_MM_CURRENT_DATA_SET;
+ *(Integer32 *) (buf + 56) = shift16(flip16(20), 1);
+ *(Integer32 *) (buf + 60) = shift16(flip16(ptpClock->steps_removed), 1);
+
+ fromInternalTime(&ptpClock->offset_from_master, &externalTime, 0);
+ *(Integer32 *) (buf + 64) = flip32(externalTime.seconds);
+ *(Integer32 *) (buf + 68) = flip32(externalTime.nanoseconds);
+
+ fromInternalTime(&ptpClock->one_way_delay, &externalTime, 0);
+ *(Integer32 *) (buf + 72) = flip32(externalTime.seconds);
+ *(Integer32 *) (buf + 76) = flip32(externalTime.nanoseconds);
+ return 80;
+
+ case PTP_MM_GET_PARENT_DATA_SET:
+ *(UInteger8 *) (buf + 55) = PTP_MM_PARENT_DATA_SET;
+ *(Integer32 *) (buf + 56) = shift16(flip16(90), 1);
+ *(Integer32 *) (buf + 60) = shift8(ptpClock->parent_communication_technology, 3);
+ memcpy(buf + 64, ptpClock->parent_uuid, 6);
+ *(Integer32 *) (buf + 72) = shift16(flip16(ptpClock->parent_port_id), 1);
+ *(Integer32 *) (buf + 76) = shift16(flip16(ptpClock->parent_last_sync_sequence_number), 1);
+ *(Integer32 *) (buf + 80) = shift8(ptpClock->parent_followup_capable, 1);
+ *(Integer32 *) (buf + 84) = shift8(ptpClock->parent_external_timing, 3);
+ *(Integer32 *) (buf + 88) = shift16(flip16(ptpClock->parent_variance), 1);
+ *(Integer32 *) (buf + 92) = shift8(ptpClock->parent_stats, 3);
+ *(Integer32 *) (buf + 96) = shift16(flip16(ptpClock->observed_variance), 1);
+ *(Integer32 *) (buf + 100) = flip32(ptpClock->observed_drift);
+ *(Integer32 *) (buf + 104) = shift8(ptpClock->utc_reasonable, 3);
+ *(Integer32 *) (buf + 108) = shift8(ptpClock->grandmaster_communication_technology, 3);
+ memcpy(buf + 112, ptpClock->grandmaster_uuid_field, 6);
+ *(Integer32 *) (buf + 120) = shift16(flip16(ptpClock->grandmaster_port_id_field), 1);
+ *(Integer32 *) (buf + 124) = shift8(ptpClock->grandmaster_stratum, 3);
+ memcpy(buf + 128, ptpClock->grandmaster_identifier, 4);
+ *(Integer32 *) (buf + 132) = shift16(flip16(ptpClock->grandmaster_variance), 1);
+ *(Integer32 *) (buf + 136) = shift8(ptpClock->grandmaster_preferred, 3);
+ *(Integer32 *) (buf + 140) = shift8(ptpClock->grandmaster_is_boundary_clock, 3);
+ *(Integer32 *) (buf + 144) = shift16(flip16(ptpClock->grandmaster_sequence_number), 1);
+ return 148;
+
+ case PTP_MM_GET_PORT_DATA_SET:
+ if (manage->targetPortId && manage->targetPortId != ptpClock->port_id_field) {
+ *(UInteger8 *) (buf + 55) = PTP_MM_NULL;
+ *(Integer32 *) (buf + 56) = shift16(flip16(0), 1);
+ return 0;
+ }
+ *(UInteger8 *) (buf + 55) = PTP_MM_PORT_DATA_SET;
+ *(Integer32 *) (buf + 56) = shift16(flip16(52), 1);
+ *(Integer32 *) (buf + 60) = shift16(flip16(ptpClock->port_id_field), 1);
+ *(Integer32 *) (buf + 64) = shift8(ptpClock->port_state, 3);
+ *(Integer32 *) (buf + 68) = shift16(flip16(ptpClock->last_sync_event_sequence_number), 1);
+ *(Integer32 *) (buf + 72) = shift16(flip16(ptpClock->last_general_event_sequence_number), 1);
+ *(Integer32 *) (buf + 76) = shift8(ptpClock->port_communication_technology, 3);
+ memcpy(buf + 80, ptpClock->port_uuid_field, 6);
+ *(Integer32 *) (buf + 88) = shift16(flip16(ptpClock->port_id_field), 1);
+ *(Integer32 *) (buf + 92) = shift8(ptpClock->burst_enabled, 3);
+ *(Integer32 *) (buf + 96) = shift8(4, 1) | shift8(2, 2) | shift8(2, 3);
+ memcpy(buf + 100, ptpClock->subdomain_address, 4);
+ memcpy(buf + 106, ptpClock->event_port_address, 2);
+ memcpy(buf + 110, ptpClock->general_port_address, 2);
+ return 112;
+
+ case PTP_MM_GET_GLOBAL_TIME_DATA_SET:
+ *(UInteger8 *) (buf + 55) = PTP_MM_GLOBAL_TIME_DATA_SET;
+ *(Integer32 *) (buf + 56) = shift16(flip16(24), 1);
+
+ getTime(&internalTime);
+ fromInternalTime(&internalTime, &externalTime, ptpClock->halfEpoch);
+ *(Integer32 *) (buf + 60) = flip32(externalTime.seconds);
+ *(Integer32 *) (buf + 64) = flip32(externalTime.nanoseconds);
+
+ *(Integer32 *) (buf + 68) = shift16(flip16(ptpClock->current_utc_offset), 1);
+ *(Integer32 *) (buf + 72) = shift8(ptpClock->leap_59, 3);
+ *(Integer32 *) (buf + 76) = shift8(ptpClock->leap_61, 3);
+ *(Integer32 *) (buf + 80) = shift16(flip16(ptpClock->epoch_number), 1);
+ return 84;
+
+ case PTP_MM_GET_FOREIGN_DATA_SET:
+ if ((manage->targetPortId && manage->targetPortId != ptpClock->port_id_field)
+ || !manage->recordKey || manage->recordKey > ptpClock->number_foreign_records) {
+ *(UInteger8 *) (buf + 55) = PTP_MM_NULL;
+ *(Integer32 *) (buf + 56) = shift16(flip16(0), 1);
+ return 0;
+ }
+ *(UInteger8 *) (buf + 55) = PTP_MM_FOREIGN_DATA_SET;
+ *(Integer32 *) (buf + 56) = shift16(flip16(28), 1);
+ *(Integer32 *) (buf + 60) = shift16(flip16(ptpClock->port_id_field), 1);
+ *(Integer32 *) (buf + 64) = shift16(flip16(manage->recordKey - 1), 1);
+ *(Integer32 *) (buf + 68) = shift8(ptpClock->foreign[manage->recordKey - 1].foreign_master_communication_technology, 3);
+ memcpy(buf + 72, ptpClock->foreign[manage->recordKey - 1].foreign_master_uuid, 6);
+ *(Integer32 *) (buf + 80) = shift16(flip16(ptpClock->foreign[manage->recordKey - 1].foreign_master_port_id), 1);
+ *(Integer32 *) (buf + 84) = shift16(flip16(ptpClock->foreign[manage->recordKey - 1].foreign_master_syncs), 1);
+ return 88;
+
+ default:
+ return 0;
+ }
+}
diff --git a/third_party/ptpd-1.1.0/src/dep/net.c b/third_party/ptpd-1.1.0/src/dep/net.c new file mode 100644 index 0000000..76c1ac3 --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep/net.c @@ -0,0 +1,570 @@ +/**
+ * @file net.c
+ * @date Tue Jul 20 16:17:49 2010
+ *
+ * @brief Functions to interact with the network sockets and NIC driver.
+ *
+ *
+ */
+
+#include "../ptpd.h"
+
+Boolean
+lookupSubdomainAddress(Octet * subdomainName, Octet * subdomainAddress)
+{
+ UInteger32 h;
+
+ /* set multicast group address based on subdomainName */
+ if (!memcmp(subdomainName, DEFAULT_PTP_DOMAIN_NAME, PTP_SUBDOMAIN_NAME_LENGTH))
+ memcpy(subdomainAddress, DEFAULT_PTP_DOMAIN_ADDRESS, NET_ADDRESS_LENGTH);
+ else if (!memcmp(subdomainName, ALTERNATE_PTP_DOMAIN1_NAME, PTP_SUBDOMAIN_NAME_LENGTH))
+ memcpy(subdomainAddress, ALTERNATE_PTP_DOMAIN1_ADDRESS, NET_ADDRESS_LENGTH);
+ else if (!memcmp(subdomainName, ALTERNATE_PTP_DOMAIN2_NAME, PTP_SUBDOMAIN_NAME_LENGTH))
+ memcpy(subdomainAddress, ALTERNATE_PTP_DOMAIN2_ADDRESS, NET_ADDRESS_LENGTH);
+ else if (!memcmp(subdomainName, ALTERNATE_PTP_DOMAIN3_NAME, PTP_SUBDOMAIN_NAME_LENGTH))
+ memcpy(subdomainAddress, ALTERNATE_PTP_DOMAIN3_ADDRESS, NET_ADDRESS_LENGTH);
+ else {
+ h = crc_algorithm(subdomainName, PTP_SUBDOMAIN_NAME_LENGTH) % 3;
+ switch (h) {
+ case 0:
+ memcpy(subdomainAddress, ALTERNATE_PTP_DOMAIN1_ADDRESS, NET_ADDRESS_LENGTH);
+ break;
+ case 1:
+ memcpy(subdomainAddress, ALTERNATE_PTP_DOMAIN2_ADDRESS, NET_ADDRESS_LENGTH);
+ break;
+ case 2:
+ memcpy(subdomainAddress, ALTERNATE_PTP_DOMAIN3_ADDRESS, NET_ADDRESS_LENGTH);
+ break;
+ default:
+ ERROR("handle out of range for '%s'!\n", subdomainName);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+UInteger8
+lookupCommunicationTechnology(UInteger8 communicationTechnology)
+{
+#if defined(linux)
+
+ switch (communicationTechnology) {
+ case ARPHRD_ETHER:
+ case ARPHRD_EETHER:
+ case ARPHRD_IEEE802:
+ return PTP_ETHER;
+
+ default:
+ break;
+ }
+
+#elif defined(BSD_INTERFACE_FUNCTIONS)
+
+#endif
+
+ return PTP_DEFAULT;
+}
+
+UInteger32
+findIface(Octet * ifaceName, UInteger8 * communicationTechnology,
+ Octet * uuid, NetPath * netPath)
+{
+#if defined(linux)
+
+ /* depends on linux specific ioctls (see 'netdevice' man page) */
+ int i, flags;
+ struct ifconf data;
+ struct ifreq device[IFCONF_LENGTH];
+
+ data.ifc_len = sizeof(device);
+ data.ifc_req = device;
+
+ memset(data.ifc_buf, 0, data.ifc_len);
+
+ flags = IFF_UP | IFF_RUNNING | IFF_MULTICAST;
+
+ /* look for an interface if none specified */
+ if (ifaceName[0] != '\0') {
+ i = 0;
+ memcpy(device[i].ifr_name, ifaceName, IFACE_NAME_LENGTH);
+
+ if (ioctl(netPath->eventSock, SIOCGIFHWADDR, &device[i]) < 0)
+ DBGV("failed to get hardware address\n");
+ else if ((*communicationTechnology = lookupCommunicationTechnology(device[i].ifr_hwaddr.sa_family)) == PTP_DEFAULT)
+ DBGV("unsupported communication technology (%d)\n", *communicationTechnology);
+ else
+ memcpy(uuid, device[i].ifr_hwaddr.sa_data, PTP_UUID_LENGTH);
+ } else {
+ /* no iface specified */
+ /* get list of network interfaces */
+ if (ioctl(netPath->eventSock, SIOCGIFCONF, &data) < 0) {
+ PERROR("failed query network interfaces");
+ return 0;
+ }
+ if (data.ifc_len >= sizeof(device))
+ DBG("device list may exceed allocated space\n");
+
+ /* search through interfaces */
+ for (i = 0; i < data.ifc_len / sizeof(device[0]); ++i) {
+ DBGV("%d %s %s\n", i, device[i].ifr_name, inet_ntoa(((struct sockaddr_in *)&device[i].ifr_addr)->sin_addr));
+
+ if (ioctl(netPath->eventSock, SIOCGIFFLAGS, &device[i]) < 0)
+ DBGV("failed to get device flags\n");
+ else if ((device[i].ifr_flags & flags) != flags)
+ DBGV("does not meet requirements (%08x, %08x)\n", device[i].ifr_flags, flags);
+ else if (ioctl(netPath->eventSock, SIOCGIFHWADDR, &device[i]) < 0)
+ DBGV("failed to get hardware address\n");
+ else if ((*communicationTechnology = lookupCommunicationTechnology(device[i].ifr_hwaddr.sa_family)) == PTP_DEFAULT)
+ DBGV("unsupported communication technology (%d)\n", *communicationTechnology);
+ else {
+ DBGV("found interface (%s)\n", device[i].ifr_name);
+
+ memcpy(uuid, device[i].ifr_hwaddr.sa_data, PTP_UUID_LENGTH);
+ memcpy(ifaceName, device[i].ifr_name, IFACE_NAME_LENGTH);
+
+ break;
+ }
+ }
+ }
+
+ if (ifaceName[0] == '\0') {
+ ERROR("failed to find a usable interface\n");
+ return 0;
+ }
+ if (ioctl(netPath->eventSock, SIOCGIFADDR, &device[i]) < 0) {
+ PERROR("failed to get ip address");
+ return 0;
+ }
+ return ((struct sockaddr_in *)&device[i].ifr_addr)->sin_addr.s_addr;
+
+#elif defined(BSD_INTERFACE_FUNCTIONS)
+
+ struct ifaddrs *if_list, *ifv4, *ifh;
+
+ if (getifaddrs(&if_list) < 0) {
+ PERROR("getifaddrs() failed");
+ return FALSE;
+ }
+ /* find an IPv4, multicast, UP interface, right name(if supplied) */
+ for (ifv4 = if_list; ifv4 != NULL; ifv4 = ifv4->ifa_next) {
+ if ((ifv4->ifa_flags & IFF_UP) == 0)
+ continue;
+ if ((ifv4->ifa_flags & IFF_RUNNING) == 0)
+ continue;
+ if ((ifv4->ifa_flags & IFF_LOOPBACK))
+ continue;
+ if ((ifv4->ifa_flags & IFF_MULTICAST) == 0)
+ continue;
+ if (ifv4->ifa_addr->sa_family != AF_INET) /* must have IPv4
+ * address */
+ continue;
+
+ if (ifaceName[0] && strncmp(ifv4->ifa_name, ifaceName, IF_NAMESIZE) != 0)
+ continue;
+
+ break;
+ }
+
+ if (ifv4 == NULL) {
+ if (ifaceName[0]) {
+ ERROR("interface \"%s\" does not exist, or is not appropriate\n", ifaceName);
+ return FALSE;
+ }
+ ERROR("no suitable interfaces found!");
+ return FALSE;
+ }
+ /* find the AF_LINK info associated with the chosen interface */
+ for (ifh = if_list; ifh != NULL; ifh = ifh->ifa_next) {
+ if (ifh->ifa_addr->sa_family != AF_LINK)
+ continue;
+ if (strncmp(ifv4->ifa_name, ifh->ifa_name, IF_NAMESIZE) == 0)
+ break;
+ }
+
+ if (ifh == NULL) {
+ ERROR("could not get hardware address for interface \"%s\"\n", ifv4->ifa_name);
+ return FALSE;
+ }
+ /* check that the interface TYPE is OK */
+ if (((struct sockaddr_dl *)ifh->ifa_addr)->sdl_type != IFT_ETHER) {
+ ERROR("\"%s\" is not an ethernet interface!\n", ifh->ifa_name);
+ return FALSE;
+ }
+
+ DBG("==> %s %s %s\n", ifv4->ifa_name,
+ inet_ntoa(((struct sockaddr_in *)ifv4->ifa_addr)->sin_addr),
+ ether_ntoa((struct ether_addr *)
+ LLADDR((struct sockaddr_dl *)ifh->ifa_addr)));
+
+ *communicationTechnology = PTP_ETHER;
+ memcpy(ifaceName, ifh->ifa_name, IFACE_NAME_LENGTH);
+ memcpy(uuid, LLADDR((struct sockaddr_dl *)ifh->ifa_addr), PTP_UUID_LENGTH);
+
+ return ((struct sockaddr_in *)ifv4->ifa_addr)->sin_addr.s_addr;
+
+#endif
+}
+
+/* start all of the UDP stuff */
+/* 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)
+{
+ int temp, i;
+ struct in_addr interfaceAddr, netAddr;
+ struct sockaddr_in addr;
+ struct ip_mreq imr;
+ char addrStr[NET_ADDRESS_LENGTH];
+ char *s;
+
+ DBG("netInit\n");
+
+ /* open sockets */
+ if ((netPath->eventSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0
+ || (netPath->generalSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ PERROR("failed to initalize sockets");
+ return FALSE;
+ }
+ /* find a network interface */
+ if (!(interfaceAddr.s_addr = findIface(rtOpts->ifaceName, &ptpClock->port_communication_technology,
+ ptpClock->port_uuid_field, netPath)))
+ return FALSE;
+
+ temp = 1; /* allow address reuse */
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(int)) < 0
+ || setsockopt(netPath->generalSock, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(int)) < 0) {
+ DBG("failed to set socket reuse\n");
+ }
+ /* bind sockets */
+ /*
+ * need INADDR_ANY to allow receipt of multi-cast and uni-cast
+ * messages
+ */
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(PTP_EVENT_PORT);
+ if (bind(netPath->eventSock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
+ PERROR("failed to bind event socket");
+ return FALSE;
+ }
+ addr.sin_port = htons(PTP_GENERAL_PORT);
+ if (bind(netPath->generalSock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
+ PERROR("failed to bind general socket");
+ return FALSE;
+ }
+ /* set general and port address */
+ *(Integer16 *) ptpClock->event_port_address = PTP_EVENT_PORT;
+ *(Integer16 *) ptpClock->general_port_address = PTP_GENERAL_PORT;
+
+ /* send a uni-cast address if specified (useful for testing) */
+ if (rtOpts->unicastAddress[0]) {
+ if (!inet_aton(rtOpts->unicastAddress, &netAddr)) {
+ ERROR("failed to encode uni-cast address: %s\n", rtOpts->unicastAddress);
+ return FALSE;
+ }
+ netPath->unicastAddr = netAddr.s_addr;
+ } else
+ netPath->unicastAddr = 0;
+
+ /* resolve PTP subdomain */
+ if (!lookupSubdomainAddress(rtOpts->subdomainName, addrStr))
+ return FALSE;
+
+ if (!inet_aton(addrStr, &netAddr)) {
+ ERROR("failed to encode multi-cast address: %s\n", addrStr);
+ return FALSE;
+ }
+ netPath->multicastAddr = netAddr.s_addr;
+
+ s = addrStr;
+ for (i = 0; i < SUBDOMAIN_ADDRESS_LENGTH; ++i) {
+ ptpClock->subdomain_address[i] = strtol(s, &s, 0);
+
+ if (!s)
+ break;
+
+ ++s;
+ }
+
+ /* 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) {
+ PERROR("failed to enable multi-cast on the interface");
+ return FALSE;
+ }
+ /* join multicast group (for receiving) on specified interface */
+ if (setsockopt(netPath->eventSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(struct ip_mreq)) < 0
+ || setsockopt(netPath->generalSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(struct ip_mreq)) < 0) {
+ PERROR("failed to join the multi-cast group");
+ return FALSE;
+ }
+ /* set socket time-to-live */
+ if (setsockopt(netPath->eventSock, IPPROTO_IP, IP_MULTICAST_TTL, &rtOpts->ttl, sizeof(int)) < 0
+ || setsockopt(netPath->generalSock, IPPROTO_IP, IP_MULTICAST_TTL, &rtOpts->ttl, sizeof(int)) < 0) {
+ PERROR("failed to set the multi-cast time-to-live");
+ return FALSE;
+ }
+ /* enable loopback */
+ temp = 1;
+ if (setsockopt(netPath->eventSock, IPPROTO_IP, IP_MULTICAST_LOOP, &temp, sizeof(int)) < 0
+ || setsockopt(netPath->generalSock, IPPROTO_IP, IP_MULTICAST_LOOP, &temp, sizeof(int)) < 0) {
+ PERROR("failed to enable multi-cast loopback");
+ return FALSE;
+ }
+ /* make timestamps available through recvmsg() */
+
+ temp = 1;
+#if defined(linux)
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_TIMESTAMP, &temp, sizeof(int)) < 0
+ || setsockopt(netPath->generalSock, SOL_SOCKET, SO_TIMESTAMP, &temp, sizeof(int)) < 0) {
+#else /* BSD */
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_BINTIME, &temp, sizeof(int)) < 0
+ || setsockopt(netPath->generalSock, SOL_SOCKET, SO_BINTIME, &temp, sizeof(int)) < 0) {
+#endif /* linux or BSD */
+ PERROR("failed to enable receive time stamps");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* shut down the UDP stuff */
+Boolean
+netShutdown(NetPath * netPath)
+{
+ struct ip_mreq imr;
+
+ imr.imr_multiaddr.s_addr = 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));
+
+ netPath->multicastAddr = 0;
+ netPath->unicastAddr = 0;
+
+ if (netPath->eventSock > 0)
+ close(netPath->eventSock);
+ netPath->eventSock = -1;
+
+ if (netPath->generalSock > 0)
+ close(netPath->generalSock);
+ netPath->generalSock = -1;
+
+ return TRUE;
+}
+
+int
+netSelect(TimeInternal * timeout, NetPath * netPath)
+{
+ int ret, nfds;
+ fd_set readfds;
+ struct timeval tv, *tv_ptr;
+
+ if (timeout < 0)
+ return FALSE;
+
+ FD_ZERO(&readfds);
+ FD_SET(netPath->eventSock, &readfds);
+ FD_SET(netPath->generalSock, &readfds);
+
+ if (timeout) {
+ tv.tv_sec = timeout->seconds;
+ tv.tv_usec = timeout->nanoseconds / 1000;
+ tv_ptr = &tv;
+ } else
+ tv_ptr = 0;
+
+ if (netPath->eventSock > netPath->generalSock)
+ nfds = netPath->eventSock;
+ else
+ nfds = netPath->generalSock;
+
+ ret = select(nfds + 1, &readfds, 0, 0, tv_ptr) > 0;
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+ }
+ return ret;
+}
+
+ssize_t
+netRecvEvent(Octet * buf, TimeInternal * time, NetPath * netPath)
+{
+ ssize_t ret;
+ struct msghdr msg;
+ struct iovec vec[1];
+ struct sockaddr_in from_addr;
+ union {
+ struct cmsghdr cm;
+ char control[CMSG_SPACE(sizeof(struct timeval))];
+ } cmsg_un;
+ struct cmsghdr *cmsg;
+#if defined(linux)
+ struct timeval *tv;
+#else /* FreeBSD */
+ struct timespec ts;
+#endif /* FreeBSD or Linux */
+
+ vec[0].iov_base = buf;
+ vec[0].iov_len = PACKET_SIZE;
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&from_addr, 0, sizeof(from_addr));
+ memset(buf, 0, PACKET_SIZE);
+ memset(&cmsg_un, 0, sizeof(cmsg_un));
+
+ msg.msg_name = (caddr_t)&from_addr;
+ msg.msg_namelen = sizeof(from_addr);
+ msg.msg_iov = vec;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg_un.control;
+ msg.msg_controllen = sizeof(cmsg_un.control);
+ msg.msg_flags = 0;
+
+ ret = recvmsg(netPath->eventSock, &msg, MSG_DONTWAIT);
+ if (ret <= 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ return ret;
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ ERROR("received truncated message\n");
+ return 0;
+ }
+ /* get time stamp of packet */
+ if (!time) {
+ ERROR("null receive time stamp argument\n");
+ return 0;
+ }
+ if (msg.msg_flags & MSG_CTRUNC) {
+ ERROR("received truncated ancillary data\n");
+ return 0;
+ }
+ 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;
+ }
+
+#if defined(linux)
+ 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);
+
+ if (tv) {
+ time->seconds = tv->tv_sec;
+ time->nanoseconds = tv->tv_usec * 1000;
+ DBGV("kernel recv time stamp %us %dns\n", time->seconds, time->nanoseconds);
+ } else {
+ /*
+ * 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");
+ return 0;
+ }
+#else /* FreeBSD */
+ bzero(&ts, sizeof(ts));
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg))
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_BINTIME)
+ bintime2timespec((struct bintime *)CMSG_DATA(cmsg),
+ &ts);
+
+ if (ts.tv_sec != 0) {
+ time->seconds = ts.tv_sec;
+ time->nanoseconds = ts.tv_nsec;
+ DBGV("kernel recv time stamp %us %dns\n", time->seconds, time->nanoseconds);
+ } else {
+ /*
+ * 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");
+ return 0;
+ }
+#endif /* FreeBSD or Linux */
+
+ return ret;
+}
+
+ssize_t
+netRecvGeneral(Octet * buf, NetPath * netPath)
+{
+ 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);
+ if (ret <= 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ return ret;
+ }
+ return ret;
+}
+
+ssize_t
+netSendEvent(Octet * buf, UInteger16 length, NetPath * netPath)
+{
+ ssize_t ret;
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(PTP_EVENT_PORT);
+ addr.sin_addr.s_addr = netPath->multicastAddr;
+
+ ret = sendto(netPath->eventSock, buf, length, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("error sending multi-cast event message\n");
+
+ if (netPath->unicastAddr) {
+ addr.sin_addr.s_addr = netPath->unicastAddr;
+
+ ret = sendto(netPath->eventSock, buf, length, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("error sending uni-cast event message\n");
+ }
+ return ret;
+}
+
+ssize_t
+netSendGeneral(Octet * buf, UInteger16 length, NetPath * netPath)
+{
+ ssize_t ret;
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(PTP_GENERAL_PORT);
+ addr.sin_addr.s_addr = netPath->multicastAddr;
+
+ ret = sendto(netPath->generalSock, buf, length, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("error sending multi-cast general message\n");
+
+ if (netPath->unicastAddr) {
+ addr.sin_addr.s_addr = netPath->unicastAddr;
+
+ ret = sendto(netPath->eventSock, buf, length, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("error sending uni-cast general message\n");
+ }
+ return ret;
+}
diff --git a/third_party/ptpd-1.1.0/src/dep/ptpd_dep.h b/third_party/ptpd-1.1.0/src/dep/ptpd_dep.h new file mode 100644 index 0000000..63cf674 --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep/ptpd_dep.h @@ -0,0 +1,154 @@ +/**
+ * @file ptpd_dep.h
+ * @date Tue Jul 20 16:18:39 2010
+ *
+ * @brief External definitions for inclusion elsewhere.
+ *
+ *
+ */
+
+#ifndef PTPD_DEP_H
+#define PTPD_DEP_H
+
+#include<stdlib.h>
+#include<stdio.h>
+#include<string.h>
+#include<unistd.h>
+#include<errno.h>
+#include<signal.h>
+#include<fcntl.h>
+#include<sys/stat.h>
+#include<time.h>
+#include<sys/time.h>
+#include<sys/timex.h>
+#include<sys/socket.h>
+#include<sys/select.h>
+#include<sys/ioctl.h>
+#include<arpa/inet.h>
+#include<stdarg.h>
+#include<syslog.h>
+#include<limits.h>
+
+/* system messages */
+#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, ...) message(LOG_DEBUG, x, ##__VA_ARGS__)
+#else
+#define DBGV(x, ...)
+#endif
+
+#ifdef PTPD_DBG
+#define DBG(x, ...) message(LOG_DEBUG, x, ##__VA_ARGS__)
+#else
+#define DBG(x, ...)
+#endif
+
+/* endian corrections */
+#if defined(PTPD_MSBF)
+#define shift8(x,y) ( (x) << ((3-y)<<3) )
+#define shift16(x,y) ( (x) << ((1-y)<<4) )
+#elif defined(PTPD_LSBF)
+#define shift8(x,y) ( (x) << ((y)<<3) )
+#define shift16(x,y) ( (x) << ((y)<<4) )
+#endif
+
+#define flip16(x) htons(x)
+#define flip32(x) htonl(x)
+
+/* i don't know any target platforms that do not have htons and htonl,
+ but here are generic funtions just in case */
+/*
+#if defined(PTPD_MSBF)
+#define flip16(x) (x)
+#define flip32(x) (x)
+#elif defined(PTPD_LSBF)
+static inline Integer16 flip16(Integer16 x)
+{
+ return (((x) >> 8) & 0x00ff) | (((x) << 8) & 0xff00);
+}
+
+static inline Integer32 flip32(x)
+{
+ return (((x) >> 24) & 0x000000ff) | (((x) >> 8 ) & 0x0000ff00) |
+ (((x) << 8 ) & 0x00ff0000) | (((x) << 24) & 0xff000000);
+}
+#endif
+*/
+
+/* bit array manipulation */
+#define getFlag(x,y) !!( *(UInteger8*)((x)+((y)<8?1:0)) & (1<<((y)<8?(y):(y)-8)) )
+#define setFlag(x,y) ( *(UInteger8*)((x)+((y)<8?1:0)) |= 1<<((y)<8?(y):(y)-8) )
+#define clearFlag(x,y) ( *(UInteger8*)((x)+((y)<8?1:0)) &= ~(1<<((y)<8?(y):(y)-8)) )
+
+
+/* msg.c */
+Boolean msgPeek(void *, ssize_t);
+void msgUnpackHeader(void *, MsgHeader *);
+void msgUnpackSync(void *, MsgSync *);
+void msgUnpackDelayReq(void *, MsgDelayReq *);
+void msgUnpackFollowUp(void *, MsgFollowUp *);
+void msgUnpackDelayResp(void *, MsgDelayResp *);
+void msgUnpackManagement(void *, MsgManagement *);
+UInteger8 msgUnloadManagement(void *, MsgManagement *, PtpClock *, RunTimeOpts *);
+void msgUnpackManagementPayload(void *buf, MsgManagement * manage);
+void msgPackHeader(void *, PtpClock *);
+void msgPackSync(void *, Boolean, TimeRepresentation *, PtpClock *);
+void msgPackDelayReq(void *, Boolean, TimeRepresentation *, PtpClock *);
+void msgPackFollowUp(void *, UInteger16, TimeRepresentation *, PtpClock *);
+void msgPackDelayResp(void *, MsgHeader *, TimeRepresentation *, PtpClock *);
+UInteger16 msgPackManagement(void *, MsgManagement *, PtpClock *);
+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 *);
+
+/* servo.c */
+void initClock(RunTimeOpts *, PtpClock *);
+void
+updateDelay(TimeInternal *, TimeInternal *,
+ one_way_delay_filter *, RunTimeOpts *, PtpClock *);
+void
+updateOffset(TimeInternal *, TimeInternal *,
+ offset_from_master_filter *, RunTimeOpts *, PtpClock *);
+void updateClock(RunTimeOpts *, PtpClock *);
+
+/* startup.c */
+/* unix API dependent */
+int logToFile();
+int recordToFile();
+PtpClock *ptpdStartup(int, char **, Integer16 *, RunTimeOpts *);
+void ptpdShutdown(void);
+
+/* sys.c */
+char *translatePortState(PtpClock *ptpClock);
+/* unix API dependent */
+void message(int priority, const char *format, ...);
+void displayStats(RunTimeOpts *, PtpClock *);
+Boolean nanoSleep(TimeInternal *);
+void getTime(TimeInternal *);
+void setTime(TimeInternal *);
+UInteger16 getRand(UInteger32 *);
+Boolean adjFreq(Integer32);
+
+/* timer.c */
+void initTimer(void);
+void timerUpdate(IntervalTimer *);
+void timerStop(UInteger16, IntervalTimer *);
+void timerStart(UInteger16, UInteger16, IntervalTimer *);
+Boolean timerExpired(UInteger16, IntervalTimer *);
+
+
+#endif
diff --git a/third_party/ptpd-1.1.0/src/dep/servo.c b/third_party/ptpd-1.1.0/src/dep/servo.c new file mode 100644 index 0000000..b888593 --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep/servo.c @@ -0,0 +1,215 @@ +/**
+ * @file servo.c
+ * @date Tue Jul 20 16:19:19 2010
+ *
+ * @brief Code which implements the clock servo in software.
+ *
+ *
+ */
+
+#include "../ptpd.h"
+
+void
+initClock(RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ DBG("initClock\n");
+
+ /* clear vars */
+ ptpClock->master_to_slave_delay.seconds = ptpClock->master_to_slave_delay.nanoseconds = 0;
+ ptpClock->slave_to_master_delay.seconds = ptpClock->slave_to_master_delay.nanoseconds = 0;
+ 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;
+
+ /* level clock */
+ if (!rtOpts->noAdjust)
+ adjFreq(0);
+}
+
+void
+updateDelay(TimeInternal * send_time, TimeInternal * recv_time,
+ one_way_delay_filter * owd_filt, RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ Integer16 s;
+ TimeInternal slave_to_master_delay;
+
+ DBGV("updateDelay\n");
+
+ /* calc 'slave_to_master_delay' */
+ subTime(&slave_to_master_delay, recv_time, send_time);
+
+ if (rtOpts->maxDelay) { /* If maxDelay is 0 then it's OFF */
+ if (slave_to_master_delay.seconds && rtOpts->maxDelay) {
+ INFO("updateDelay aborted, delay greater than 1"
+ " second.");
+ return;
+ }
+
+ if (slave_to_master_delay.nanoseconds > rtOpts->maxDelay) {
+ INFO("updateDelay aborted, delay %d greater than "
+ "administratively set maximum %d\n",
+ slave_to_master_delay.nanoseconds,
+ rtOpts->maxDelay);
+ return;
+ }
+ }
+
+ ptpClock->slave_to_master_delay = slave_to_master_delay;
+
+ /* update 'one_way_delay' */
+ 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;
+
+ if (ptpClock->one_way_delay.seconds) {
+ /* cannot filter with secs, clear filter */
+ owd_filt->s_exp = owd_filt->nsec_prev = 0;
+ return;
+ }
+ /* avoid overflowing filter */
+ s = rtOpts->s;
+ while (abs(owd_filt->y) >> (31 - s))
+ --s;
+
+ /* crank down filter cutoff by increasing 's_exp' */
+ if (owd_filt->s_exp < 1)
+ owd_filt->s_exp = 1;
+ else if (owd_filt->s_exp < 1 << s)
+ ++owd_filt->s_exp;
+ else if (owd_filt->s_exp > 1 << s)
+ owd_filt->s_exp = 1 << s;
+
+ /* filter 'one_way_delay' */
+ owd_filt->y = (owd_filt->s_exp - 1) * owd_filt->y / owd_filt->s_exp +
+ (ptpClock->one_way_delay.nanoseconds / 2 + owd_filt->nsec_prev / 2) / owd_filt->s_exp;
+
+ 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);
+}
+
+void
+updateOffset(TimeInternal * send_time, TimeInternal * recv_time,
+ offset_from_master_filter * ofm_filt, RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ TimeInternal master_to_slave_delay;
+ DBGV("updateOffset\n");
+
+ /* calc 'master_to_slave_delay' */
+ subTime(&master_to_slave_delay, recv_time, send_time);
+
+ if (rtOpts->maxDelay) { /* If maxDelay is 0 then it's OFF */
+ if (master_to_slave_delay.seconds && rtOpts->maxDelay) {
+ INFO("updateDelay aborted, delay greater than 1"
+ " second.");
+ return;
+ }
+
+ if (master_to_slave_delay.nanoseconds > rtOpts->maxDelay) {
+ INFO("updateDelay aborted, delay %d greater than "
+ "administratively set maximum %d\n",
+ master_to_slave_delay.nanoseconds,
+ rtOpts->maxDelay);
+ return;
+ }
+ }
+ ptpClock->master_to_slave_delay = master_to_slave_delay;
+
+ /* update 'offset_from_master' */
+ subTime(&ptpClock->offset_from_master, &ptpClock->master_to_slave_delay, &ptpClock->one_way_delay);
+
+ if (ptpClock->offset_from_master.seconds) {
+ /* cannot filter with secs, clear filter */
+ ofm_filt->nsec_prev = 0;
+ return;
+ }
+ /* filter 'offset_from_master' */
+ ofm_filt->y = ptpClock->offset_from_master.nanoseconds / 2 + ofm_filt->nsec_prev / 2;
+ 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);
+}
+
+void
+updateClock(RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ Integer32 adj;
+ TimeInternal timeTmp;
+
+ DBGV("updateClock\n");
+
+ if (rtOpts->maxReset) { /* If maxReset is 0 then it's OFF */
+ if (ptpClock->offset_from_master.seconds) {
+ INFO("updateClock aborted, offset greater than 1"
+ " second.");
+ goto display;
+ }
+
+ if (ptpClock->offset_from_master.nanoseconds >
+ rtOpts->maxReset) {
+ INFO("updateClock aborted, offset %d greater than "
+ "administratively set maximum %d\n",
+ ptpClock->offset_from_master.nanoseconds,
+ rtOpts->maxReset);
+ goto display;
+ }
+ }
+
+ if (ptpClock->offset_from_master.seconds) {
+ /* if secs, reset clock or set freq adjustment to max */
+ if (!rtOpts->noAdjust) {
+ if (!rtOpts->noResetClock) {
+ getTime(&timeTmp);
+ subTime(&timeTmp, &timeTmp, &ptpClock->offset_from_master);
+ setTime(&timeTmp);
+ initClock(rtOpts, ptpClock);
+ } else {
+ adj = ptpClock->offset_from_master.nanoseconds > 0 ? ADJ_FREQ_MAX : -ADJ_FREQ_MAX;
+ adjFreq(-adj);
+ }
+ }
+ } else {
+ /* the PI controller */
+
+ /* no negative or zero attenuation */
+ if (rtOpts->ap < 1)
+ rtOpts->ap = 1;
+ if (rtOpts->ai < 1)
+ rtOpts->ai = 1;
+
+ /* the accumulator for the I component */
+ ptpClock->observed_drift += ptpClock->offset_from_master.nanoseconds / rtOpts->ai;
+
+ /* clamp the accumulator to ADJ_FREQ_MAX for sanity */
+ if (ptpClock->observed_drift > ADJ_FREQ_MAX)
+ ptpClock->observed_drift = ADJ_FREQ_MAX;
+ else if (ptpClock->observed_drift < -ADJ_FREQ_MAX)
+ ptpClock->observed_drift = -ADJ_FREQ_MAX;
+
+ adj = ptpClock->offset_from_master.nanoseconds / rtOpts->ap + ptpClock->observed_drift;
+
+ /* apply controller output as a clock tick rate adjustment */
+ if (!rtOpts->noAdjust)
+ adjFreq(-adj);
+ }
+
+display:
+
+ if (rtOpts->displayStats)
+ displayStats(rtOpts, ptpClock);
+
+ DBGV("master-to-slave delay: %10ds %11dns\n",
+ ptpClock->master_to_slave_delay.seconds, ptpClock->master_to_slave_delay.nanoseconds);
+ DBGV("slave-to-master delay: %10ds %11dns\n",
+ ptpClock->slave_to_master_delay.seconds, ptpClock->slave_to_master_delay.nanoseconds);
+ DBGV("one-way delay: %10ds %11dns\n",
+ ptpClock->one_way_delay.seconds, ptpClock->one_way_delay.nanoseconds);
+ DBG("offset from master: %10ds %11dns\n",
+ ptpClock->offset_from_master.seconds, ptpClock->offset_from_master.nanoseconds);
+ DBG("observed drift: %10d\n", ptpClock->observed_drift);
+}
diff --git a/third_party/ptpd-1.1.0/src/dep/startup.c b/third_party/ptpd-1.1.0/src/dep/startup.c new file mode 100644 index 0000000..edbcbfd --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep/startup.c @@ -0,0 +1,364 @@ +/**
+ * @file startup.c
+ * @date Wed Jun 23 09:33:27 2010
+ *
+ * @brief Code to handle daemon startup, including command line args
+ *
+ * The function in this file are called when the daemon starts up
+ * and include the getopt() command line argument parsing.
+ */
+
+#include "../ptpd.h"
+
+PtpClock *ptpClock;
+
+/**
+ * Signal handler for catching a close signal
+ *
+ * @param sig
+ */
+void
+catch_close(int sig)
+{
+ char *s;
+
+ ptpdShutdown();
+
+ switch (sig) {
+ case SIGINT:
+ s = "interrupt";
+ break;
+
+ case SIGTERM:
+ s = "terminate";
+ break;
+
+ default:
+ s = "?";
+ }
+
+ NOTIFY("shutdown on %s signal\n", s);
+
+ exit(0);
+}
+
+/**
+ * Signal handler for HUP which tells us to swap the log file.
+ *
+ * @param sig
+ */
+void
+catch_sighup(int sig)
+{
+ if(!logToFile())
+ NOTIFY("SIGHUP failed\n");
+ else
+ NOTIFY("I've been SIGHUP'd\n");
+ if(!recordToFile())
+ NOTIFY("SIGHUP failed\n");
+ else
+ NOTIFY("I've been SIGHUP'd\n");
+
+}
+
+/**
+ * Log output to a file
+ *
+ *
+ * @return True if success, False if failure
+ */
+int
+logToFile()
+{
+ extern RunTimeOpts rtOpts;
+ if(rtOpts.logFd != -1)
+ close(rtOpts.logFd);
+
+ if((rtOpts.logFd = creat(rtOpts.file, 0444)) != -1) {
+ dup2(rtOpts.logFd, STDOUT_FILENO);
+ dup2(rtOpts.logFd, STDERR_FILENO);
+ }
+ return (rtOpts.logFd != -1);
+}
+
+/**
+ * Record quality data for later correlation
+ *
+ *
+ * @return True if success, False if failure
+ */
+int
+recordToFile()
+{
+ extern RunTimeOpts rtOpts;
+
+ if (rtOpts.recordFP != NULL)
+ fclose(rtOpts.recordFP);
+
+ if ((rtOpts.recordFP = fopen(rtOpts.recordFile, "w")) == NULL)
+ PERROR("could not open sync recording file");
+ else
+ setlinebuf(rtOpts.recordFP);
+ return (rtOpts.recordFP != NULL);
+}
+
+void
+ptpdShutdown()
+{
+ netShutdown(&ptpClock->netPath);
+
+ free(ptpClock->foreign);
+ free(ptpClock);
+}
+
+PtpClock *
+ptpdStartup(int argc, char **argv, Integer16 * ret, RunTimeOpts * rtOpts)
+{
+ int c, nondaemon = 0, noclose = 0;
+
+ /* parse command line arguments */
+ while ((c = getopt(argc, argv, "?cf:dDR:xM:O:ta:w:b:u:l:o:e:hy:m:gpSs:i:v:n:k:rT:")) != -1) {
+ switch (c) {
+ case '?':
+ printf(
+ "\nUsage: ptpd [OPTION]\n\n"
+ "-? show this page\n"
+ "\n"
+ "-c run in command line (non-daemon) mode\n"
+ "-f FILE send stats to FILE\n"
+ "-S send output to syslog \n"
+ "-T set multicast time to live\n"
+ "-d display stats\n"
+ "-D display stats in .csv format\n"
+ "-R record data about sync packets in a file\n"
+ "\n"
+ "-x do not reset the clock if off by more than one second\n"
+ "-O do not reset the clock if offset is more than NUMBER nanoseconds\n"
+ "-M do not accept delay values of more than NUMBER nanoseconds\n"
+ "-t do not adjust the system clock\n"
+ "-a NUMBER,NUMBER specify clock servo P and I attenuations\n"
+ "-w NUMBER specify one way delay filter stiffness\n"
+ "\n"
+ "-b NAME bind PTP to network interface NAME\n"
+ "-u ADDRESS also send uni-cast to ADDRESS\n"
+ "-l NUMBER,NUMBER specify inbound, outbound latency in nsec\n"
+ "\n"
+ "-o NUMBER specify current UTC offset\n"
+ "-e NUMBER specify epoch NUMBER\n"
+ "-h specify half epoch\n"
+ "\n"
+ "-y NUMBER specify sync interval in 2^NUMBER sec\n"
+ "-m NUMBER specify max number of foreign master records\n"
+ "\n"
+ "-g run as slave only\n"
+ "-p make this a preferred clock\n"
+ "-s NUMBER specify system clock stratum\n"
+ "-i NAME specify system clock identifier\n"
+ "-v NUMBER specify system clock allen variance\n"
+ "\n"
+ "-n NAME specify PTP subdomain name (not related to IP or DNS)\n"
+ "\n"
+ "-k NUMBER,NUMBER send a management message of key, record, then exit\n"
+ "\n"
+ );
+ *ret = 0;
+ return 0;
+
+ case 'c':
+ nondaemon = 1;
+ break;
+
+ case 'S':
+ rtOpts->useSysLog = TRUE;
+ break;
+ case 'T':
+ rtOpts->ttl = atoi(optarg);
+ break;
+ case 'f':
+ strncpy(rtOpts->file, optarg, PATH_MAX);
+ if(logToFile())
+ noclose = 1;
+ else
+ PERROR("could not open output file");
+ break;
+
+ case 'd':
+#ifndef PTPD_DBG
+ rtOpts->displayStats = TRUE;
+#endif
+ break;
+
+ case 'D':
+#ifndef PTPD_DBG
+ rtOpts->displayStats = TRUE;
+ rtOpts->csvStats = TRUE;
+#endif
+ break;
+
+ case 'R':
+ strncpy(rtOpts->recordFile, optarg, PATH_MAX);
+ if (recordToFile())
+ noclose = 1;
+ else
+ PERROR("could not open quality file");
+ break;
+
+ case 'x':
+ rtOpts->noResetClock = TRUE;
+ break;
+
+ case 'O':
+ rtOpts->maxReset = atoi(optarg);
+ if (rtOpts->maxReset > 1000000000) {
+ PERROR("Use -x to prevent jumps of more"
+ " than one second.");
+ *ret = 1;
+ return (0);
+ }
+ break;
+ case 'M':
+ rtOpts->maxDelay = atoi(optarg);
+ if (rtOpts->maxDelay > 1000000000) {
+ PERROR("Use -x to prevent jumps of more"
+ " than one second.");
+ *ret = 1;
+ return (0);
+ }
+ break;
+ case 't':
+ rtOpts->noAdjust = TRUE;
+ break;
+
+ case 'a':
+ rtOpts->ap = strtol(optarg, &optarg, 0);
+ if (optarg[0])
+ rtOpts->ai = strtol(optarg + 1, 0, 0);
+ break;
+
+ case 'w':
+ rtOpts->s = strtol(optarg, &optarg, 0);
+ break;
+
+ case 'b':
+ memset(rtOpts->ifaceName, 0, IFACE_NAME_LENGTH);
+ strncpy(rtOpts->ifaceName, optarg, IFACE_NAME_LENGTH);
+ break;
+
+ case 'u':
+ strncpy(rtOpts->unicastAddress, optarg, NET_ADDRESS_LENGTH);
+ break;
+
+ case 'l':
+ rtOpts->inboundLatency.nanoseconds = strtol(optarg, &optarg, 0);
+ if (optarg[0])
+ rtOpts->outboundLatency.nanoseconds = strtol(optarg + 1, 0, 0);
+ break;
+
+ case 'o':
+ rtOpts->currentUtcOffset = strtol(optarg, &optarg, 0);
+ break;
+
+ case 'e':
+ rtOpts->epochNumber = strtoul(optarg, &optarg, 0);
+ break;
+
+ case 'h':
+ rtOpts->halfEpoch = TRUE;
+ break;
+
+ case 'y':
+ rtOpts->syncInterval = strtol(optarg, 0, 0);
+ break;
+
+ case 'm':
+ rtOpts->max_foreign_records = strtol(optarg, 0, 0);
+ if (rtOpts->max_foreign_records < 1)
+ rtOpts->max_foreign_records = 1;
+ break;
+
+ case 'g':
+ rtOpts->slaveOnly = TRUE;
+ break;
+
+ case 'p':
+ rtOpts->clockPreferred = TRUE;
+ break;
+
+ case 's':
+ rtOpts->clockStratum = strtol(optarg, 0, 0);
+ if (rtOpts->clockStratum <= 0)
+ rtOpts->clockStratum = 255;
+ break;
+
+ case 'i':
+ memset(rtOpts->clockIdentifier, 0, PTP_CODE_STRING_LENGTH);
+ strncpy(rtOpts->clockIdentifier, optarg, PTP_CODE_STRING_LENGTH);
+ break;
+
+ case 'v':
+ rtOpts->clockVariance = strtol(optarg, 0, 0);
+ break;
+
+ case 'n':
+ memset(rtOpts->subdomainName, 0, PTP_SUBDOMAIN_NAME_LENGTH);
+ strncpy(rtOpts->subdomainName, optarg, PTP_SUBDOMAIN_NAME_LENGTH);
+ break;
+
+ case 'k':
+ rtOpts->probe = TRUE;
+
+ rtOpts->probe_management_key = strtol(optarg, &optarg, 0);
+ if (optarg[0])
+ rtOpts->probe_record_key = strtol(optarg + 1, 0, 0);
+
+ nondaemon = 1;
+ break;
+
+ case 'r':
+ ERROR("The '-r' option has been removed because it is now the default behaviour.\n");
+ ERROR("Use the '-x' option to disable clock resetting.\n");
+ *ret = 1;
+ return 0;
+
+ default:
+ *ret = 1;
+ return 0;
+ }
+ }
+
+ ptpClock = (PtpClock *) calloc(1, sizeof(PtpClock));
+ if (!ptpClock) {
+ PERROR("failed to allocate memory for protocol engine data");
+ *ret = 2;
+ return 0;
+ } else {
+ DBG("allocated %d bytes for protocol engine data\n", (int)sizeof(PtpClock));
+ ptpClock->foreign = (ForeignMasterRecord *) calloc(rtOpts->max_foreign_records, sizeof(ForeignMasterRecord));
+ if (!ptpClock->foreign) {
+ PERROR("failed to allocate memory for foreign master data");
+ *ret = 2;
+ free(ptpClock);
+ return 0;
+ } else {
+ DBG("allocated %d bytes for foreign master data\n", (int)(rtOpts->max_foreign_records * sizeof(ForeignMasterRecord)));
+ }
+ }
+
+#ifndef PTPD_NO_DAEMON
+ if (!nondaemon) {
+ if (daemon(0, noclose) == -1) {
+ PERROR("failed to start as daemon");
+ *ret = 3;
+ return 0;
+ }
+ DBG("running as daemon\n");
+ }
+#endif
+
+ signal(SIGINT, catch_close);
+ signal(SIGTERM, catch_close);
+ signal(SIGHUP, catch_sighup);
+
+ *ret = 0;
+ return ptpClock;
+}
diff --git a/third_party/ptpd-1.1.0/src/dep/sys.c b/third_party/ptpd-1.1.0/src/dep/sys.c new file mode 100644 index 0000000..9e1221e --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep/sys.c @@ -0,0 +1,282 @@ +/**
+ * @file sys.c
+ * @date Tue Jul 20 16:19:46 2010
+ *
+ * @brief Code to call kernel time routines and also display server statistics.
+ *
+ *
+ */
+
+#include "../ptpd.h"
+
+
+int
+isTimeInternalNegative(const TimeInternal * p)
+{
+ return (p->seconds < 0) || (p->nanoseconds < 0);
+}
+
+
+int
+snprint_TimeInternal(char *s, int max_len, const TimeInternal * p)
+{
+ int len = 0;
+
+ if (isTimeInternalNegative(p))
+ len += snprintf(&s[len], max_len - len, "-");
+
+ len += snprintf(&s[len], max_len - len, "%d.%09d",
+ abs(p->seconds), abs(p->nanoseconds));
+
+ return len;
+}
+
+
+
+int
+snprint_ClockIdentity(char *s, int max_len, const Octet uuid[PTP_UUID_LENGTH], const char *info)
+{
+ int len = 0;
+ int i;
+
+ if (info)
+ len += snprintf(&s[len], max_len - len, "%s", info);
+
+ for (i = 0; ;) {
+ len += snprintf(&s[len], max_len - len, "%02x", (unsigned char) uuid[i]);
+
+ if (++i >= PTP_UUID_LENGTH)
+ break;
+
+ // uncomment the line below to print a separator after each byte except the last one
+ // len += snprintf(&s[len], max_len - len, "%s", "-");
+ }
+
+ return len;
+}
+
+
+int
+snprint_PortIdentity(char *s, int max_len, const Octet uuid[PTP_UUID_LENGTH],
+ UInteger16 portId, const char *info)
+{
+ int len = 0;
+
+ if (info)
+ len += snprintf(&s[len], max_len - len, "%s", info);
+
+ len += snprint_ClockIdentity(&s[len], max_len - len, uuid, NULL);
+ len += snprintf(&s[len], max_len - len, ":%02x", portId);
+ return len;
+}
+
+
+void
+message(int priority, const char *format, ...)
+{
+ extern RunTimeOpts rtOpts;
+ va_list ap;
+ va_start(ap, format);
+ if(rtOpts.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);
+}
+
+char *
+translatePortState(PtpClock *ptpClock)
+{
+ char *s;
+ switch(ptpClock->port_state) {
+ case PTP_INITIALIZING: s = "init"; break;
+ case PTP_FAULTY: s = "flt"; break;
+ case PTP_LISTENING: s = "lstn"; break;
+ case PTP_PASSIVE: s = "pass"; break;
+ case PTP_UNCALIBRATED: s = "uncl"; break;
+ case PTP_SLAVE: s = "slv"; break;
+ case PTP_PRE_MASTER: s = "pmst"; break;
+ case PTP_MASTER: s = "mst"; break;
+ case PTP_DISABLED: s = "dsbl"; break;
+ default: s = "?"; break;
+ }
+ return s;
+}
+
+void
+displayStats(RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ static int start = 1;
+ static char sbuf[SCREEN_BUFSZ];
+ int len = 0;
+ struct timeval now;
+ char time_str[MAXTIMESTR];
+
+ if (start && rtOpts->csvStats) {
+ start = 0;
+ printf("timestamp, state, clock ID, one way delay, "
+ "offset from master, master to slave, "
+ "slave to master, drift, variance");
+ fflush(stdout);
+ }
+ memset(sbuf, ' ', sizeof(sbuf));
+
+ gettimeofday(&now, 0);
+ strftime(time_str, MAXTIMESTR, "%Y-%m-%d %X", localtime(&now.tv_sec));
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "%s%s:%06d, %s",
+ rtOpts->csvStats ? "\n" : "\rstate: ",
+ time_str, (int)now.tv_usec,
+ translatePortState(ptpClock));
+
+ if (ptpClock->port_state == PTP_SLAVE) {
+ len += snprint_PortIdentity(sbuf + len, sizeof(sbuf) - len,
+ ptpClock->parent_uuid, ptpClock->parent_port_id, ", ");
+
+ /*
+ * if grandmaster ID differs from parent port ID then also
+ * print GM ID
+ */
+ if (memcmp(ptpClock->grandmaster_uuid_field,
+ ptpClock->parent_uuid, PTP_UUID_LENGTH)) {
+ len += snprint_ClockIdentity(sbuf + len,
+ sizeof(sbuf) - len,
+ ptpClock->grandmaster_uuid_field,
+ " GM:");
+ }
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, ", ");
+
+ if (!rtOpts->csvStats)
+ len += snprintf(sbuf + len,
+ sizeof(sbuf) - len, "owd: ");
+
+ len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
+ &ptpClock->one_way_delay);
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, ", ");
+
+ if (!rtOpts->csvStats)
+ len += snprintf(sbuf + len, sizeof(sbuf) - len,
+ "ofm: ");
+
+ len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
+ &ptpClock->offset_from_master);
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len,
+ ", %s%d.%09d" ", %s%d.%09d",
+ rtOpts->csvStats ? "" : "stm: ",
+ ptpClock->slave_to_master_delay.seconds,
+ abs(ptpClock->slave_to_master_delay.nanoseconds),
+ rtOpts->csvStats ? "" : "mts: ",
+ ptpClock->master_to_slave_delay.seconds,
+ abs(ptpClock->master_to_slave_delay.nanoseconds));
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len,
+ ", %s%d",
+ rtOpts->csvStats ? "" : "drift: ",
+ ptpClock->observed_drift);
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len,
+ ", %s%d",
+ rtOpts->csvStats ? "" : "var: ",
+ ptpClock->observed_variance);
+ }
+ else {
+ if (ptpClock->port_state == PTP_MASTER) {
+ len += snprint_ClockIdentity(sbuf + len, sizeof(sbuf) - len,
+ ptpClock->clock_uuid_field, " (ID:");
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, ")");
+ }
+ }
+ 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) {
+ t->seconds = tr.tv_sec;
+ t->nanoseconds = tr.tv_nsec;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void
+getTime(TimeInternal * time)
+{
+#if defined(linux)
+
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ time->seconds = tv.tv_sec;
+ time->nanoseconds = tv.tv_usec * 1000;
+
+#else /* FreeBSD */
+
+ struct timespec tp;
+ if (clock_gettime(CLOCK_REALTIME, &tp) < 0) {
+ PERROR("clock_gettime() failed, exiting.");
+ exit(0);
+ }
+ time->seconds = tp.tv_sec;
+ time->nanoseconds = tp.tv_nsec;
+#endif /* FreeBSD or Linux */
+}
+
+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)
+{
+ 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/third_party/ptpd-1.1.0/src/dep/timer.c b/third_party/ptpd-1.1.0/src/dep/timer.c new file mode 100644 index 0000000..4bd7f80 --- /dev/null +++ b/third_party/ptpd-1.1.0/src/dep/timer.c @@ -0,0 +1,96 @@ +/**
+ * @file timer.c
+ * @date Wed Jun 23 09:41:26 2010
+ *
+ * @brief The timers which run the state machine.
+ *
+ * Timers in the PTP daemon are run off of the signal system.
+ */
+
+#include "../ptpd.h"
+
+#define TIMER_INTERVAL 1
+int elapsed;
+
+void
+catch_alarm(int sig)
+{
+ elapsed += TIMER_INTERVAL;
+
+ DBGV("catch_alarm: elapsed %d\n", elapsed);
+}
+
+void
+initTimer(void)
+{
+ struct itimerval itimer;
+
+ DBG("initTimer\n");
+
+ signal(SIGALRM, SIG_IGN);
+
+ elapsed = 0;
+ itimer.it_value.tv_sec = itimer.it_interval.tv_sec = TIMER_INTERVAL;
+ itimer.it_value.tv_usec = itimer.it_interval.tv_usec = 0;
+
+ signal(SIGALRM, catch_alarm);
+ setitimer(ITIMER_REAL, &itimer, 0);
+}
+
+void
+timerUpdate(IntervalTimer * itimer)
+{
+ int i, delta;
+
+ delta = elapsed;
+ elapsed = 0;
+
+ if (delta <= 0)
+ return;
+
+ for (i = 0; i < TIMER_ARRAY_SIZE; ++i) {
+ if (itimer[i].interval > 0 && (itimer[i].left -= delta) <= 0) {
+ itimer[i].left = itimer[i].interval;
+ itimer[i].expire = TRUE;
+ DBGV("timerUpdate: timer %u expired\n", i);
+ }
+ }
+}
+
+void
+timerStop(UInteger16 index, IntervalTimer * itimer)
+{
+ if (index >= TIMER_ARRAY_SIZE)
+ return;
+
+ itimer[index].interval = 0;
+}
+
+void
+timerStart(UInteger16 index, UInteger16 interval, IntervalTimer * itimer)
+{
+ if (index >= TIMER_ARRAY_SIZE)
+ return;
+
+ itimer[index].expire = FALSE;
+ itimer[index].left = interval;
+ itimer[index].interval = itimer[index].left;
+
+ DBGV("timerStart: set timer %d to %d\n", index, interval);
+}
+
+Boolean
+timerExpired(UInteger16 index, IntervalTimer * itimer)
+{
+ timerUpdate(itimer);
+
+ if (index >= TIMER_ARRAY_SIZE)
+ return FALSE;
+
+ if (!itimer[index].expire)
+ return FALSE;
+
+ itimer[index].expire = FALSE;
+
+ return TRUE;
+}
diff --git a/third_party/ptpd-1.1.0/src/probe.c b/third_party/ptpd-1.1.0/src/probe.c new file mode 100644 index 0000000..8fdadcf --- /dev/null +++ b/third_party/ptpd-1.1.0/src/probe.c @@ -0,0 +1,284 @@ +/**
+ * @file probe.c
+ * @date Tue Jul 20 16:13:30 2010
+ *
+ * @brief Management packet handling
+ *
+ *
+ */
+
+#include "ptpd.h"
+
+#define KEY_ARRAY_LEN 6
+UInteger8 management_key_array[KEY_ARRAY_LEN] =
+{PTP_MM_OBTAIN_IDENTITY, PTP_MM_GET_DEFAULT_DATA_SET, PTP_MM_GET_CURRENT_DATA_SET,
+PTP_MM_GET_PARENT_DATA_SET, PTP_MM_GET_PORT_DATA_SET, PTP_MM_GET_GLOBAL_TIME_DATA_SET};
+
+void displayHeader(MsgHeader *);
+void displayManagement(MsgHeader *, MsgManagement *);
+
+void
+probe(RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ UInteger16 i;
+ UInteger16 length;
+ TimeInternal interval, now, finish, timestamp;
+
+ /* 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) {
+ ERROR("send not supported for that management message\n");
+ return;
+ }
+ /* init */
+ if (!netInit(&ptpClock->netPath, rtOpts, ptpClock)) {
+ ERROR("failed to initialize network\n");
+ return;
+ }
+ initData(rtOpts, ptpClock);
+ msgPackHeader(ptpClock->msgObuf, ptpClock);
+
+ memset(&ptpClock->msgTmp.manage, 0, sizeof(MsgManagement));
+ ptpClock->msgTmp.manage.targetCommunicationTechnology = PTP_DEFAULT;
+
+ /* send */
+ for (i = 0; i < KEY_ARRAY_LEN; ++i) {
+ if (rtOpts->probe_management_key > 0) {
+ ptpClock->msgTmp.manage.managementMessageKey = rtOpts->probe_management_key;
+ ptpClock->msgTmp.manage.recordKey = rtOpts->probe_record_key;
+ } else
+ ptpClock->msgTmp.manage.managementMessageKey = management_key_array[i];
+
+ if (!(length = msgPackManagement(ptpClock->msgObuf, &ptpClock->msgTmp.manage, ptpClock))) {
+ ERROR("failed to pack management message\n");
+ return;
+ }
+ printf("\n(sending managementMessageKey %hhu)\n", ptpClock->msgTmp.manage.managementMessageKey);
+
+ if (!netSendGeneral(ptpClock->msgObuf, length, &ptpClock->netPath)) {
+ ERROR("failed to send message\n");
+ return;
+ }
+ if (rtOpts->probe_management_key > 0)
+ break;
+ }
+
+ getTime(&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);
+
+ netRecvEvent(ptpClock->msgIbuf, ×tamp, &ptpClock->netPath);
+
+ if (netRecvGeneral(ptpClock->msgIbuf, &ptpClock->netPath)) {
+ msgUnpackHeader(ptpClock->msgIbuf, &ptpClock->msgTmpHeader);
+
+ if (ptpClock->msgTmpHeader.control == PTP_MANAGEMENT_MESSAGE) {
+ msgUnpackManagement(ptpClock->msgIbuf, &ptpClock->msgTmp.manage);
+ msgUnpackManagementPayload(ptpClock->msgIbuf, &ptpClock->msgTmp.manage);
+
+ displayManagement(&ptpClock->msgTmpHeader, &ptpClock->msgTmp.manage);
+ }
+ fflush(stdout);
+ }
+ getTime(&now);
+ if (now.seconds > finish.seconds || (now.seconds == finish.seconds
+ && now.nanoseconds > finish.nanoseconds))
+ break;
+ }
+
+ /* done */
+ printf("\n");
+ ptpdShutdown();
+
+ exit(0);
+}
+
+void
+displayHeader(MsgHeader * header)
+{
+ printf(
+ " sourceCommunicationTechnology %hhu\n"
+ " sourceUuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n"
+ " sourcePortId %hu\n",
+ header->sourceCommunicationTechnology,
+ header->sourceUuid[0], header->sourceUuid[1], header->sourceUuid[2],
+ header->sourceUuid[3], header->sourceUuid[4], header->sourceUuid[5],
+ header->sourcePortId);
+}
+
+void
+displayManagement(MsgHeader * header, MsgManagement * manage)
+{
+ Integer16 i;
+
+ switch (manage->managementMessageKey) {
+ case PTP_MM_CLOCK_IDENTITY:
+ printf("\n");
+ displayHeader(header);
+ printf(" managementMessageKey %hhu (PTP_MM_CLOCK_IDENTITY)\n", manage->managementMessageKey);
+ printf(" clockCommunicationTechnology %hhu\n", manage->payload.clockIdentity.clockCommunicationTechnology);
+ printf(" clockUuidField %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ manage->payload.clockIdentity.clockUuidField[0], manage->payload.clockIdentity.clockUuidField[1],
+ manage->payload.clockIdentity.clockUuidField[2], manage->payload.clockIdentity.clockUuidField[3],
+ manage->payload.clockIdentity.clockUuidField[4], manage->payload.clockIdentity.clockUuidField[5]);
+ printf(" clockPortField %hu\n", manage->payload.clockIdentity.clockPortField);
+ printf(" manufacturerIdentity ");
+ for (i = 0; i < MANUFACTURER_ID_LENGTH && manage->payload.clockIdentity.manufacturerIdentity[i]; ++i)
+ putchar(manage->payload.clockIdentity.manufacturerIdentity[i]);
+ putchar('\n');
+ break;
+
+ case PTP_MM_DEFAULT_DATA_SET:
+ printf("\n");
+ displayHeader(header);
+ printf(" managementMessageKey %hhu (PTP_MM_DEFAULT_DATA_SET)\n", manage->managementMessageKey);
+ printf(" clockCommunicationTechnology %hhu\n", manage->payload.defaultData.clockCommunicationTechnology);
+ printf(" clockUuidField %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ manage->payload.defaultData.clockUuidField[0], manage->payload.defaultData.clockUuidField[1],
+ manage->payload.defaultData.clockUuidField[2], manage->payload.defaultData.clockUuidField[3],
+ manage->payload.defaultData.clockUuidField[4], manage->payload.defaultData.clockUuidField[5]);
+ printf(" clockPortField %hu\n", manage->payload.defaultData.clockPortField);
+ printf(" clockStratum %hhu\n", manage->payload.defaultData.clockStratum);
+ printf(" clockIdentifier ");
+ for (i = 0; i < PTP_CODE_STRING_LENGTH && manage->payload.defaultData.clockIdentifier[i]; ++i)
+ putchar(manage->payload.defaultData.clockIdentifier[i]);
+ putchar('\n');
+ printf(" clockVariance %hd\n", manage->payload.defaultData.clockVariance);
+ printf(" clockFollowupCapable %hhu\n", manage->payload.defaultData.clockFollowupCapable);
+ printf(" preferred %hhu\n", manage->payload.defaultData.preferred);
+ printf(" initializable %hhu\n", manage->payload.defaultData.initializable);
+ printf(" externalTiming %hhu\n", manage->payload.defaultData.externalTiming);
+ printf(" isBoundaryClock %hhu\n", manage->payload.defaultData.isBoundaryClock);
+ printf(" syncInterval %hhd\n", manage->payload.defaultData.syncInterval);
+ printf(" subdomainName ");
+ for (i = 0; i < PTP_SUBDOMAIN_NAME_LENGTH && manage->payload.defaultData.subdomainName[i]; ++i)
+ putchar(manage->payload.defaultData.subdomainName[i]);
+ putchar('\n');
+ printf(" numberPorts %hu\n", manage->payload.defaultData.numberPorts);
+ printf(" numberForeignRecords %hu\n", manage->payload.defaultData.numberForeignRecords);
+ break;
+
+ case PTP_MM_CURRENT_DATA_SET:
+ printf("\n");
+ displayHeader(header);
+ printf(" managementMessageKey %hhu (PTP_MM_CURRENT_DATA_SET)\n", manage->managementMessageKey);
+ printf(" stepsRemoved %hu\n", manage->payload.current.stepsRemoved);
+ printf(" offsetFromMaster %s%u.%09d\n",
+ manage->payload.current.offsetFromMaster.nanoseconds & 0x80000000 ? "-" : "",
+ manage->payload.current.offsetFromMaster.seconds,
+ manage->payload.current.offsetFromMaster.nanoseconds & ~0x80000000);
+ printf(" oneWayDelay %s%u.%09d\n",
+ manage->payload.current.oneWayDelay.nanoseconds & 0x80000000 ? "-" : "",
+ manage->payload.current.oneWayDelay.seconds,
+ manage->payload.current.oneWayDelay.nanoseconds & ~0x80000000);
+ break;
+
+ case PTP_MM_PARENT_DATA_SET:
+ printf("\n");
+ displayHeader(header);
+ printf(" managementMessageKey %hhu (PTP_MM_PARENT_DATA_SET)\n", manage->managementMessageKey);
+ printf(" parentCommunicationTechnology %hhu\n", manage->payload.parent.parentCommunicationTechnology);
+ printf(" parentUuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ manage->payload.parent.parentUuid[0], manage->payload.parent.parentUuid[1],
+ manage->payload.parent.parentUuid[2], manage->payload.parent.parentUuid[3],
+ manage->payload.parent.parentUuid[4], manage->payload.parent.parentUuid[5]);
+ printf(" parentPortId %hu\n", manage->payload.parent.parentPortId);
+ printf(" parentLastSyncSequenceNumber %hu\n", manage->payload.parent.parentLastSyncSequenceNumber);
+ printf(" parentFollowupCapable %hhu\n", manage->payload.parent.parentFollowupCapable);
+ printf(" parentExternalTiming %hhu\n", manage->payload.parent.parentExternalTiming);
+ printf(" parentVariance %hd\n", manage->payload.parent.parentVariance);
+ printf(" parentStats %hhu\n", manage->payload.parent.parentStats);
+ printf(" observedVariance %hd\n", manage->payload.parent.observedVariance);
+ printf(" observedDrift %d\n", manage->payload.parent.observedDrift);
+ printf(" utcReasonable %hhu\n", manage->payload.parent.utcReasonable);
+ printf(" grandmasterCommunicationTechnology %hhu\n", manage->payload.parent.grandmasterCommunicationTechnology);
+ printf(" grandmasterUuidField %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ manage->payload.parent.grandmasterUuidField[0], manage->payload.parent.grandmasterUuidField[1],
+ manage->payload.parent.grandmasterUuidField[2], manage->payload.parent.grandmasterUuidField[3],
+ manage->payload.parent.grandmasterUuidField[4], manage->payload.parent.grandmasterUuidField[5]);
+ printf(" grandmasterPortIdField %hu\n", manage->payload.parent.grandmasterPortIdField);
+ printf(" grandmasterStratum %hhu\n", manage->payload.parent.grandmasterStratum);
+ printf(" grandmasterIdentifier ");
+ for (i = 0; i < PTP_CODE_STRING_LENGTH && manage->payload.parent.grandmasterIdentifier[i]; ++i)
+ putchar(manage->payload.parent.grandmasterIdentifier[i]);
+ putchar('\n');
+ printf(" grandmasterVariance %hd\n", manage->payload.parent.grandmasterVariance);
+ printf(" grandmasterPreferred %hhu\n", manage->payload.parent.grandmasterPreferred);
+ printf(" grandmasterIsBoundaryClock %hhu\n", manage->payload.parent.grandmasterIsBoundaryClock);
+ printf(" grandmasterSequenceNumber %hu\n", manage->payload.parent.grandmasterSequenceNumber);
+ break;
+
+ case PTP_MM_PORT_DATA_SET:
+ printf("\n");
+ displayHeader(header);
+ printf(" managementMessageKey %hhu (PTP_MM_PORT_DATA_SET)\n", manage->managementMessageKey);
+ printf(" returnedPortNumber %hu\n", manage->payload.port.returnedPortNumber);
+ printf(" portState %hhu\n", manage->payload.port.portState);
+ printf(" lastSyncEventSequenceNumber %hu\n", manage->payload.port.lastSyncEventSequenceNumber);
+ printf(" lastGeneralEventSequenceNumber %hu\n", manage->payload.port.lastGeneralEventSequenceNumber);
+ printf(" portCommunicationTechnology %hhu\n", manage->payload.port.portCommunicationTechnology);
+ printf(" portUuidField %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ manage->payload.port.portUuidField[0], manage->payload.port.portUuidField[1],
+ manage->payload.port.portUuidField[2], manage->payload.port.portUuidField[3],
+ manage->payload.port.portUuidField[4], manage->payload.port.portUuidField[5]);
+ printf(" portIdField %hu\n", manage->payload.port.portIdField);
+ printf(" burstEnabled %hhu\n", manage->payload.port.burstEnabled);
+ printf(" subdomainAddressOctets %hhu\n", manage->payload.port.subdomainAddressOctets);
+ printf(" eventPortAddressOctets %hhu\n", manage->payload.port.eventPortAddressOctets);
+ printf(" generalPortAddressOctets %hhu\n", manage->payload.port.generalPortAddressOctets);
+ printf(" subdomainAddress ");
+ printf("%hhu", manage->payload.port.subdomainAddress[0]);
+ for (i = 1; i < SUBDOMAIN_ADDRESS_LENGTH; ++i)
+ printf(".%hhu", manage->payload.port.subdomainAddress[i]);
+ putchar('\n');
+ printf(" eventPortAddress %hu\n", *(UInteger16 *) manage->payload.port.eventPortAddress);
+ printf(" generalPortAddress %hu\n", *(UInteger16 *) manage->payload.port.generalPortAddress);
+ break;
+
+ case PTP_MM_GLOBAL_TIME_DATA_SET:
+ printf("\n");
+ displayHeader(header);
+ printf(" managementMessageKey %hhu (PTP_MM_GLOBAL_TIME_DATA_SET)\n", manage->managementMessageKey);
+ printf(" localTime %s%u.%09d\n",
+ manage->payload.globalTime.localTime.nanoseconds & 0x80000000 ? "-" : "",
+ manage->payload.globalTime.localTime.seconds,
+ manage->payload.globalTime.localTime.nanoseconds & ~0x80000000);
+ printf(" currentUtcOffset %hd\n", manage->payload.globalTime.currentUtcOffset);
+ printf(" leap59 %hhu\n", manage->payload.globalTime.leap59);
+ printf(" leap61 %hhu\n", manage->payload.globalTime.leap61);
+ printf(" epochNumber %hu\n", manage->payload.globalTime.epochNumber);
+ break;
+
+
+ case PTP_MM_FOREIGN_DATA_SET:
+ printf("\n");
+ displayHeader(header);
+ printf(" managementMessageKey %hhu (PTP_MM_FOREIGN_DATA_SET)\n", manage->managementMessageKey);
+ printf(" returnedPortNumber %hu\n", manage->payload.foreign.returnedPortNumber);
+ printf(" returnedRecordNumber %hu\n", manage->payload.foreign.returnedRecordNumber);
+ printf(" foreignMasterCommunicationTechnology %hu\n", manage->payload.foreign.foreignMasterCommunicationTechnology);
+ printf(" foreignMasterUuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ manage->payload.foreign.foreignMasterUuid[0], manage->payload.foreign.foreignMasterUuid[1],
+ manage->payload.foreign.foreignMasterUuid[2], manage->payload.foreign.foreignMasterUuid[3],
+ manage->payload.foreign.foreignMasterUuid[4], manage->payload.foreign.foreignMasterUuid[5]);
+
+ printf(" foreignMasterPortId %hu\n", manage->payload.foreign.foreignMasterPortId);
+ printf(" foreignMasterSyncs %hu\n", manage->payload.foreign.foreignMasterSyncs);
+ break;
+
+ case PTP_MM_NULL:
+ printf("\n");
+ displayHeader(header);
+ printf(" managementMessageKey %hhu (NULL)\n", manage->managementMessageKey);
+ break;
+
+ default:
+ break;
+ }
+
+ return;
+}
diff --git a/third_party/ptpd-1.1.0/src/protocol.c b/third_party/ptpd-1.1.0/src/protocol.c new file mode 100644 index 0000000..4db8e95 --- /dev/null +++ b/third_party/ptpd-1.1.0/src/protocol.c @@ -0,0 +1,865 @@ +/**
+ * @file protocol.c
+ * @date Wed Jun 23 09:40:39 2010
+ *
+ * @brief The code that handles the IEEE-1588 protocol and state machine
+ *
+ *
+ */
+
+#include "ptpd.h"
+
+Boolean doInit(RunTimeOpts *, PtpClock *);
+void doState(RunTimeOpts *, PtpClock *);
+void toState(UInteger8, RunTimeOpts *, PtpClock *);
+
+void handle(RunTimeOpts *, PtpClock *);
+void 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 *);
+
+MsgSync *addForeign(Octet *, MsgHeader *, PtpClock *);
+
+/* Initialize the PTPD stack.
+ *
+ * NB: This function was added by TI!
+ */
+void protocol_first(RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ DBG("event POWERUP\n");
+
+ toState(PTP_INITIALIZING, rtOpts, ptpClock);
+
+ if(ptpClock->port_state != PTP_INITIALIZING)
+ doState(rtOpts, ptpClock);
+ else if(!doInit(rtOpts, ptpClock))
+ return;
+
+ if(ptpClock->message_activity)
+ DBGV("activity\n");
+#if 0
+ else
+ DBGV("no activity\n");
+#endif
+}
+
+/* Perform one iteration of the PTPD state machine. This function does not
+ * block and may be used in non-RTOS systems where the main loop needs to do
+ * more than process the PTPD stack.
+ *
+ * NB: This function was added by TI!
+ */
+void protocol_loop(RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ if(ptpClock->port_state != PTP_INITIALIZING)
+ doState(rtOpts, ptpClock);
+ else if(!doInit(rtOpts, ptpClock))
+ return;
+
+ if(ptpClock->message_activity)
+ DBGV("activity\n");
+#if 0
+ else
+ DBGV("no activity\n");
+#endif
+}
+
+/* loop forever. doState() has a switch for the actions and events to be
+ checked for 'port_state'. the actions and events may or may not change
+ 'port_state' by calling toState(), but once they are done we loop around
+ again and perform the actions required for the new 'port_state'. */
+void
+protocol(RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ DBG("event POWERUP\n");
+
+ toState(PTP_INITIALIZING, rtOpts, ptpClock);
+
+ DBGV("Debug Initializing...");
+
+ for (;;) {
+ if (ptpClock->port_state != PTP_INITIALIZING)
+ doState(rtOpts, ptpClock);
+ else if (!doInit(rtOpts, ptpClock))
+ return;
+
+ if (ptpClock->message_activity)
+ DBGV("activity\n");
+ else
+ DBGV("no activity\n");
+ }
+}
+
+Boolean
+doInit(RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ DBG("manufacturerIdentity: %s\n", MANUFACTURER_ID);
+
+ /* initialize networking */
+ netShutdown(&ptpClock->netPath);
+ if (!netInit(&ptpClock->netPath, rtOpts, ptpClock)) {
+ ERROR("failed to initialize network\n");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ return FALSE;
+ }
+ /* initialize other stuff */
+ initData(rtOpts, ptpClock);
+ initTimer();
+ initClock(rtOpts, ptpClock);
+ m1(ptpClock);
+ msgPackHeader(ptpClock->msgObuf, ptpClock);
+
+ DBG("sync message interval: %d\n", PTP_SYNC_INTERVAL_TIMEOUT(ptpClock->sync_interval));
+ DBG("clock identifier: %s\n", ptpClock->clock_identifier);
+ 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("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],
+ ptpClock->port_uuid_field[3], ptpClock->port_uuid_field[4], ptpClock->port_uuid_field[5]);
+ DBG("PTP subdomain name: %s\n", ptpClock->subdomain_name);
+ DBG("subdomain address: %hhx.%hhx.%hhx.%hhx\n",
+ ptpClock->subdomain_address[0], ptpClock->subdomain_address[1],
+ ptpClock->subdomain_address[2], ptpClock->subdomain_address[3]);
+ DBG("event port address: %hhx %hhx\n",
+ ptpClock->event_port_address[0], ptpClock->event_port_address[1]);
+ DBG("general port address: %hhx %hhx\n",
+ ptpClock->general_port_address[0], ptpClock->general_port_address[1]);
+
+ toState(PTP_LISTENING, rtOpts, ptpClock);
+ return TRUE;
+}
+
+/* handle actions and events for 'port_state' */
+void
+doState(RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ UInteger8 state;
+
+ ptpClock->message_activity = FALSE;
+
+ switch (ptpClock->port_state) {
+ case PTP_LISTENING:
+ case PTP_PASSIVE:
+ case PTP_SLAVE:
+ case PTP_MASTER:
+ if (ptpClock->record_update) {
+ ptpClock->record_update = FALSE;
+ state = bmc(ptpClock->foreign, rtOpts, ptpClock);
+ if (state != ptpClock->port_state)
+ toState(state, rtOpts, ptpClock);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (ptpClock->port_state) {
+ case PTP_FAULTY:
+ /* imaginary troubleshooting */
+
+ DBG("event FAULT_CLEARED\n");
+ toState(PTP_INITIALIZING, rtOpts, ptpClock);
+ return;
+
+ case PTP_LISTENING:
+ case PTP_PASSIVE:
+ case PTP_UNCALIBRATED:
+ case PTP_SLAVE:
+ handle(rtOpts, 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) {
+ m1(ptpClock);
+ toState(PTP_MASTER, rtOpts, ptpClock);
+ } else if (ptpClock->port_state != PTP_LISTENING)
+ toState(PTP_LISTENING, rtOpts, ptpClock);
+ }
+ break;
+
+ case PTP_MASTER:
+ if (timerExpired(SYNC_INTERVAL_TIMER, ptpClock->itimer)) {
+ DBGV("event SYNC_INTERVAL_TIMEOUT_EXPIRES\n");
+ issueSync(rtOpts, ptpClock);
+ }
+ handle(rtOpts, ptpClock);
+
+ if (rtOpts->slaveOnly || ptpClock->clock_stratum == 255)
+ toState(PTP_LISTENING, rtOpts, ptpClock);
+
+ break;
+
+ case PTP_DISABLED:
+ handle(rtOpts, ptpClock);
+ break;
+
+ default:
+ DBG("do unrecognized state\n");
+ break;
+ }
+}
+
+/* perform actions required when leaving 'port_state' and entering 'state' */
+void
+toState(UInteger8 state, RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ ptpClock->message_activity = TRUE;
+
+ /* leaving state tasks */
+ switch (ptpClock->port_state) {
+ case PTP_MASTER:
+ timerStop(SYNC_INTERVAL_TIMER, ptpClock->itimer);
+ timerStart(SYNC_RECEIPT_TIMER, PTP_SYNC_RECEIPT_TIMEOUT(ptpClock->sync_interval), ptpClock->itimer);
+ break;
+
+ case PTP_SLAVE:
+ initClock(rtOpts, ptpClock);
+ break;
+
+ default:
+ break;
+ }
+
+ /* entering state tasks */
+ switch (state) {
+ case PTP_INITIALIZING:
+ DBG("state PTP_INITIALIZING\n");
+ timerStop(SYNC_RECEIPT_TIMER, ptpClock->itimer);
+
+ ptpClock->port_state = PTP_INITIALIZING;
+ break;
+
+ case PTP_FAULTY:
+ DBG("state PTP_FAULTY\n");
+ timerStop(SYNC_RECEIPT_TIMER, ptpClock->itimer);
+
+ ptpClock->port_state = PTP_FAULTY;
+ break;
+
+ case PTP_DISABLED:
+ DBG("state change to PTP_DISABLED\n");
+ timerStop(SYNC_RECEIPT_TIMER, ptpClock->itimer);
+
+ ptpClock->port_state = PTP_DISABLED;
+ break;
+
+ case PTP_LISTENING:
+ DBG("state PTP_LISTENING\n");
+
+ timerStart(SYNC_RECEIPT_TIMER, PTP_SYNC_RECEIPT_TIMEOUT(ptpClock->sync_interval), ptpClock->itimer);
+
+ ptpClock->port_state = PTP_LISTENING;
+ break;
+
+ case PTP_MASTER:
+ DBG("state PTP_MASTER\n");
+
+ if (ptpClock->port_state != PTP_PRE_MASTER)
+ timerStart(SYNC_INTERVAL_TIMER, PTP_SYNC_INTERVAL_TIMEOUT(ptpClock->sync_interval), ptpClock->itimer);
+
+ timerStop(SYNC_RECEIPT_TIMER, ptpClock->itimer);
+
+ ptpClock->port_state = PTP_MASTER;
+ break;
+
+ case PTP_PASSIVE:
+ DBG("state PTP_PASSIVE\n");
+ ptpClock->port_state = PTP_PASSIVE;
+ break;
+
+ case PTP_UNCALIBRATED:
+ DBG("state PTP_UNCALIBRATED\n");
+ ptpClock->port_state = PTP_UNCALIBRATED;
+ break;
+
+ case PTP_SLAVE:
+ DBG("state PTP_PTP_SLAVE\n");
+
+ initClock(rtOpts, 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
+ */
+ ptpClock->Q = 0;
+ ptpClock->R = getRand(&ptpClock->random_seed) % 4 + 4;
+ DBG("Q = %d, R = %d\n", ptpClock->Q, ptpClock->R);
+
+ ptpClock->waitingForFollow = FALSE;
+ ptpClock->delay_req_send_time.seconds = 0;
+ ptpClock->delay_req_send_time.nanoseconds = 0;
+ ptpClock->delay_req_receive_time.seconds = 0;
+ ptpClock->delay_req_receive_time.nanoseconds = 0;
+
+ timerStart(SYNC_RECEIPT_TIMER, PTP_SYNC_RECEIPT_TIMEOUT(ptpClock->sync_interval), ptpClock->itimer);
+
+ ptpClock->port_state = PTP_SLAVE;
+ break;
+
+ default:
+ DBG("to unrecognized state\n");
+ break;
+ }
+
+ NOTIFY("Port state changed to %s\n", translatePortState(ptpClock));
+
+ if (rtOpts->displayStats)
+ displayStats(rtOpts, ptpClock);
+}
+
+/* check and handle received messages */
+void
+handle(RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ int ret;
+ ssize_t length;
+ Boolean isFromSelf;
+ TimeInternal time = {0, 0};
+
+ if (!ptpClock->message_activity) {
+ ret = netSelect(0, &ptpClock->netPath);
+ if (ret < 0) {
+ PERROR("failed to poll sockets");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ return;
+ } else if (!ret) {
+ DBGV("handle: nothing\n");
+ return;
+ }
+ /* else length > 0 */
+ }
+ DBGV("handle: something\n");
+
+ length = netRecvEvent(ptpClock->msgIbuf, &time, &ptpClock->netPath);
+ if (length < 0) {
+ PERROR("failed to receive on the event socket");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ return;
+ } else if (!length) {
+ length = netRecvGeneral(ptpClock->msgIbuf, &ptpClock->netPath);
+ if (length < 0) {
+ PERROR("failed to receive on the general socket");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ return;
+ } else if (!length)
+ return;
+ }
+ ptpClock->message_activity = TRUE;
+
+ if (!msgPeek(ptpClock->msgIbuf, length))
+ return;
+
+ if (length < HEADER_LENGTH) {
+ ERROR("message shorter than header length\n");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ return;
+ }
+ msgUnpackHeader(ptpClock->msgIbuf, &ptpClock->msgTmpHeader);
+
+ DBGV("event 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",
+ ptpClock->msgTmpHeader.versionPTP,
+ ptpClock->msgTmpHeader.control,
+ ptpClock->msgTmpHeader.sourceUuid[0], ptpClock->msgTmpHeader.sourceUuid[1],
+ ptpClock->msgTmpHeader.sourceUuid[2], ptpClock->msgTmpHeader.sourceUuid[3],
+ ptpClock->msgTmpHeader.sourceUuid[4], ptpClock->msgTmpHeader.sourceUuid[5],
+ ptpClock->msgTmpHeader.sequenceId,
+ time.seconds, time.nanoseconds);
+
+ if (ptpClock->msgTmpHeader.versionPTP != VERSION_PTP) {
+ DBGV("ignore version %d message\n", ptpClock->msgTmpHeader.versionPTP);
+ return;
+ }
+ if (memcmp(ptpClock->msgTmpHeader.subdomain, ptpClock->subdomain_name,
+ PTP_SUBDOMAIN_NAME_LENGTH)) {
+ DBGV("ignore message from subdomain %s\n", ptpClock->msgTmpHeader.subdomain);
+ return;
+ }
+ isFromSelf = ptpClock->msgTmpHeader.sourceCommunicationTechnology == ptpClock->port_communication_technology
+ && ptpClock->msgTmpHeader.sourcePortId == ptpClock->port_id_field
+ && !memcmp(ptpClock->msgTmpHeader.sourceUuid, ptpClock->port_uuid_field, PTP_UUID_LENGTH);
+
+ /*
+ * subtract the inbound latency adjustment if it is not a loop back
+ * and the time stamp seems reasonable
+ */
+ if (!isFromSelf && time.seconds > 0)
+ subTime(&time, &time, &rtOpts->inboundLatency);
+
+ switch (ptpClock->msgTmpHeader.control) {
+ case PTP_SYNC_MESSAGE:
+ handleSync(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, &time, isFromSelf, rtOpts, ptpClock);
+ break;
+
+ case PTP_FOLLOWUP_MESSAGE:
+ handleFollowUp(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, rtOpts, ptpClock);
+ break;
+
+ case PTP_DELAY_REQ_MESSAGE:
+ handleDelayReq(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, &time, isFromSelf, rtOpts, ptpClock);
+ break;
+
+ case PTP_DELAY_RESP_MESSAGE:
+ handleDelayResp(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, rtOpts, ptpClock);
+ break;
+
+ case PTP_MANAGEMENT_MESSAGE:
+ handleManagement(&ptpClock->msgTmpHeader, ptpClock->msgIbuf, length, isFromSelf, rtOpts, ptpClock);
+ break;
+
+ default:
+ DBG("handle: unrecognized message\n");
+ break;
+ }
+}
+
+void
+handleSync(MsgHeader * header, Octet * msgIbuf, ssize_t length, TimeInternal * time, Boolean isFromSelf, RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ MsgSync *sync;
+ TimeInternal originTimestamp;
+
+ if (length < SYNC_PACKET_LENGTH) {
+ ERROR("short sync message\n");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ return;
+ }
+ switch (ptpClock->port_state) {
+ case PTP_FAULTY:
+ case PTP_INITIALIZING:
+ case PTP_DISABLED:
+ DBGV("handleSync: disreguard\n");
+ return;
+
+ case PTP_UNCALIBRATED:
+ case PTP_SLAVE:
+ if (isFromSelf) {
+ DBG("handleSync: ignore from self\n");
+ return;
+ }
+ if (getFlag(header->flags, PTP_SYNC_BURST) && !ptpClock->burst_enabled)
+ return;
+
+ DBGV("handleSync: looking for uuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ ptpClock->parent_uuid[0], ptpClock->parent_uuid[1], ptpClock->parent_uuid[2],
+ ptpClock->parent_uuid[3], ptpClock->parent_uuid[4], ptpClock->parent_uuid[5]);
+
+ if (header->sequenceId > ptpClock->parent_last_sync_sequence_number
+ && header->sourceCommunicationTechnology == ptpClock->parent_communication_technology
+ && header->sourcePortId == ptpClock->parent_port_id
+ && !memcmp(header->sourceUuid, ptpClock->parent_uuid, PTP_UUID_LENGTH)) {
+ /* addForeign() takes care of msgUnpackSync() */
+ ptpClock->record_update = TRUE;
+ sync = addForeign(ptpClock->msgIbuf, &ptpClock->msgTmpHeader, ptpClock);
+
+ if (sync->syncInterval != ptpClock->sync_interval) {
+ DBGV("message's sync interval is %d, but clock's is %d\n", sync->syncInterval, ptpClock->sync_interval);
+ /*
+ * spec recommends handling a sync interval
+ * discrepancy as a fault
+ */
+ }
+ ptpClock->sync_receive_time.seconds = time->seconds;
+ ptpClock->sync_receive_time.nanoseconds = time->nanoseconds;
+
+ if (!getFlag(header->flags, PTP_ASSIST)) {
+ ptpClock->waitingForFollow = FALSE;
+
+ toInternalTime(&originTimestamp, &sync->originTimestamp, &ptpClock->halfEpoch);
+ updateOffset(&originTimestamp, &ptpClock->sync_receive_time,
+ &ptpClock->ofm_filt, rtOpts, ptpClock);
+ updateClock(rtOpts, ptpClock);
+ } else {
+ ptpClock->waitingForFollow = TRUE;
+ }
+
+ s1(header, sync, ptpClock);
+
+ if (!(--ptpClock->R)) {
+ issueDelayReq(rtOpts, ptpClock);
+
+ ptpClock->Q = 0;
+ ptpClock->R = getRand(&ptpClock->random_seed) % (PTP_DELAY_REQ_INTERVAL - 2) + 2;
+ DBG("Q = %d, R = %d\n", ptpClock->Q, ptpClock->R);
+ }
+ DBGV("SYNC_RECEIPT_TIMER reset\n");
+ timerStart(SYNC_RECEIPT_TIMER, PTP_SYNC_RECEIPT_TIMEOUT(ptpClock->sync_interval), ptpClock->itimer);
+
+#ifndef NO_FILE_SYSTEM
+ if (rtOpts->recordFP != NULL)
+ fprintf(rtOpts->recordFP, "%d %llu\n",
+ header->sequenceId,
+ ((time->seconds * 1000000000ULL) +
+ time->nanoseconds));
+#endif
+ } else {
+ DBGV("handleSync: unwanted\n");
+ }
+
+ case PTP_MASTER:
+ default:
+ if (header->sourceCommunicationTechnology == ptpClock->clock_communication_technology
+ || header->sourceCommunicationTechnology == PTP_DEFAULT
+ || ptpClock->clock_communication_technology == PTP_DEFAULT) {
+ if (!isFromSelf) {
+ ptpClock->record_update = TRUE;
+ addForeign(ptpClock->msgIbuf, &ptpClock->msgTmpHeader, ptpClock);
+ } else if (ptpClock->port_state == PTP_MASTER && ptpClock->clock_followup_capable) {
+ addTime(time, time, &rtOpts->outboundLatency);
+ issueFollowup(time, rtOpts, ptpClock);
+ }
+ }
+ break;
+ }
+}
+
+void
+handleFollowUp(MsgHeader * header, Octet * msgIbuf, ssize_t length, Boolean isFromSelf, RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ MsgFollowUp *follow;
+ TimeInternal preciseOriginTimestamp;
+
+ if (length < FOLLOW_UP_PACKET_LENGTH) {
+ ERROR("short folow up message\n");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ return;
+ }
+ switch (ptpClock->port_state) {
+ case PTP_SLAVE:
+ if (isFromSelf) {
+ DBG("handleFollowUp: ignore from self\n");
+ return;
+ }
+ if (getFlag(header->flags, PTP_SYNC_BURST) && !ptpClock->burst_enabled)
+ return;
+
+ DBGV("handleFollowUp: looking for uuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ ptpClock->parent_uuid[0], ptpClock->parent_uuid[1], ptpClock->parent_uuid[2],
+ ptpClock->parent_uuid[3], ptpClock->parent_uuid[4], ptpClock->parent_uuid[5]);
+
+ follow = &ptpClock->msgTmp.follow;
+ msgUnpackFollowUp(ptpClock->msgIbuf, follow);
+
+ if (ptpClock->waitingForFollow
+ && follow->associatedSequenceId == ptpClock->parent_last_sync_sequence_number
+ && header->sourceCommunicationTechnology == ptpClock->parent_communication_technology
+ && header->sourcePortId == ptpClock->parent_port_id
+ && !memcmp(header->sourceUuid, ptpClock->parent_uuid, PTP_UUID_LENGTH)) {
+ ptpClock->waitingForFollow = FALSE;
+
+ toInternalTime(&preciseOriginTimestamp, &follow->preciseOriginTimestamp, &ptpClock->halfEpoch);
+ updateOffset(&preciseOriginTimestamp, &ptpClock->sync_receive_time,
+ &ptpClock->ofm_filt, rtOpts, ptpClock);
+ updateClock(rtOpts, ptpClock);
+ } else {
+ DBGV("handleFollowUp: unwanted\n");
+ }
+ break;
+
+ default:
+ DBGV("handleFollowUp: disreguard\n");
+ return;
+ }
+}
+
+void
+handleDelayReq(MsgHeader * header, Octet * msgIbuf, ssize_t length, TimeInternal * time, Boolean isFromSelf, RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ if (length < DELAY_REQ_PACKET_LENGTH) {
+ ERROR("short delay request message\n");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ return;
+ }
+ switch (ptpClock->port_state) {
+ case PTP_MASTER:
+ if (isFromSelf) {
+ DBG("handleDelayReq: ignore from self\n");
+ return;
+ }
+ if (header->sourceCommunicationTechnology == ptpClock->clock_communication_technology
+ || header->sourceCommunicationTechnology == PTP_DEFAULT
+ || ptpClock->clock_communication_technology == PTP_DEFAULT) {
+ issueDelayResp(time, &ptpClock->msgTmpHeader, rtOpts, ptpClock);
+ }
+ break;
+
+ case PTP_SLAVE:
+ if (isFromSelf) {
+ DBG("handleDelayReq: self\n");
+
+ 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);
+
+ if (ptpClock->delay_req_receive_time.seconds) {
+ updateDelay(&ptpClock->delay_req_send_time,
+ &ptpClock->delay_req_receive_time,
+ &ptpClock->owd_filt, rtOpts,
+ ptpClock);
+
+ ptpClock->delay_req_send_time.seconds = 0;
+ ptpClock->delay_req_send_time.nanoseconds = 0;
+ ptpClock->delay_req_receive_time.seconds = 0;
+ ptpClock->delay_req_receive_time.nanoseconds = 0;
+ }
+ }
+ break;
+
+ default:
+ DBGV("handleDelayReq: disreguard\n");
+ return;
+ }
+}
+
+void
+handleDelayResp(MsgHeader * header, Octet * msgIbuf, ssize_t length, Boolean isFromSelf, RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ MsgDelayResp *resp;
+
+ if (length < DELAY_RESP_PACKET_LENGTH) {
+ ERROR("short delay request message\n");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ return;
+ }
+ switch (ptpClock->port_state) {
+ case PTP_SLAVE:
+ if (isFromSelf) {
+ DBG("handleDelayResp: ignore from self\n");
+ return;
+ }
+ resp = &ptpClock->msgTmp.resp;
+ msgUnpackDelayResp(ptpClock->msgIbuf, resp);
+
+ if (ptpClock->sentDelayReq
+ && resp->requestingSourceSequenceId == ptpClock->sentDelayReqSequenceId
+ && resp->requestingSourceCommunicationTechnology == ptpClock->port_communication_technology
+ && resp->requestingSourcePortId == ptpClock->port_id_field
+ && !memcmp(resp->requestingSourceUuid, ptpClock->port_uuid_field, PTP_UUID_LENGTH)
+ && header->sourceCommunicationTechnology == ptpClock->parent_communication_technology
+ && header->sourcePortId == ptpClock->parent_port_id
+ && !memcmp(header->sourceUuid, ptpClock->parent_uuid, PTP_UUID_LENGTH)) {
+ ptpClock->sentDelayReq = FALSE;
+
+ toInternalTime(&ptpClock->delay_req_receive_time, &resp->delayReceiptTimestamp, &ptpClock->halfEpoch);
+
+ if (ptpClock->delay_req_send_time.seconds) {
+ updateDelay(&ptpClock->delay_req_send_time, &ptpClock->delay_req_receive_time,
+ &ptpClock->owd_filt, rtOpts, ptpClock);
+
+ ptpClock->delay_req_send_time.seconds = 0;
+ ptpClock->delay_req_send_time.nanoseconds = 0;
+ ptpClock->delay_req_receive_time.seconds = 0;
+ ptpClock->delay_req_receive_time.nanoseconds = 0;
+ }
+ } else {
+ DBGV("handleDelayResp: unwanted\n");
+ }
+ break;
+
+ default:
+ DBGV("handleDelayResp: disreguard\n");
+ return;
+ }
+}
+
+void
+handleManagement(MsgHeader * header, Octet * msgIbuf, ssize_t length, Boolean isFromSelf, RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ MsgManagement *manage;
+
+ UInteger8 state;
+
+ if (ptpClock->port_state == PTP_INITIALIZING)
+ return;
+
+ manage = &ptpClock->msgTmp.manage;
+ msgUnpackManagement(ptpClock->msgIbuf, manage);
+
+ if ((manage->targetCommunicationTechnology == ptpClock->clock_communication_technology
+ && !memcmp(manage->targetUuid, ptpClock->clock_uuid_field, PTP_UUID_LENGTH))
+ || ((manage->targetCommunicationTechnology == PTP_DEFAULT
+ || manage->targetCommunicationTechnology == ptpClock->clock_communication_technology)
+ && !sum(manage->targetUuid, PTP_UUID_LENGTH))) {
+ switch (manage->managementMessageKey) {
+ case PTP_MM_OBTAIN_IDENTITY:
+ case PTP_MM_GET_DEFAULT_DATA_SET:
+ case PTP_MM_GET_CURRENT_DATA_SET:
+ case PTP_MM_GET_PARENT_DATA_SET:
+ 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);
+ break;
+
+ default:
+ ptpClock->record_update = TRUE;
+ state = msgUnloadManagement(ptpClock->msgIbuf, manage, ptpClock, rtOpts);
+ if (state != ptpClock->port_state)
+ toState(state, rtOpts, ptpClock);
+ break;
+ }
+ } else {
+ DBG("handleManagement: unwanted\n");
+ }
+}
+
+/* pack and send various messages */
+void
+issueSync(RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ TimeInternal internalTime;
+ TimeRepresentation originTimestamp;
+
+ ++ptpClock->last_sync_event_sequence_number;
+ ptpClock->grandmaster_sequence_number = ptpClock->last_sync_event_sequence_number;
+
+ getTime(&internalTime);
+ fromInternalTime(&internalTime, &originTimestamp, ptpClock->halfEpoch);
+ msgPackSync(ptpClock->msgObuf, FALSE, &originTimestamp, ptpClock);
+
+ if (!netSendEvent(ptpClock->msgObuf, SYNC_PACKET_LENGTH, &ptpClock->netPath))
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ else
+ DBGV("sent sync message\n");
+}
+
+void
+issueFollowup(TimeInternal * time, RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ TimeRepresentation preciseOriginTimestamp;
+
+ ++ptpClock->last_general_event_sequence_number;
+
+ 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);
+ else
+ DBGV("sent followup message\n");
+}
+
+void
+issueDelayReq(RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ TimeInternal internalTime;
+ TimeRepresentation originTimestamp;
+
+ ptpClock->sentDelayReq = TRUE;
+ ptpClock->sentDelayReqSequenceId = ++ptpClock->last_sync_event_sequence_number;
+
+ getTime(&internalTime);
+ fromInternalTime(&internalTime, &originTimestamp, ptpClock->halfEpoch);
+ msgPackDelayReq(ptpClock->msgObuf, FALSE, &originTimestamp, ptpClock);
+
+ if (!netSendEvent(ptpClock->msgObuf, DELAY_REQ_PACKET_LENGTH, &ptpClock->netPath))
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ else
+ DBGV("sent delay request message\n");
+}
+
+void
+issueDelayResp(TimeInternal * time, MsgHeader * header, RunTimeOpts * rtOpts, 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);
+ else
+ DBGV("sent delay response message\n");
+}
+
+void
+issueManagement(MsgHeader * header, MsgManagement * manage, RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ UInteger16 length;
+
+ ++ptpClock->last_general_event_sequence_number;
+
+ if (!(length = msgPackManagementResponse(ptpClock->msgObuf, header, manage, ptpClock)))
+ return;
+
+ if (!netSendGeneral(ptpClock->msgObuf, length, &ptpClock->netPath))
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ else
+ DBGV("sent management message\n");
+}
+
+/* add or update an entry in the foreign master data set */
+MsgSync *
+addForeign(Octet * buf, MsgHeader * header, PtpClock * ptpClock)
+{
+ int i, j;
+ Boolean found = FALSE;
+
+ DBGV("updateForeign\n");
+
+ j = ptpClock->foreign_record_best;
+ for (i = 0; i < ptpClock->number_foreign_records; ++i) {
+ if (header->sourceCommunicationTechnology == ptpClock->foreign[j].foreign_master_communication_technology
+ && header->sourcePortId == ptpClock->foreign[j].foreign_master_port_id
+ && !memcmp(header->sourceUuid, ptpClock->foreign[j].foreign_master_uuid, PTP_UUID_LENGTH)) {
+ ++ptpClock->foreign[j].foreign_master_syncs;
+ found = TRUE;
+ DBGV("updateForeign: update record %d\n", j);
+ break;
+ }
+ j = (j + 1) % ptpClock->number_foreign_records;
+ }
+
+ if (!found) {
+ if (ptpClock->number_foreign_records < ptpClock->max_foreign_records)
+ ++ptpClock->number_foreign_records;
+
+ j = ptpClock->foreign_record_i;
+
+ ptpClock->foreign[j].foreign_master_communication_technology =
+ header->sourceCommunicationTechnology;
+ ptpClock->foreign[j].foreign_master_port_id =
+ header->sourcePortId;
+ memcpy(ptpClock->foreign[j].foreign_master_uuid,
+ header->sourceUuid, PTP_UUID_LENGTH);
+
+ DBG("updateForeign: new record (%d,%d) %d %d %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ ptpClock->foreign_record_i, ptpClock->number_foreign_records,
+ ptpClock->foreign[j].foreign_master_communication_technology,
+ ptpClock->foreign[j].foreign_master_port_id,
+ ptpClock->foreign[j].foreign_master_uuid[0], ptpClock->foreign[j].foreign_master_uuid[1],
+ ptpClock->foreign[j].foreign_master_uuid[2], ptpClock->foreign[j].foreign_master_uuid[3],
+ ptpClock->foreign[j].foreign_master_uuid[4], ptpClock->foreign[j].foreign_master_uuid[5]);
+
+ ptpClock->foreign_record_i = (ptpClock->foreign_record_i + 1) % ptpClock->max_foreign_records;
+ }
+ msgUnpackHeader(buf, &ptpClock->foreign[j].header);
+ msgUnpackSync(buf, &ptpClock->foreign[j].sync);
+
+ return &ptpClock->foreign[j].sync;
+}
diff --git a/third_party/ptpd-1.1.0/src/ptpd.8 b/third_party/ptpd-1.1.0/src/ptpd.8 new file mode 100644 index 0000000..2452d7f --- /dev/null +++ b/third_party/ptpd-1.1.0/src/ptpd.8 @@ -0,0 +1,143 @@ +.\" -*- nroff -*"
+.TH ptpd 8 "August 9, 2010" "version 1.1.0" "Precision Time Protocol daemon"
+.SH NAME
+ptpd \- Precision Time Protocol daemon
+.SH SYNOPSIS
+.B ptpd
+
+[-?]
+[-c]
+[-S]
+[-f FILE]
+[-R FILE]
+[-d]
+[-D]
+[-x]
+[-M NUMBER]
+[-t]
+[-T ttl]
+[-a NUMBER,NUMBER]
+[-w NUMBER]
+[-b NAME]
+[-u ADDRESS]
+[-l NUMBER,NUMBER]
+[-o NUMBER]
+[-e NUMBER]
+[-y NUMBER]
+[-m NUMBER]
+[-g]
+[-p]
+[-s NUMBER]
+[-i NAME]
+[-v NUMBER]
+[-n NAME]
+[-k NUMBER,NUMBER]
+
+.SH DESCRIPTION
+Implements version 1 of the Precision Time Protocol (PTP) as defined
+by the IEEE 1588 standard. PTP was developed to provide very precise
+time coordination of LAN connected computers.
+.PP
+PTPd is a complete implementation of the IEEE 1588 specification for a
+standard (non-boundary) clock. PTPd has been tested with and is known
+to work properly with other IEEE 1588 implementations. The source code
+for PTPd is freely available under a BSD-style license. Thanks to
+contributions from users, PTPd is becoming an increasingly portable,
+interoperable, and stable IEEE 1588 implementation.
+.PP
+For more information, see http://ptpd.sourceforge.net/
+.SH OPTIONS
+.TP
+.B \-?
+display a short help text
+.TP
+.B \-c
+run in command line (non-daemon) mode
+.TP
+.B \-S
+log information to syslog
+.TP
+.B \-f FILE
+send output to FILE
+.TP
+.B \-R FILE
+record quality data to FILE
+.TP
+.B \-d
+display stats
+.TP
+.B \-D
+display stats in .csv format
+.TP
+.B \-x
+do not reset the clock if off by more than one second
+.TP
+.B \-M NUMBER
+do not reset the clock if off by more than NUMBER nanoseconds
+.TP
+.B \-t
+do not adjust the system clock
+.TP
+.B \-T
+set multicast TTL for packets. Defaults to 1.
+.TP
+.B \-a NUMBER,NUMBER
+specify clock servo P and I attenuations
+.TP
+.B \-w NUMBER
+specify one way delay filter stiffness
+.TP
+.B \-b NAME
+bind PTP to network interface NAME
+.TP
+.B \-u ADDRESS
+don't multicast, send messages unicast to ADDRESS
+.TP
+.B \-l NUMBER,NUMBER
+specify inbound, outbound latency in nsec
+.TP
+.B \-o NUMBER
+specify current UTC offset
+.TP
+.B \-e NUMBER
+specify epoch NUMBER
+.TP
+.B \-y NUMBER
+specify sync interval in 2^NUMBER sec
+.TP
+.B \-m NUMBER
+specify max number of foreign master records
+.TP
+.B \-g
+run as slave only
+.TP
+.B \-p
+make this a preferred clock
+.TP
+.B \-s NUMBER
+specify system clock stratum
+.TP
+.B \-i NAME
+specify system clock identifier
+.TP
+.B \-v NUMBER
+specify system clock allen variance
+.TP
+.B \-n NAME
+specify PTP subdomain name (not related to IP or DNS)
+.TP
+.B \-k NUMBER,NUMBER
+send a management message of key, record, then exit
+
+.SH AUTHORS
+.PP
+Originally written and maintained by Kendall Correll
+<kendall_c@users.sourceforge.net>
+.PP
+Steven Kreuzer <skreuzer@freebsd.org>
+.PP
+George Neville-Neil <gnn@freebsd.org>
+.PP
+This manual page was originally written by Andrew Straw
+<strawman@astraw.com> for the Debian Project (but may be used by
+others).
diff --git a/third_party/ptpd-1.1.0/src/ptpd.c b/third_party/ptpd-1.1.0/src/ptpd.c new file mode 100644 index 0000000..f137ae5 --- /dev/null +++ b/third_party/ptpd-1.1.0/src/ptpd.c @@ -0,0 +1,59 @@ +/**
+ * @file ptpd.c
+ * @date Wed Jun 23 10:13:38 2010
+ *
+ * @brief The main() function for the PTP daemon
+ *
+ * This file contains very little code, as should be obvious,
+ * and only serves to tie together the rest of the daemon.
+ * All of the default options are set here, but command line
+ * arguments are processed in the ptpdStartup() routine called
+ * below.
+ */
+
+#include "ptpd.h"
+
+RunTimeOpts rtOpts; /* statically allocated run-time
+ * configuration data */
+
+int
+main(int argc, char **argv)
+{
+ PtpClock *ptpClock;
+ Integer16 ret;
+
+ /* initialize run-time options to reasonable values */
+ rtOpts.syncInterval = DEFAULT_SYNC_INTERVAL;
+ memcpy(rtOpts.subdomainName, DEFAULT_PTP_DOMAIN_NAME, PTP_SUBDOMAIN_NAME_LENGTH);
+ memcpy(rtOpts.clockIdentifier, IDENTIFIER_DFLT, PTP_CODE_STRING_LENGTH);
+ rtOpts.clockVariance = DEFAULT_CLOCK_VARIANCE;
+ rtOpts.clockStratum = DEFAULT_CLOCK_STRATUM;
+ rtOpts.unicastAddress[0] = 0;
+ rtOpts.inboundLatency.nanoseconds = DEFAULT_INBOUND_LATENCY;
+ rtOpts.outboundLatency.nanoseconds = DEFAULT_OUTBOUND_LATENCY;
+ rtOpts.noResetClock = DEFAULT_NO_RESET_CLOCK;
+ rtOpts.s = DEFAULT_DELAY_S;
+ rtOpts.ap = DEFAULT_AP;
+ rtOpts.ai = DEFAULT_AI;
+ rtOpts.max_foreign_records = DEFAULT_MAX_FOREIGN_RECORDS;
+ rtOpts.currentUtcOffset = DEFAULT_UTC_OFFSET;
+ rtOpts.logFd = -1;
+ rtOpts.recordFP = NULL;
+ rtOpts.useSysLog = FALSE;
+ rtOpts.ttl = 1;
+
+ if (!(ptpClock = ptpdStartup(argc, argv, &ret, &rtOpts)))
+ return ret;
+
+ if (rtOpts.probe) {
+ probe(&rtOpts, ptpClock);
+ } else {
+ /* do the protocol engine */
+ protocol(&rtOpts, ptpClock);
+ }
+
+ ptpdShutdown();
+
+ NOTIFY("self shutdown, probably due to an error\n");
+ return 1;
+}
diff --git a/third_party/ptpd-1.1.0/src/ptpd.h b/third_party/ptpd-1.1.0/src/ptpd.h new file mode 100644 index 0000000..b0abe7e --- /dev/null +++ b/third_party/ptpd-1.1.0/src/ptpd.h @@ -0,0 +1,49 @@ +/**
+ * @file ptpd.h
+ * @mainpage Ptpd Documentation
+ * @authors Martin Burnicki, Alexandre van Kempen, Steven Kreuzer,
+ * George Neville-Neil
+ * @version 1.0
+ * @date Tue Jul 20 16:15:04 2010
+ *
+ *
+ */
+
+#ifndef PTPD_H
+#define PTPD_H
+
+/* Definitions and dependencies for the TI TivaWare port */
+#include "constants.h"
+#include "dep-tiva/constants_dep.h"
+#include "dep-tiva/datatypes_dep.h"
+#include "datatypes.h"
+#include "dep-tiva/ptpd_dep.h"
+
+/* arith.c */
+UInteger32 crc_algorithm(Octet *, Integer16);
+UInteger32 sum(Octet *, Integer16);
+void fromInternalTime(TimeInternal *, TimeRepresentation *, Boolean);
+void toInternalTime(TimeInternal *, TimeRepresentation *, Boolean *);
+void normalizeTime(TimeInternal *);
+void addTime(TimeInternal *, TimeInternal *, TimeInternal *);
+void subTime(TimeInternal *, TimeInternal *, TimeInternal *);
+
+/* bmc.c */
+UInteger8 bmc(ForeignMasterRecord *, RunTimeOpts *, PtpClock *);
+void m1 (PtpClock *);
+void s1 (MsgHeader *, MsgSync *, PtpClock *);
+void initData(RunTimeOpts *, PtpClock *);
+
+/* probe.c */
+void probe(RunTimeOpts *, PtpClock *);
+
+/* protocol.c */
+void protocol(RunTimeOpts *, PtpClock *);
+
+/* Split versions of protocol() that may be used when no RTOS is present.
+ * These functions were added by TI.
+ */
+void protocol_first(RunTimeOpts*,PtpClock*);
+void protocol_loop(RunTimeOpts*,PtpClock*);
+
+#endif
|
