From a6163888f3c56123b1db313743c6147ba498732c Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Fri, 8 Aug 2014 14:42:07 +0300 Subject: Add third_party libs --- third_party/ptpd-1.1.0/src/Doxyfile | 1511 ++++++++++++++++++++ third_party/ptpd-1.1.0/src/Makefile | 35 + third_party/ptpd-1.1.0/src/arith.c | 113 ++ third_party/ptpd-1.1.0/src/bmc.c | 330 +++++ third_party/ptpd-1.1.0/src/constants.h | 120 ++ third_party/ptpd-1.1.0/src/datatypes.h | 362 +++++ .../ptpd-1.1.0/src/dep-tiva/constants_dep.h | 87 ++ .../ptpd-1.1.0/src/dep-tiva/datatypes_dep.h | 42 + third_party/ptpd-1.1.0/src/dep-tiva/ptpd_dep.h | 138 ++ third_party/ptpd-1.1.0/src/dep-tiva/ptpd_msg.c | 655 +++++++++ third_party/ptpd-1.1.0/src/dep-tiva/ptpd_net.c | 401 ++++++ third_party/ptpd-1.1.0/src/dep-tiva/ptpd_servo.c | 179 +++ third_party/ptpd-1.1.0/src/dep-tiva/ptpd_timer.c | 75 + third_party/ptpd-1.1.0/src/dep/constants_dep.h | 99 ++ third_party/ptpd-1.1.0/src/dep/datatypes_dep.h | 37 + third_party/ptpd-1.1.0/src/dep/msg.c | 671 +++++++++ third_party/ptpd-1.1.0/src/dep/net.c | 570 ++++++++ third_party/ptpd-1.1.0/src/dep/ptpd_dep.h | 154 ++ third_party/ptpd-1.1.0/src/dep/servo.c | 215 +++ third_party/ptpd-1.1.0/src/dep/startup.c | 364 +++++ third_party/ptpd-1.1.0/src/dep/sys.c | 282 ++++ third_party/ptpd-1.1.0/src/dep/timer.c | 96 ++ third_party/ptpd-1.1.0/src/probe.c | 284 ++++ third_party/ptpd-1.1.0/src/protocol.c | 865 +++++++++++ third_party/ptpd-1.1.0/src/ptpd.8 | 143 ++ third_party/ptpd-1.1.0/src/ptpd.c | 59 + third_party/ptpd-1.1.0/src/ptpd.h | 49 + 27 files changed, 7936 insertions(+) create mode 100644 third_party/ptpd-1.1.0/src/Doxyfile create mode 100644 third_party/ptpd-1.1.0/src/Makefile create mode 100644 third_party/ptpd-1.1.0/src/arith.c create mode 100644 third_party/ptpd-1.1.0/src/bmc.c create mode 100644 third_party/ptpd-1.1.0/src/constants.h create mode 100644 third_party/ptpd-1.1.0/src/datatypes.h create mode 100644 third_party/ptpd-1.1.0/src/dep-tiva/constants_dep.h create mode 100644 third_party/ptpd-1.1.0/src/dep-tiva/datatypes_dep.h create mode 100644 third_party/ptpd-1.1.0/src/dep-tiva/ptpd_dep.h create mode 100644 third_party/ptpd-1.1.0/src/dep-tiva/ptpd_msg.c create mode 100644 third_party/ptpd-1.1.0/src/dep-tiva/ptpd_net.c create mode 100644 third_party/ptpd-1.1.0/src/dep-tiva/ptpd_servo.c create mode 100644 third_party/ptpd-1.1.0/src/dep-tiva/ptpd_timer.c create mode 100644 third_party/ptpd-1.1.0/src/dep/constants_dep.h create mode 100644 third_party/ptpd-1.1.0/src/dep/datatypes_dep.h create mode 100644 third_party/ptpd-1.1.0/src/dep/msg.c create mode 100644 third_party/ptpd-1.1.0/src/dep/net.c create mode 100644 third_party/ptpd-1.1.0/src/dep/ptpd_dep.h create mode 100644 third_party/ptpd-1.1.0/src/dep/servo.c create mode 100644 third_party/ptpd-1.1.0/src/dep/startup.c create mode 100644 third_party/ptpd-1.1.0/src/dep/sys.c create mode 100644 third_party/ptpd-1.1.0/src/dep/timer.c create mode 100644 third_party/ptpd-1.1.0/src/probe.c create mode 100644 third_party/ptpd-1.1.0/src/protocol.c create mode 100644 third_party/ptpd-1.1.0/src/ptpd.8 create mode 100644 third_party/ptpd-1.1.0/src/ptpd.c create mode 100644 third_party/ptpd-1.1.0/src/ptpd.h (limited to 'third_party/ptpd-1.1.0/src') 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 , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = YES + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +GENERATE_HTMLHELP = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's +# filter section matches. +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to FRAME, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. Other possible values +# for this tag are: HIERARCHIES, which will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list; +# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which +# disables this behavior completely. For backwards compatibility with previous +# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE +# respectively. + +GENERATE_TREEVIEW = NONE + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = YES + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Options related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = YES + 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 + +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 + +/* + * 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_exp; + else if(owd_filt->s_exp > 1<s_exp = 1<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 + +#if !defined(linux) && !defined(__NetBSD__) && !defined(__FreeBSD__) +#error Not ported to this architecture, please update. +#endif + +#ifdef linux +#include +#include +#include +#define IFACE_NAME_LENGTH IF_NAMESIZE +#define NET_ADDRESS_LENGTH INET_ADDRSTRLEN + +#define IFCONF_LENGTH 10 + +#include +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define PTPD_LSBF +#elif __BYTE_ORDER == __BIG_ENDIAN +#define PTPD_MSBF +#endif +#endif /* linux */ + + +#if defined(__NetBSD__) || defined(__FreeBSD__) +#include +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) +#include +#include +#else +#include +#endif +#include +#define IFACE_NAME_LENGTH IF_NAMESIZE +#define NET_ADDRESS_LENGTH INET_ADDRSTRLEN + +#define IFCONF_LENGTH 10 + +#define adjtimex ntp_adjtime + +#include +#if BYTE_ORDER == LITTLE_ENDIAN +#define PTPD_LSBF +#elif BYTE_ORDER == BIG_ENDIAN +#define PTPD_MSBF +#endif +#endif + + +#define 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 + * @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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 + +.PP +Steven Kreuzer +.PP +George Neville-Neil +.PP +This manual page was originally written by Andrew Straw + 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 -- cgit v1.3.1