From 669d1b7ba366874e280734f4bb9e81b57f0acb3c Mon Sep 17 00:00:00 2001 From: intel-ethernet Date: Thu, 1 Nov 2012 16:54:25 -0700 Subject: [PATCH] Update directory structure, readmes, add gptp --- README | 151 +- daemons/gptp/common/CVS/Entries | 16 + daemons/gptp/common/CVS/Repository | 1 + daemons/gptp/common/CVS/Root | 1 + daemons/gptp/common/avbts_clock.hpp | 208 +++ daemons/gptp/common/avbts_message.hpp | 462 ++++++ daemons/gptp/common/avbts_oscondition.hpp | 63 + daemons/gptp/common/avbts_osipc.hpp | 44 + daemons/gptp/common/avbts_oslock.hpp | 54 + daemons/gptp/common/avbts_osnet.cpp | 37 + daemons/gptp/common/avbts_osnet.hpp | 176 ++ daemons/gptp/common/avbts_osthread.hpp | 48 + daemons/gptp/common/avbts_ostimer.hpp | 46 + daemons/gptp/common/avbts_ostimerq.hpp | 52 + daemons/gptp/common/avbts_port.hpp | 450 +++++ daemons/gptp/common/ieee1588.hpp | 231 +++ daemons/gptp/common/ieee1588clock.cpp | 197 +++ daemons/gptp/common/ieee1588port.cpp | 1031 ++++++++++++ daemons/gptp/common/ptp_message.cpp | 1718 ++++++++++++++++++++ daemons/gptp/linux/CVS/Entries | 2 + daemons/gptp/linux/CVS/Repository | 1 + daemons/gptp/linux/CVS/Root | 1 + daemons/gptp/linux/build/CVS/Entries | 2 + daemons/gptp/linux/build/CVS/Repository | 1 + daemons/gptp/linux/build/CVS/Root | 1 + daemons/gptp/linux/build/Makefile | 84 + daemons/gptp/linux/build/obj/.dir | 0 daemons/gptp/linux/build/obj/CVS/Entries | 2 + daemons/gptp/linux/build/obj/CVS/Repository | 1 + daemons/gptp/linux/build/obj/CVS/Root | 1 + daemons/gptp/linux/build/obj/avbts_osnet.o | Bin 0 -> 63064 bytes daemons/gptp/linux/build/obj/daemon_cl | Bin 0 -> 558311 bytes daemons/gptp/linux/build/obj/ieee1588clock.o | Bin 0 -> 81172 bytes daemons/gptp/linux/build/obj/ieee1588port.o | Bin 0 -> 305900 bytes daemons/gptp/linux/build/obj/ptp_message.o | Bin 0 -> 192004 bytes daemons/gptp/linux/src/CVS/Entries | 4 + daemons/gptp/linux/src/CVS/Repository | 1 + daemons/gptp/linux/src/CVS/Root | 1 + daemons/gptp/linux/src/daemon_cl.cpp | 89 + daemons/gptp/linux/src/linux_hal.hpp | 875 ++++++++++ daemons/gptp/linux/src/platform.hpp | 46 + daemons/gptp/windows/CVS/Entries | 4 + daemons/gptp/windows/CVS/Repository | 1 + daemons/gptp/windows/CVS/Root | 1 + daemons/gptp/windows/daemon_cl/CVS/Entries | 15 + daemons/gptp/windows/daemon_cl/CVS/Repository | 1 + daemons/gptp/windows/daemon_cl/CVS/Root | 1 + daemons/gptp/windows/daemon_cl/ReadMe.txt | 56 + daemons/gptp/windows/daemon_cl/daemon_cl.cpp | 124 ++ daemons/gptp/windows/daemon_cl/daemon_cl.vcxproj | 209 +++ .../windows/daemon_cl/daemon_cl.vcxproj.filters | 99 ++ .../gptp/windows/daemon_cl/daemon_cl.vcxproj.user | 11 + daemons/gptp/windows/daemon_cl/ipcdef.hpp | 88 + daemons/gptp/windows/daemon_cl/packet.cpp | 207 +++ daemons/gptp/windows/daemon_cl/packet.hpp | 62 + daemons/gptp/windows/daemon_cl/platform.hpp | 46 + daemons/gptp/windows/daemon_cl/stdafx.cpp | 8 + daemons/gptp/windows/daemon_cl/stdafx.h | 15 + daemons/gptp/windows/daemon_cl/targetver.h | 8 + daemons/gptp/windows/daemon_cl/tsc.hpp | 65 + daemons/gptp/windows/daemon_cl/windows_hal.hpp | 576 +++++++ daemons/gptp/windows/daemon_cl/x64/CVS/Entries | 1 + daemons/gptp/windows/daemon_cl/x64/CVS/Repository | 1 + daemons/gptp/windows/daemon_cl/x64/CVS/Root | 1 + daemons/gptp/windows/gptp.sln | 21 + daemons/gptp/windows/named_pipe_test/CVS/Entries | 9 + .../gptp/windows/named_pipe_test/CVS/Repository | 1 + daemons/gptp/windows/named_pipe_test/CVS/Root | 1 + daemons/gptp/windows/named_pipe_test/ReadMe.txt | 10 + .../windows/named_pipe_test/named_pipe_test.cpp | 114 ++ .../named_pipe_test/named_pipe_test.vcxproj | 162 ++ .../named_pipe_test.vcxproj.filters | 36 + .../named_pipe_test/named_pipe_test.vcxproj.user | 3 + daemons/gptp/windows/named_pipe_test/stdafx.cpp | 8 + daemons/gptp/windows/named_pipe_test/stdafx.h | 16 + daemons/gptp/windows/named_pipe_test/targetver.h | 8 + .../x64/Release/named_pipe_test.Build.CppClean.log | 16 + .../x64/Release/named_pipe_test.log | 9 + daemons/gptp/windows/x64/CVS/Entries | 1 + daemons/gptp/windows/x64/CVS/Repository | 1 + daemons/gptp/windows/x64/CVS/Root | 1 + daemons/gptp/windows/x64/Release/daemon_cl.map | 732 +++++++++ daemons/mrpd/README | 11 + examples/Makefile | 42 - examples/latency_test.c | 305 ---- examples/mrp_client/Makefile | 26 + examples/{ => mrp_client}/mrpl.c | 0 examples/{ => mrp_client}/mrpq.c | 0 examples/simple_talker.c | 970 ----------- examples/simple_talker/Makefile | 21 + examples/simple_talker/README | 22 + examples/simple_talker/simple_talker.c | 1155 +++++++++++++ examples/test.c | 219 --- kmod/{ => igb}/LICENSE | 0 kmod/{ => igb}/Makefile | 0 kmod/igb/README | 31 + kmod/{ => igb}/e1000_82575.c | 0 kmod/{ => igb}/e1000_82575.h | 0 kmod/{ => igb}/e1000_api.c | 0 kmod/{ => igb}/e1000_api.h | 0 kmod/{ => igb}/e1000_defines.h | 0 kmod/{ => igb}/e1000_hw.h | 0 kmod/{ => igb}/e1000_i210.c | 0 kmod/{ => igb}/e1000_i210.h | 0 kmod/{ => igb}/e1000_mac.c | 0 kmod/{ => igb}/e1000_mac.h | 0 kmod/{ => igb}/e1000_manage.c | 0 kmod/{ => igb}/e1000_manage.h | 0 kmod/{ => igb}/e1000_mbx.c | 0 kmod/{ => igb}/e1000_mbx.h | 0 kmod/{ => igb}/e1000_nvm.c | 0 kmod/{ => igb}/e1000_nvm.h | 0 kmod/{ => igb}/e1000_osdep.h | 0 kmod/{ => igb}/e1000_phy.c | 0 kmod/{ => igb}/e1000_phy.h | 0 kmod/{ => igb}/e1000_regs.h | 0 kmod/{ => igb}/igb.h | 0 kmod/{ => igb}/igb_ethtool.c | 0 kmod/{ => igb}/igb_main.c | 0 kmod/{ => igb}/igb_param.c | 0 kmod/{ => igb}/igb_procfs.c | 0 kmod/{ => igb}/igb_ptp.c | 0 kmod/{ => igb}/igb_regtest.h | 0 kmod/{ => igb}/igb_sysfs.c | 0 kmod/{ => igb}/igb_vmdq.c | 0 kmod/{ => igb}/igb_vmdq.h | 0 kmod/{ => igb}/kcompat.c | 0 kmod/{ => igb}/kcompat.h | 0 kmod/{ => igb}/kcompat_ethtool.c | 0 lib/{ => igb}/LICENSE | 0 lib/{ => igb}/Makefile | 0 lib/{ => igb}/e1000_82575.h | 0 lib/{ => igb}/e1000_defines.h | 0 lib/{ => igb}/e1000_hw.h | 0 lib/{ => igb}/e1000_osdep.h | 0 lib/{ => igb}/e1000_regs.h | 0 lib/{ => igb}/igb.c | 80 +- lib/{ => igb}/igb.h | 2 +- lib/{ => igb}/igb_internal.h | 0 139 files changed, 10092 insertions(+), 1639 deletions(-) create mode 100644 daemons/gptp/common/CVS/Entries create mode 100644 daemons/gptp/common/CVS/Repository create mode 100644 daemons/gptp/common/CVS/Root create mode 100644 daemons/gptp/common/avbts_clock.hpp create mode 100644 daemons/gptp/common/avbts_message.hpp create mode 100644 daemons/gptp/common/avbts_oscondition.hpp create mode 100644 daemons/gptp/common/avbts_osipc.hpp create mode 100644 daemons/gptp/common/avbts_oslock.hpp create mode 100644 daemons/gptp/common/avbts_osnet.cpp create mode 100644 daemons/gptp/common/avbts_osnet.hpp create mode 100644 daemons/gptp/common/avbts_osthread.hpp create mode 100644 daemons/gptp/common/avbts_ostimer.hpp create mode 100644 daemons/gptp/common/avbts_ostimerq.hpp create mode 100644 daemons/gptp/common/avbts_port.hpp create mode 100644 daemons/gptp/common/ieee1588.hpp create mode 100644 daemons/gptp/common/ieee1588clock.cpp create mode 100644 daemons/gptp/common/ieee1588port.cpp create mode 100644 daemons/gptp/common/ptp_message.cpp create mode 100644 daemons/gptp/linux/CVS/Entries create mode 100644 daemons/gptp/linux/CVS/Repository create mode 100644 daemons/gptp/linux/CVS/Root create mode 100644 daemons/gptp/linux/build/CVS/Entries create mode 100644 daemons/gptp/linux/build/CVS/Repository create mode 100644 daemons/gptp/linux/build/CVS/Root create mode 100644 daemons/gptp/linux/build/Makefile create mode 100644 daemons/gptp/linux/build/obj/.dir create mode 100644 daemons/gptp/linux/build/obj/CVS/Entries create mode 100644 daemons/gptp/linux/build/obj/CVS/Repository create mode 100644 daemons/gptp/linux/build/obj/CVS/Root create mode 100644 daemons/gptp/linux/build/obj/avbts_osnet.o create mode 100755 daemons/gptp/linux/build/obj/daemon_cl create mode 100644 daemons/gptp/linux/build/obj/ieee1588clock.o create mode 100644 daemons/gptp/linux/build/obj/ieee1588port.o create mode 100644 daemons/gptp/linux/build/obj/ptp_message.o create mode 100644 daemons/gptp/linux/src/CVS/Entries create mode 100644 daemons/gptp/linux/src/CVS/Repository create mode 100644 daemons/gptp/linux/src/CVS/Root create mode 100644 daemons/gptp/linux/src/daemon_cl.cpp create mode 100644 daemons/gptp/linux/src/linux_hal.hpp create mode 100644 daemons/gptp/linux/src/platform.hpp create mode 100644 daemons/gptp/windows/CVS/Entries create mode 100644 daemons/gptp/windows/CVS/Repository create mode 100644 daemons/gptp/windows/CVS/Root create mode 100644 daemons/gptp/windows/daemon_cl/CVS/Entries create mode 100644 daemons/gptp/windows/daemon_cl/CVS/Repository create mode 100644 daemons/gptp/windows/daemon_cl/CVS/Root create mode 100644 daemons/gptp/windows/daemon_cl/ReadMe.txt create mode 100644 daemons/gptp/windows/daemon_cl/daemon_cl.cpp create mode 100644 daemons/gptp/windows/daemon_cl/daemon_cl.vcxproj create mode 100644 daemons/gptp/windows/daemon_cl/daemon_cl.vcxproj.filters create mode 100644 daemons/gptp/windows/daemon_cl/daemon_cl.vcxproj.user create mode 100644 daemons/gptp/windows/daemon_cl/ipcdef.hpp create mode 100644 daemons/gptp/windows/daemon_cl/packet.cpp create mode 100644 daemons/gptp/windows/daemon_cl/packet.hpp create mode 100644 daemons/gptp/windows/daemon_cl/platform.hpp create mode 100644 daemons/gptp/windows/daemon_cl/stdafx.cpp create mode 100644 daemons/gptp/windows/daemon_cl/stdafx.h create mode 100644 daemons/gptp/windows/daemon_cl/targetver.h create mode 100644 daemons/gptp/windows/daemon_cl/tsc.hpp create mode 100644 daemons/gptp/windows/daemon_cl/windows_hal.hpp create mode 100644 daemons/gptp/windows/daemon_cl/x64/CVS/Entries create mode 100644 daemons/gptp/windows/daemon_cl/x64/CVS/Repository create mode 100644 daemons/gptp/windows/daemon_cl/x64/CVS/Root create mode 100644 daemons/gptp/windows/gptp.sln create mode 100644 daemons/gptp/windows/named_pipe_test/CVS/Entries create mode 100644 daemons/gptp/windows/named_pipe_test/CVS/Repository create mode 100644 daemons/gptp/windows/named_pipe_test/CVS/Root create mode 100644 daemons/gptp/windows/named_pipe_test/ReadMe.txt create mode 100644 daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp create mode 100644 daemons/gptp/windows/named_pipe_test/named_pipe_test.vcxproj create mode 100644 daemons/gptp/windows/named_pipe_test/named_pipe_test.vcxproj.filters create mode 100644 daemons/gptp/windows/named_pipe_test/named_pipe_test.vcxproj.user create mode 100644 daemons/gptp/windows/named_pipe_test/stdafx.cpp create mode 100644 daemons/gptp/windows/named_pipe_test/stdafx.h create mode 100644 daemons/gptp/windows/named_pipe_test/targetver.h create mode 100644 daemons/gptp/windows/named_pipe_test/x64/Release/named_pipe_test.Build.CppClean.log create mode 100644 daemons/gptp/windows/named_pipe_test/x64/Release/named_pipe_test.log create mode 100644 daemons/gptp/windows/x64/CVS/Entries create mode 100644 daemons/gptp/windows/x64/CVS/Repository create mode 100644 daemons/gptp/windows/x64/CVS/Root create mode 100644 daemons/gptp/windows/x64/Release/daemon_cl.map create mode 100644 daemons/mrpd/README delete mode 100644 examples/Makefile delete mode 100644 examples/latency_test.c create mode 100644 examples/mrp_client/Makefile rename examples/{ => mrp_client}/mrpl.c (100%) rename examples/{ => mrp_client}/mrpq.c (100%) delete mode 100755 examples/simple_talker.c create mode 100644 examples/simple_talker/Makefile create mode 100644 examples/simple_talker/README create mode 100755 examples/simple_talker/simple_talker.c delete mode 100644 examples/test.c rename kmod/{ => igb}/LICENSE (100%) rename kmod/{ => igb}/Makefile (100%) create mode 100644 kmod/igb/README rename kmod/{ => igb}/e1000_82575.c (100%) rename kmod/{ => igb}/e1000_82575.h (100%) rename kmod/{ => igb}/e1000_api.c (100%) rename kmod/{ => igb}/e1000_api.h (100%) rename kmod/{ => igb}/e1000_defines.h (100%) rename kmod/{ => igb}/e1000_hw.h (100%) rename kmod/{ => igb}/e1000_i210.c (100%) rename kmod/{ => igb}/e1000_i210.h (100%) rename kmod/{ => igb}/e1000_mac.c (100%) rename kmod/{ => igb}/e1000_mac.h (100%) rename kmod/{ => igb}/e1000_manage.c (100%) rename kmod/{ => igb}/e1000_manage.h (100%) rename kmod/{ => igb}/e1000_mbx.c (100%) rename kmod/{ => igb}/e1000_mbx.h (100%) rename kmod/{ => igb}/e1000_nvm.c (100%) rename kmod/{ => igb}/e1000_nvm.h (100%) rename kmod/{ => igb}/e1000_osdep.h (100%) rename kmod/{ => igb}/e1000_phy.c (100%) rename kmod/{ => igb}/e1000_phy.h (100%) rename kmod/{ => igb}/e1000_regs.h (100%) rename kmod/{ => igb}/igb.h (100%) rename kmod/{ => igb}/igb_ethtool.c (100%) rename kmod/{ => igb}/igb_main.c (100%) rename kmod/{ => igb}/igb_param.c (100%) rename kmod/{ => igb}/igb_procfs.c (100%) rename kmod/{ => igb}/igb_ptp.c (100%) rename kmod/{ => igb}/igb_regtest.h (100%) rename kmod/{ => igb}/igb_sysfs.c (100%) rename kmod/{ => igb}/igb_vmdq.c (100%) rename kmod/{ => igb}/igb_vmdq.h (100%) rename kmod/{ => igb}/kcompat.c (100%) rename kmod/{ => igb}/kcompat.h (100%) rename kmod/{ => igb}/kcompat_ethtool.c (100%) rename lib/{ => igb}/LICENSE (100%) rename lib/{ => igb}/Makefile (100%) rename lib/{ => igb}/e1000_82575.h (100%) rename lib/{ => igb}/e1000_defines.h (100%) rename lib/{ => igb}/e1000_hw.h (100%) rename lib/{ => igb}/e1000_osdep.h (100%) rename lib/{ => igb}/e1000_regs.h (100%) rename lib/{ => igb}/igb.c (91%) rename lib/{ => igb}/igb.h (98%) rename lib/{ => igb}/igb_internal.h (100%) diff --git a/README b/README index c4f87bc..b1e720b 100644 --- a/README +++ b/README @@ -1,87 +1,66 @@ -INTRODUCTION +Open AVB +======== +maintainer: eric.mann AT intel.com + +Intel created the Open AVB repository to encourage collaborative source code +development for AVB technology enabling. By publishing the source code, our +intent is to encourage standardization, stability and inter-operability between +multiple vendors. This repository - created by the Intel LAN Access Division - +is open for contributions from other vendors. The repository contains primarily +network building block components - drivers, libraries, example applications +and daemon source code - required to build an AVB system. It is planned to +eventually include the various packet encapsulation types, protocol discovery +daemons, libraries to convert media clocks to AVB clocks (and vice +versa), and drivers. + +This repository does not include all components required to build a full +production AVB system (e.g. a turnkey solution to stream stored or live audio +or video content). Some simple example applications are provided which +illustrate the flow - but a professional AVB system requires a full media stack +- including audio and video inputs and outputs, media processing elements, and +various graphical user interfaces. Various companies provide such integrated +solutions. + +LICENSING AND CONTRIBUTION GUIDELINES +====================================== +To the extent possible, content is licensed under BSD licensing terms. Linux +kernel mode components are provided under a GPLv2 license. The specific license +information is included in the various directories to eliminate confusion. We +encourage you to review the ‘LICENSE’ file included in the head of the +various subdirectories for details. + +Third party submissions are welcomed. Our intent for third party content +contributions is to enable derivative products with minimal licensing +entanglements. Practically speaking, this means we would enforce (a) an +original-source attestation for any contributed content, and (b) rejecting +patches with GPL content into existing “BSD” licensed components. Third +party copyrights can be included provided they do not narrow the licensing +terms of an existing component. + +Prior to accepting a commit, Intel may perform scans using third-party tools +to identify suspected hits of GPL code. Intel may also perform vulnerability +scans of patches in an attempt to find various coding errors such as memory +leaks, buffer overflows and usage of uninitialized variables. The submitter +will be asked to correct any detected issues prior to a commit. Owners +of submitted third-party content are free to apply changes without supervision +by Intel. + +RELATED OPEN SOURCE PROJECTS +============================ + +AVDECC +------ +Jeff Koftinoff maintains a repository of AVDECC example open +source code. AVDECC is a management layer, similar to SNMP MIB formats, +which enables remote devices to detect, enumerate and configure AVB-related +devices based on their standardized management properties. + ++ https://github.com/jdkoftinoff/avdecc ++ https://github.com/jdkoftinoff/avdecc-examples + +XMOS +---- +XMOS is a semiconductor company providing a reference design for AVB +endpoints in pro audio and automotive. Our source code is open source +and available on Github - https://github.com/xcore/sw_avb -This component demonstrates various features of the Intel I210 Ethernet controller. -These features can be used for developing Audio/Video Bridging applications, -Industrial Ethernet applications which require precise timing control over frame -transmission, or test harnesses for measuring system latencies and sampling events. - -This component - igb_avb - is limited to the Intel I210 Ethernet controller. The kernel -module can be loaded in parallel to existing in-kernel igb modules which may be -used on other supported Intel LAN controllers. Modifications are required to -the in-kernel drivers if the existing in-kernel igb driver has support for the Intel I210. - -BUILDING - -There are four primary components - kmod (for the kernel-mode igb_avb driver), -lib (for the user-mode driver library), examples (for test applications), and -daemons (at present an MRP daemon required for establishing AVB streams). - -To build, 'cd' into each of the respective directories, and execute 'make'. - -The kernel igb module can be built which supports the latest Linux kernel -3.x PTP clock support - to enable, modify kmod/Makefile and enable -DCONFIG_PTP -as a build option (e.g. EXTRA_CFLAGS += -DCONFIG_PTP). - -The example application uses the pciutils library - the latest version -can be downloaded from < ftp://ftp.kernel.org/pub/software/utils/pciutils/ >. -Download and extract the library, and run 'make;make install;make install-lib'. - -RUNNING - -To install the kernel mode driver, you must have root permissions. Typically, -the driver is loaded by: - sudo modprobe dca - sudo modprobe ptp - sudo insmod ./igb_avb.ko - -As 3.4 and later kernels include support for the I210, you may need to 'rmmod igb' -before loading the igb_avb module. - -Note that packets generated from the 'user-mode' libraries are not counted in -ifconfig output, and likewise, if generating multicast traffic, the receiving -node must be registered to receive the traffic (or run the interface in promiscious -mode). - -TIME SYNC - -This package relies on using the linuxptp daemon to establish time -synchronization. The latest source for this is available here: - - git clone git://git.code.sf.net/p/linuxptp/code linuxptp-code - -Type 'make' to build. This version has been tested with kernel 3.5.1. -Assuming kernel support is enabled, simply 'modprobe ptp' to load the -required kernel mode components. Example command line to the daemon is: - - ptp4l -f gPTP.cfg -2 -P -i p11p1 - -Note the default gPTP.cfg file queries and times out relatively fast while -awaiting the transmit timestamps to complete. To adjust this, modify the -'tx_timestamp_retries' parameter to 400 (which roughly corresponds to 400 usec). - -EXAMPLE APPLICATIONS - -The test application - which simply streams timed packets - requires root permissions -as well to execute and attach to the driver. - sudo ./test - -To exit the test app, hit Ctrl-C. The application gracefully tears down -the connection to the driver. If the application unexpectedly aborts the -kernel-mode driver also reclaims the various buffers and attempts to clean up. -The application should be able to re-initialize and use the transmit queues -without restarting the driver. - -The 'simple_talker' application illustrates the various steps to publish a stream -and being streaming after a listener connects. - -MRPD DAEMON - -The MRP daemon is required to establish stream reservations with compatible AVB -infrastructure devices. The command line selectively enables MMRP (via the -m option), -MVRP (via the -v option), and MSRP (via the -s option). You must also specify the -interface on which you want to bind the daemon to (e.g. -i eth2). The full command line -typically appears as follows: - sudo ./mrpd -mvs -i eth2 - -Sample client applications - mrpctl, mrpq, mrpl - illustrates how to connect, query -and add attributes to the MRP daemon. diff --git a/daemons/gptp/common/CVS/Entries b/daemons/gptp/common/CVS/Entries new file mode 100644 index 0000000..fb8a7b1 --- /dev/null +++ b/daemons/gptp/common/CVS/Entries @@ -0,0 +1,16 @@ +/avbts_clock.hpp/1.1/Fri Sep 21 15:48:06 2012// +/avbts_message.hpp/1.1/Fri Sep 7 19:02:51 2012// +/avbts_oscondition.hpp/1.1/Fri Sep 21 20:14:21 2012// +/avbts_osipc.hpp/1.1/Fri Sep 7 19:03:47 2012// +/avbts_oslock.hpp/1.1/Fri Sep 7 19:03:59 2012// +/avbts_osnet.cpp/1.1/Fri Sep 7 19:02:51 2012// +/avbts_osnet.hpp/1.1/Fri Sep 21 20:12:53 2012// +/avbts_osthread.hpp/1.1/Fri Sep 7 19:04:20 2012// +/avbts_ostimer.hpp/1.1/Fri Sep 7 19:04:50 2012// +/avbts_ostimerq.hpp/1.1/Fri Sep 7 19:02:51 2012// +/avbts_port.hpp/1.1/Fri Sep 21 20:11:27 2012// +/ieee1588.hpp/1.1/Fri Sep 21 15:56:15 2012// +/ieee1588clock.cpp/1.1/Fri Sep 21 15:57:08 2012// +/ieee1588port.cpp/1.1/Fri Sep 21 20:05:48 2012// +/ptp_message.cpp/1.1/Fri Sep 21 18:27:54 2012// +D diff --git a/daemons/gptp/common/CVS/Repository b/daemons/gptp/common/CVS/Repository new file mode 100644 index 0000000..95ecafe --- /dev/null +++ b/daemons/gptp/common/CVS/Repository @@ -0,0 +1 @@ +linux_igb_avb/daemons/gptp/common diff --git a/daemons/gptp/common/CVS/Root b/daemons/gptp/common/CVS/Root new file mode 100644 index 0000000..beb56f8 --- /dev/null +++ b/daemons/gptp/common/CVS/Root @@ -0,0 +1 @@ +:pserver:ekmann@azusa.jf.intel.com:/home/cvsroot/ladsw diff --git a/daemons/gptp/common/avbts_clock.hpp b/daemons/gptp/common/avbts_clock.hpp new file mode 100644 index 0000000..b02fd55 --- /dev/null +++ b/daemons/gptp/common/avbts_clock.hpp @@ -0,0 +1,208 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef AVBTS_CLOCK_HPP +#define AVBTS_CLOCK_HPP + +#include +#include +#include +#include +#include + +#define EVENT_TIMER_GRANULARITY 5000000 + +struct ClockQuality { + unsigned char cq_class; + unsigned char clockAccuracy; + int16_t offsetScaledLogVariance; +}; + +class IEEE1588Clock { + private: + ClockIdentity clock_identity; + ClockQuality clock_quality; + unsigned char priority1; + unsigned char priority2; + bool initializable; + bool is_boundary_clock; + bool two_step_clock; + unsigned char domain_number; + uint16_t number_ports; + uint16_t number_foreign_records; + bool slave_only; + int16_t current_utc_offset; + bool leap_59; + bool leap_61; + uint16_t epoch_number; + uint16_t steps_removed; + int64_t offset_from_master; + Timestamp one_way_delay; + PortIdentity parent_identity; + PortIdentity grandmaster_port_identity; + ClockQuality grandmaster_clock_quality; + unsigned char grandmaster_priority1; + unsigned char grandmaster_priority2; + bool grandmaster_is_boundary_clock; + uint8_t time_source; + + int32_t master_local_offset_125us_offset; + uint64_t master_local_offset_nrst125us_initial; + bool master_local_offset_nrst125us_initialized; + + ClockIdentity LastEBestIdentity; + + IEEE1588Port *port_list[MAX_PORTS]; + + static Timestamp start_time; + Timestamp last_sync_time; + + OS_IPC *ipc; + + OSTimerQueue *timerq; + + bool forceOrdinarySlave; + public: + IEEE1588Clock(bool forceOrdinarySlave, + OSTimerQueueFactory * timerq_factory, OS_IPC * ipc); + ~IEEE1588Clock(void); + + Timestamp getTime(void); + Timestamp getPreciseTime(void); + + bool isBetterThan(PTPMessageAnnounce * msg); + + ClockIdentity getLastEBestIdentity( void ) { + return LastEBestIdentity; + } + void setLastEBestIdentity( ClockIdentity id ) { + LastEBestIdentity = id; + return; + } + + void setClockIdentity(char *id) { + clock_identity.set((uint8_t *) id); + } + void setClockIdentity(LinkLayerAddress * addr) { + clock_identity.set(addr); + } + + unsigned char getDomain(void) { + return domain_number; + } + + PortIdentity getGrandmasterPortIdentity(void) { + return grandmaster_port_identity; + } + void setGrandmasterPortIdentity(PortIdentity id) { + grandmaster_port_identity = id; + } + + ClockQuality getGrandmasterClockQuality(void) { + return grandmaster_clock_quality; + } + + ClockQuality getClockQuality(void) { + return clock_quality; + } + + unsigned char getGrandmasterPriority1(void) { + return grandmaster_priority1; + } + + unsigned char getGrandmasterPriority2(void) { + return grandmaster_priority2; + } + + uint16_t getMasterStepsRemoved(void) { + return steps_removed; + } + + uint16_t getCurrentUtcOffset(void) { + return current_utc_offset; + } + + uint8_t getTimeSource(void) { + return time_source; + } + + void getGrandmasterIdentity(char *id); + + unsigned char getPriority1(void) { + return priority1; + } + + unsigned char getPriority2(void) { + return priority2; + } + uint16_t getNextPortId(void) { + return (number_ports++ % (MAX_PORTS + 1)) + 1; + } + void registerPort(IEEE1588Port * port, uint16_t index) { + if (index < MAX_PORTS) { + port_list[index - 1] = port; + } + ++number_ports; + } + void getPortList(int &count, IEEE1588Port ** &ports) { + ports = this->port_list; + count = number_ports; + return; + } + + static Timestamp getSystemTime(void); + + void addEventTimer(IEEE1588Port * target, Event e, + unsigned long long time_ns); + void deleteEventTimer(IEEE1588Port * target, Event e); + +#define SHM_SIZE 2*sizeof( int64_t ) \ + + 2*(sizeof( last_sync_time.seconds_ms ) + sizeof( last_sync_time.seconds_ls ) + sizeof( last_sync_time.nanoseconds )) \ + + 4*sizeof(uint32_t) + sizeof( uint16_t ) + + void setMasterOffset(int64_t master_local_offset, Timestamp local_time, + int32_t master_local_freq_offset, + int64_t local_system_offset, Timestamp system_time, + int32_t local_system_freq_offset, + uint32_t nominal_clock_rate, uint32_t local_clock); + + ClockIdentity getClockIdentity() { + return clock_identity; + } + + friend void tick_handler(int sig); +}; + +void tick_handler(int sig); + +#endif diff --git a/daemons/gptp/common/avbts_message.hpp b/daemons/gptp/common/avbts_message.hpp new file mode 100644 index 0000000..fa8eff8 --- /dev/null +++ b/daemons/gptp/common/avbts_message.hpp @@ -0,0 +1,462 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef AVBTS_MESSAGE_HPP +#define AVBTS_MESSAGE_HPP + +#include +#include +#include + +#define PTP_CODE_STRING_LENGTH 4 +#define PTP_SUBDOMAIN_NAME_LENGTH 16 +#define PTP_FLAGS_LENGTH 2 + +#define GPTP_VERSION 2 +#define PTP_NETWORK_VERSION 1 + +#define PTP_ETHER 1 +#define PTP_DEFAULT 255 + +#define PTP_COMMON_HDR_OFFSET 0 +#define PTP_COMMON_HDR_LENGTH 34 +#define PTP_COMMON_HDR_TRANSSPEC_MSGTYPE(x) x +#define PTP_COMMON_HDR_PTP_VERSION(x) x+1 +#define PTP_COMMON_HDR_MSG_LENGTH(x) x+2 +#define PTP_COMMON_HDR_DOMAIN_NUMBER(x) x+4 +#define PTP_COMMON_HDR_FLAGS(x) x+6 +#define PTP_COMMON_HDR_CORRECTION(x) x+8 +#define PTP_COMMON_HDR_SOURCE_CLOCK_ID(x) x+20 +#define PTP_COMMON_HDR_SOURCE_PORT_ID(x) x+28 +#define PTP_COMMON_HDR_SEQUENCE_ID(x) x+30 +#define PTP_COMMON_HDR_CONTROL(x) x+32 +#define PTP_COMMON_HDR_LOG_MSG_INTRVL(x) x+33 + +#define PTP_ANNOUNCE_OFFSET 34 +#define PTP_ANNOUNCE_LENGTH 30 +#define PTP_ANNOUNCE_CURRENT_UTC_OFFSET(x) x+10 +#define PTP_ANNOUNCE_GRANDMASTER_PRIORITY1(x) x+13 +#define PTP_ANNOUNCE_GRANDMASTER_CLOCK_QUALITY(x) x+14 +#define PTP_ANNOUNCE_GRANDMASTER_PRIORITY2(x) x+18 +#define PTP_ANNOUNCE_GRANDMASTER_IDENTITY(x) x+19 +#define PTP_ANNOUNCE_STEPS_REMOVED(x) x+27 +#define PTP_ANNOUNCE_TIME_SOURCE(x) x+29 + +#define PTP_SYNC_OFFSET 34 +#define PTP_SYNC_LENGTH 10 +#define PTP_SYNC_SEC_MS(x) x +#define PTP_SYNC_SEC_LS(x) x+2 +#define PTP_SYNC_NSEC(x) x+6 + +#define PTP_FOLLOWUP_OFFSET 34 +#define PTP_FOLLOWUP_LENGTH 10 +#define PTP_FOLLOWUP_SEC_MS(x) x +#define PTP_FOLLOWUP_SEC_LS(x) x+2 +#define PTP_FOLLOWUP_NSEC(x) x+6 + +#define PTP_PDELAY_REQ_OFFSET 34 +#define PTP_PDELAY_REQ_LENGTH 20 +#define PTP_PDELAY_REQ_SEC_MS(x) x +#define PTP_PDELAY_REQ_SEC_LS(x) x+2 +#define PTP_PDELAY_REQ_NSEC(x) x+6 + +#define PTP_PDELAY_RESP_OFFSET 34 +#define PTP_PDELAY_RESP_LENGTH 20 +#define PTP_PDELAY_RESP_SEC_MS(x) x +#define PTP_PDELAY_RESP_SEC_LS(x) x+2 +#define PTP_PDELAY_RESP_NSEC(x) x+6 +#define PTP_PDELAY_RESP_REQ_CLOCK_ID(x) x+10 +#define PTP_PDELAY_RESP_REQ_PORT_ID(x) x+18 + +#define PTP_PDELAY_FOLLOWUP_OFFSET 34 +#define PTP_PDELAY_FOLLOWUP_LENGTH 20 +#define PTP_PDELAY_FOLLOWUP_SEC_MS(x) x +#define PTP_PDELAY_FOLLOWUP_SEC_LS(x) x+2 +#define PTP_PDELAY_FOLLOWUP_NSEC(x) x+6 +#define PTP_PDELAY_FOLLOWUP_REQ_CLOCK_ID(x) x+10 +#define PTP_PDELAY_FOLLOWUP_REQ_PORT_ID(x) x+18 + +#define PTP_LI_61_BYTE 0 +#define PTP_LI_61_BIT 0 +#define PTP_LI_59_BYTE 0 +#define PTP_LI_59_BIT 1 +#define PTP_ASSIST_BYTE 0 +#define PTP_ASSIST_BIT 1 + +enum MessageType { + SYNC_MESSAGE = 0, + DELAY_REQ_MESSAGE = 1, + PATH_DELAY_REQ_MESSAGE = 2, + PATH_DELAY_RESP_MESSAGE = 3, + FOLLOWUP_MESSAGE = 8, + DELAY_RESP_MESSAGE = 9, + PATH_DELAY_FOLLOWUP_MESSAGE = 0xA, + ANNOUNCE_MESSAGE = 0xB, + SIGNALLING_MESSAGE = 0xC, + MANAGEMENT_MESSAGE = 0xD, +}; + +enum LegacyMessageType { + SYNC, + DELAY_REQ, + FOLLOWUP, + DELAY_RESP, + MANAGEMENT, + MESSAGE_OTHER +}; + +enum MulticastType { + MCAST_NONE, + MCAST_PDELAY, + MCAST_OTHER +}; + +class PTPMessageCommon { + protected: + unsigned char versionPTP; + uint16_t versionNetwork; + MessageType messageType; + + PortIdentity *sourcePortIdentity; + + uint16_t sequenceId; + LegacyMessageType control; + unsigned char flags[2]; + + uint16_t messageLength; + char logMeanMessageInterval; + long long correctionField; + unsigned char domainNumber; + + Timestamp _timestamp; + unsigned _timestamp_counter_value; + bool _gc; + + PTPMessageCommon(void) { + }; + public: + PTPMessageCommon(IEEE1588Port * port); + virtual ~ PTPMessageCommon(void); + + unsigned char *getFlags(void) { + return flags; + } + + uint16_t getSequenceId(void) { + return sequenceId; + } + void setSequenceId(uint16_t seq) { + sequenceId = seq; + } + + MessageType getMessageType(void) { + return messageType; + } + + long long getCorrectionField(void) { + return correctionField; + } + void setCorrectionField(long long correctionAmount) { + correctionField = correctionAmount; + } + + void getPortIdentity(PortIdentity * identity); + void setPortIdentity(PortIdentity * identity); + + Timestamp getTimestamp(void) { + return _timestamp; + } + uint32_t getTimestampCounterValue(void) { + return _timestamp_counter_value;; + } + void setTimestamp(Timestamp & timestamp) { + _timestamp = timestamp; + } + + bool garbage() { + return _gc; + } + + bool isSenderEqual(PortIdentity portIdentity); + + virtual void processMessage(IEEE1588Port * port); + + void buildCommonHeader(uint8_t * buf); + + friend PTPMessageCommon *buildPTPMessage(char *buf, int size, + LinkLayerAddress * remote, + IEEE1588Port * port); +}; + +#pragma pack(push,1) + +class PathTraceTLV { + private: + uint16_t tlvType; + uint16_t lengthField; + ClockIdentity identity; + public: + PathTraceTLV() { + tlvType = PLAT_htons(0x8); + lengthField = PLAT_htons(sizeof(identity)); + } + void setClockIdentity(ClockIdentity * id) { + identity = *id; + } + void toByteString(uint8_t * byte_str) { + memcpy(byte_str, this, sizeof(*this)); + } +}; + +#pragma pack(pop) + +class PTPMessageAnnounce:public PTPMessageCommon { + private: + char grandmasterIdentity[PTP_CLOCK_IDENTITY_LENGTH]; + + PathTraceTLV tlv; + + uint16_t currentUtcOffset; + unsigned char grandmasterPriority1; + unsigned char grandmasterPriority2; + ClockQuality *clockQuality; + uint16_t stepsRemoved; + unsigned char timeSource; + + PTPMessageAnnounce(void); + public: + PTPMessageAnnounce(IEEE1588Port * port); + ~PTPMessageAnnounce(); + + bool isBetterThan(PTPMessageAnnounce * msg); + + unsigned char getGrandmasterPriority1(void) { + return grandmasterPriority1; + } + unsigned char getGrandmasterPriority2(void) { + return grandmasterPriority2; + } + + ClockQuality *getGrandmasterClockQuality(void) { + return clockQuality; + } + + uint16_t getStepsRemoved(void) { + return stepsRemoved; + } + + void getGrandmasterIdentity(char *identity) { + memcpy(identity, grandmasterIdentity, + PTP_CLOCK_IDENTITY_LENGTH); + } + + void processMessage(IEEE1588Port * port); + + void sendPort(IEEE1588Port * port, PortIdentity * destIdentity); + + friend PTPMessageCommon *buildPTPMessage(char *buf, int size, + LinkLayerAddress * remote, + IEEE1588Port * port); +}; + +class PTPMessageSync:public PTPMessageCommon { + private: + Timestamp originTimestamp; + + PTPMessageSync() { + return; + } + public: + PTPMessageSync(IEEE1588Port * port); + void processMessage(IEEE1588Port * port); + + Timestamp getOriginTimestamp(void) { + return originTimestamp; + } + + void sendPort(IEEE1588Port * port, PortIdentity * destIdentity); + + friend PTPMessageCommon *buildPTPMessage(char *buf, int size, + LinkLayerAddress * remote, + IEEE1588Port * port); +}; + +#pragma pack(push,1) + +class scaledNs { + private: + int32_t ms; + uint64_t ls; + public: + scaledNs() { + ms = 0; + ls = 0; + } void toByteString(uint8_t * byte_str) { + memcpy(byte_str, this, sizeof(*this)); + } +}; + +class FollowUpTLV { + private: + uint16_t tlvType; + uint16_t lengthField; + uint8_t organizationId[3]; + uint8_t organizationSubType_ms; + uint16_t organizationSubType_ls; + int32_t cumulativeScaledRateOffset; + uint16_t gmTimeBaseIndicator; + scaledNs scaledLastGmPhaseChange; + int32_t scaledLastGmFreqChange; + public: + FollowUpTLV() { + tlvType = PLAT_htons(0x3); + lengthField = PLAT_htons(28); + organizationId[0] = '\x00'; + organizationId[1] = '\x80'; + organizationId[2] = '\xC2'; + organizationSubType_ms = 0; + organizationSubType_ls = PLAT_htons(1); + cumulativeScaledRateOffset = PLAT_htonl(0); + gmTimeBaseIndicator = PLAT_htons(0); + scaledLastGmFreqChange = PLAT_htonl(0); + } + void toByteString(uint8_t * byte_str) { + memcpy(byte_str, this, sizeof(*this)); + } +}; + +#pragma pack(pop) + +class PTPMessageFollowUp:public PTPMessageCommon { + private: + Timestamp preciseOriginTimestamp; + + FollowUpTLV tlv; + + PTPMessageFollowUp(void) { + } public: + PTPMessageFollowUp(IEEE1588Port * port); + void sendPort(IEEE1588Port * port, PortIdentity * destIdentity); + void processMessage(IEEE1588Port * port); + + Timestamp getPreciseOriginTimestamp(void) { + return preciseOriginTimestamp; + } + void setPreciseOriginTimestamp(Timestamp & timestamp) { + preciseOriginTimestamp = timestamp; + } + + friend PTPMessageCommon *buildPTPMessage(char *buf, int size, + LinkLayerAddress * remote, + IEEE1588Port * port); +}; + +class PTPMessagePathDelayReq:public PTPMessageCommon { + private: + Timestamp originTimestamp; + + PTPMessagePathDelayReq() { + return; + } + public: + PTPMessagePathDelayReq(IEEE1588Port * port); + void sendPort(IEEE1588Port * port, PortIdentity * destIdentity); + void processMessage(IEEE1588Port * port); + + Timestamp getOriginTimestamp(void) { + return originTimestamp; + } + + friend PTPMessageCommon *buildPTPMessage(char *buf, int size, + LinkLayerAddress * remote, + IEEE1588Port * port); +}; + +class PTPMessagePathDelayResp:public PTPMessageCommon { + private: + PortIdentity * requestingPortIdentity; + Timestamp requestReceiptTimestamp; + + PTPMessagePathDelayResp(void) { + } public: + ~PTPMessagePathDelayResp(); + PTPMessagePathDelayResp(IEEE1588Port * port); + void sendPort(IEEE1588Port * port, PortIdentity * destIdentity); + void processMessage(IEEE1588Port * port); + + void setRequestReceiptTimestamp(Timestamp timestamp) { + requestReceiptTimestamp = timestamp; + } + + void setRequestingPortIdentity(PortIdentity * identity); + void getRequestingPortIdentity(PortIdentity * identity); + + Timestamp getRequestReceiptTimestamp(void) { + return requestReceiptTimestamp; + } + + friend PTPMessageCommon *buildPTPMessage(char *buf, int size, + LinkLayerAddress * remote, + IEEE1588Port * port); +}; + +class PTPMessagePathDelayRespFollowUp:public PTPMessageCommon { + private: + Timestamp responseOriginTimestamp; + PortIdentity *requestingPortIdentity; + + PTPMessagePathDelayRespFollowUp(void) { + } public: + PTPMessagePathDelayRespFollowUp(IEEE1588Port * port); + ~PTPMessagePathDelayRespFollowUp(); + void sendPort(IEEE1588Port * port, PortIdentity * destIdentity); + void processMessage(IEEE1588Port * port); + + void setResponseOriginTimestamp(Timestamp timestamp) { + responseOriginTimestamp = timestamp; + } + void setRequestingPortIdentity(PortIdentity * identity); + + Timestamp getResponseOriginTimestamp(void) { + return responseOriginTimestamp; + } + PortIdentity *getRequestingPortIdentity(void) { + return requestingPortIdentity; + } + + friend PTPMessageCommon *buildPTPMessage(char *buf, int size, + LinkLayerAddress * remote, + IEEE1588Port * port); +}; + +#endif diff --git a/daemons/gptp/common/avbts_oscondition.hpp b/daemons/gptp/common/avbts_oscondition.hpp new file mode 100644 index 0000000..90232f0 --- /dev/null +++ b/daemons/gptp/common/avbts_oscondition.hpp @@ -0,0 +1,63 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef AVBTS_OSSIGNAL_HPP +#define AVBTS_OSSIGNAL_HPP + class OSCondition { +private: + int wait_count; +public: + virtual bool wait() = 0; + virtual bool wait_prelock() = 0; + virtual bool signal() = 0; +protected: + OSCondition() { + wait_count = 0; + }; + void up() { + ++wait_count; + } + void down() { + --wait_count; + } + bool waiting() { + return wait_count > 0; + } +}; + + class OSConditionFactory { + public:virtual OSCondition * createCondition() = 0; +}; + + +#endif diff --git a/daemons/gptp/common/avbts_osipc.hpp b/daemons/gptp/common/avbts_osipc.hpp new file mode 100644 index 0000000..ce7f576 --- /dev/null +++ b/daemons/gptp/common/avbts_osipc.hpp @@ -0,0 +1,44 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef AVBTS_OSIPC_HPP +#define AVBTS_OSIPC_HPP + class OS_IPC { + public:virtual bool init() = 0; + virtual bool update(int64_t ml_phoffset, int64_t ls_phoffset, + int32_t ml_freqoffset, int32_t ls_freq_offset, + uint64_t local_time) = 0; +}; + + +#endif diff --git a/daemons/gptp/common/avbts_oslock.hpp b/daemons/gptp/common/avbts_oslock.hpp new file mode 100644 index 0000000..8877ada --- /dev/null +++ b/daemons/gptp/common/avbts_oslock.hpp @@ -0,0 +1,54 @@ +/****************************************************************************** + + Copyright (c) 2001-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef AVBTS_OSLOCK_HPP +#define AVBTS_OSLOCK_HPP + typedef enum { oslock_recursive, oslock_nonrecursive } OSLockType; +typedef enum { oslock_ok, oslock_self, oslock_held, oslock_fail } OSLockResult; + class OSLock { + public:virtual OSLockResult lock() = 0; + virtual OSLockResult unlock() = 0; + virtual OSLockResult trylock() = 0; + protected:OSLock() { + }; + bool initialize(OSLockType type) { + return false; + }; +}; + + class OSLockFactory { + public:virtual OSLock * createLock(OSLockType type) = 0; +}; + + +#endif diff --git a/daemons/gptp/common/avbts_osnet.cpp b/daemons/gptp/common/avbts_osnet.cpp new file mode 100644 index 0000000..2039f23 --- /dev/null +++ b/daemons/gptp/common/avbts_osnet.cpp @@ -0,0 +1,37 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#include + +std::map < factory_name_t, + OSNetworkInterfaceFactory * >OSNetworkInterfaceFactory::factoryMap; diff --git a/daemons/gptp/common/avbts_osnet.hpp b/daemons/gptp/common/avbts_osnet.hpp new file mode 100644 index 0000000..5656072 --- /dev/null +++ b/daemons/gptp/common/avbts_osnet.hpp @@ -0,0 +1,176 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef AVBTS_OSNET_HPP +#define AVBTS_OSNET_HPP + +#include +#include +#include +#include + +#define FACTORY_NAME_LENGTH 48 +#define ETHER_ADDR_OCTETS 6 +#define PTP_ETHERTYPE 0x88F7 +#define DEFAULT_TIMEOUT 1 // milliseconds + +class LinkLayerAddress:public InterfaceLabel { + private: + uint8_t addr[ETHER_ADDR_OCTETS]; + public: + LinkLayerAddress() { + }; + LinkLayerAddress(uint64_t address_scalar) { + uint8_t *ptr; + address_scalar <<= 16; + for (ptr = addr; ptr < addr + ETHER_ADDR_OCTETS; ++ptr) { + *ptr = (address_scalar & 0xFF00000000000000ULL) >> 56; + address_scalar <<= 8; + } + } + LinkLayerAddress(uint8_t * address_octet_array) { + uint8_t *ptr; + for (ptr = addr; ptr < addr + ETHER_ADDR_OCTETS; + ++ptr, ++address_octet_array) { + *ptr = *address_octet_array; + } + } + bool operator==(const LinkLayerAddress & cmp) const { + return memcmp(this->addr, cmp.addr, + ETHER_ADDR_OCTETS) == 0 ? true : false; + } + bool operator<(const LinkLayerAddress & cmp)const { + return memcmp(this->addr, cmp.addr, + ETHER_ADDR_OCTETS) < 0 ? true : false; + } + bool operator>(const LinkLayerAddress & cmp)const { + return memcmp(this->addr, cmp.addr, + ETHER_ADDR_OCTETS) < 0 ? true : false; + } + void toOctetArray(uint8_t * address_octet_array) { + uint8_t *ptr; + for (ptr = addr; ptr < addr + ETHER_ADDR_OCTETS; + ++ptr, ++address_octet_array) { + *address_octet_array = *ptr; + } + } +}; + +class InterfaceName:public InterfaceLabel { + private: + char *name; + public: + InterfaceName() { + }; + InterfaceName(char *name, int length) { + this->name = new char[length + 1]; + PLAT_strncpy(this->name, name, length); + } + bool operator==(const InterfaceName & cmp) const { + return strcmp(name, cmp.name) == 0 ? true : false; + } + bool operator<(const InterfaceName & cmp)const { + return strcmp(name, cmp.name) < 0 ? true : false; + } + bool operator>(const InterfaceName & cmp)const { + return strcmp(name, cmp.name) < 0 ? true : false; + } + bool toString(char *string, size_t length) { + if (length >= strlen(name) + 1) { + PLAT_strncpy(string, name, length); + return true; + } + return false; + } +}; + +class factory_name_t { + private: + char name[FACTORY_NAME_LENGTH]; + factory_name_t(); + public: + factory_name_t(const char *name_a) { + PLAT_strncpy(name, name_a, FACTORY_NAME_LENGTH - 1); + } + bool operator==(const factory_name_t & cmp) { + return strcmp(cmp.name, this->name) == 0 ? true : false; + } + bool operator<(const factory_name_t & cmp)const { + return strcmp(cmp.name, this->name) < 0 ? true : false; + } + bool operator>(const factory_name_t & cmp)const { + return strcmp(cmp.name, this->name) > 0 ? true : false; + } +}; + +typedef enum { net_trfail, net_fatal, net_succeed } net_result; + +class OSNetworkInterface { + public: + virtual net_result send(LinkLayerAddress * addr, uint8_t * payload, + size_t length, bool timestamp) = 0; + virtual net_result recv(LinkLayerAddress * addr, uint8_t * payload, + size_t & length) = 0; + virtual void getLinkLayerAddress(LinkLayerAddress * addr) = 0; + virtual unsigned getPayloadOffset() = 0; +}; + +class OSNetworkInterfaceFactory; + +typedef std::map < factory_name_t, OSNetworkInterfaceFactory * >FactoryMap_t; + +class OSNetworkInterfaceFactory { + public: + static bool registerFactory(factory_name_t id, + OSNetworkInterfaceFactory * factory) { + FactoryMap_t::iterator iter = factoryMap.find(id); + if (iter != factoryMap.end()) + return false; + factoryMap[id] = factory; + return true; + } + static bool buildInterface(OSNetworkInterface ** iface, + factory_name_t id, + InterfaceLabel * iflabel, + HWTimestamper * timestamper) { + return factoryMap[id]->createInterface(iface, iflabel, + timestamper); + } + private: + virtual bool createInterface(OSNetworkInterface ** iface, + InterfaceLabel * iflabel, + HWTimestamper * timestamper) = 0; + static FactoryMap_t factoryMap; +}; + +#endif diff --git a/daemons/gptp/common/avbts_osthread.hpp b/daemons/gptp/common/avbts_osthread.hpp new file mode 100644 index 0000000..24520ce --- /dev/null +++ b/daemons/gptp/common/avbts_osthread.hpp @@ -0,0 +1,48 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef AVBTS_OSTHREAD_HPP +#define AVBTS_OSTHREAD_HPP + typedef enum { osthread_ok, osthread_error } OSThreadExitCode; +typedef OSThreadExitCode(*OSThreadFunction) (void *); + class OSThread { + public:virtual bool start(OSThreadFunction function, void *arg) = 0; + virtual bool join(OSThreadExitCode & exit_code) = 0; +}; + + class OSThreadFactory { + public:virtual OSThread * createThread() = 0; +}; + + +#endif diff --git a/daemons/gptp/common/avbts_ostimer.hpp b/daemons/gptp/common/avbts_ostimer.hpp new file mode 100644 index 0000000..8fda4c1 --- /dev/null +++ b/daemons/gptp/common/avbts_ostimer.hpp @@ -0,0 +1,46 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef AVBTS_OSTIMER_HPP +#define AVBTS_OSTIMER_HPP + +class OSTimer { + public:virtual unsigned long sleep(unsigned long micro) = 0; +}; + +class OSTimerFactory { + public:virtual OSTimer * createTimer() = 0; +}; + + +#endif diff --git a/daemons/gptp/common/avbts_ostimerq.hpp b/daemons/gptp/common/avbts_ostimerq.hpp new file mode 100644 index 0000000..a0b1163 --- /dev/null +++ b/daemons/gptp/common/avbts_ostimerq.hpp @@ -0,0 +1,52 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef AVBTS_OSTIMERQ_HPP +#define AVBTS_OSTIMERQ_HPP + +typedef void (*ostimerq_handler) (void *); + +class OSTimerQueue { + public: + virtual bool addEvent(unsigned long micros, int type, + ostimerq_handler func, event_descriptor_t * arg, + bool dynamic, unsigned *event) = 0; + virtual bool cancelEvent(int type, unsigned *event) = 0; +}; + +class OSTimerQueueFactory { + public: + virtual OSTimerQueue * createOSTimerQueue() = 0; +}; + +#endif diff --git a/daemons/gptp/common/avbts_port.hpp b/daemons/gptp/common/avbts_port.hpp new file mode 100644 index 0000000..32d2e29 --- /dev/null +++ b/daemons/gptp/common/avbts_port.hpp @@ -0,0 +1,450 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef AVBTS_PORT_HPP +#define AVBTS_PORT_HPP + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define GPTP_MULTICAST 0x0180C200000EULL +#define PDELAY_MULTICAST GPTP_MULTICAST +#define OTHER_MULTICAST GPTP_MULTICAST + +#define PDELAY_RESP_RECEIPT_TIMEOUT_MULTIPLIER 3 +#define SYNC_RECEIPT_TIMEOUT_MULTIPLIER 10 +#define ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER 10 + +typedef enum { + PTP_MASTER, + PTP_PRE_MASTER, + PTP_SLAVE, + PTP_UNCALIBRATED, + PTP_DISABLED, + PTP_FAULTY, + PTP_INITIALIZING, + PTP_LISTENING +} PortState; + +typedef enum { + V1, + V2_E2E, + V2_P2P +} PortType; + +class PortIdentity { + private: + ClockIdentity clock_id; + uint16_t portNumber; + public: + PortIdentity() { + }; + PortIdentity(uint8_t * clock_id, uint16_t * portNumber) { + this->portNumber = *portNumber; + this->portNumber = PLAT_ntohs(this->portNumber); + this->clock_id.set(clock_id); + } + bool operator!=(const PortIdentity & cmp) const { + return !(this->clock_id == cmp.clock_id) + || this->portNumber != cmp.portNumber ? true : false; + } + bool operator==(const PortIdentity & cmp)const { + return this->clock_id == cmp.clock_id + && this->portNumber == cmp.portNumber ? true : false; + } + bool operator<(const PortIdentity & cmp)const { + return this->clock_id < cmp.clock_id ? true : + this->clock_id == cmp.clock_id + && this->portNumber < cmp.portNumber ? true : false; + } + bool operator>(const PortIdentity & cmp)const { + return this->clock_id > cmp.clock_id ? true : + this->clock_id == cmp.clock_id + && this->portNumber > cmp.portNumber ? true : false; + } + void getClockIdentityString(char *id) { + clock_id.getIdentityString(id); + } + void setClockIdentity(ClockIdentity clock_id) { + this->clock_id = clock_id; + } + ClockIdentity getClockIdentity( void ) { + return this->clock_id; + } + void getPortNumberNO(uint16_t * id) { // Network byte order + uint16_t portNumberNO = PLAT_htons(portNumber); + *id = portNumberNO; + } + void getPortNumber(uint16_t * id) { // Host byte order + *id = portNumber; + } + void setPortNumber(uint16_t * id) { + portNumber = *id; + } +}; + +typedef std::map < PortIdentity, LinkLayerAddress > IdentityMap_t; + +typedef std::list < PTPMessageAnnounce * >AnnounceList_t; + +class IEEE1588Port { + static LinkLayerAddress other_multicast; + static LinkLayerAddress pdelay_multicast; + + PortIdentity port_identity; + // directly connected node + PortIdentity peer_identity; + + OSNetworkInterface *net_iface; + LinkLayerAddress local_addr; + + // Port Configuration + unsigned char delay_mechanism; + PortState port_state; + char log_mean_unicast_sync_interval; + char log_mean_sync_interval; + char log_mean_announce_interval; + char log_min_mean_delay_req_interval; + char log_min_mean_pdelay_req_interval; + bool burst_enabled; + int64_t one_way_delay; // Allow this to be negative result of a large clock skew + // Implementation Specific data/methods + IEEE1588Clock *clock; // Pointer to clock object with which the port is associated + + bool _syntonize; + + bool asCapable; + + int32_t *rate_offset_array; + uint32_t rate_offset_array_size; + uint32_t rate_offset_count; + uint32_t rate_offset_index; + + int32_t _peer_rate_offset; + Timestamp _peer_offset_ts_theirs; + Timestamp _peer_offset_ts_mine; + bool _peer_offset_init; + + int32_t _master_rate_offset; + + int32_t _initial_clock_offset; + int32_t _current_clock_offset; + + bool _master_local_freq_offset_init; + int64_t _prev_master_local_offset; + Timestamp _prev_sync_time; + + bool _local_system_freq_offset_init; + int64_t _prev_local_system_offset; + Timestamp _prev_system_time; + + AnnounceList_t qualified_announce; + + uint16_t announce_sequence_id; + uint16_t sync_sequence_id; + + uint16_t pdelay_sequence_id; + PTPMessagePathDelayReq *last_pdelay_req; + PTPMessagePathDelayResp *last_pdelay_resp; + PTPMessagePathDelayRespFollowUp *last_pdelay_resp_fwup; + + // Network socket description + uint16_t ifindex; // physical interface number that object represents + + IdentityMap_t identity_map; + + PTPMessageSync *last_sync; + + OSThread *listening_thread; + + OSCondition *port_ready_condition; + + OSLock *pdelay_rx_lock; + OSLock *port_tx_lock; + + OSThreadFactory *thread_factory; + OSTimerFactory *timer_factory; + + HWTimestamper *_hw_timestamper; + + net_result port_send(uint8_t * buf, int size, MulticastType mcast_type, + PortIdentity * destIdentity, bool timestamp); + + InterfaceLabel *net_label; + + OSLockFactory *lock_factory; + OSConditionFactory *condition_factory; + public: + // Added for testing + bool forceSlave; + + OSTimerFactory *getTimerFactory() { + return timer_factory; + } + void setAsCapable(bool ascap) { + if (ascap != asCapable) { + fprintf(stderr, "AsCapable: %s\n", + ascap == true ? "Enabled" : "Disabled"); + } + asCapable = ascap; + } + + ~IEEE1588Port(); + IEEE1588Port(IEEE1588Clock * clock, uint16_t index, bool forceSlave, + HWTimestamper * timestamper, bool syntonize, + int32_t offset, InterfaceLabel * net_label, + OSConditionFactory * condition_factory, + OSThreadFactory * thread_factory, + OSTimerFactory * timer_factory, + OSLockFactory * lock_factory); + bool init_port(); + + void recoverPort(void); + void *openPort(void); + unsigned getPayloadOffset(); + void sendEventPort(uint8_t * buf, int len, MulticastType mcast_type, + PortIdentity * destIdentity); + void sendGeneralPort(uint8_t * buf, int len, MulticastType mcast_type, + PortIdentity * destIdentity); + void processEvent(Event e); + + PTPMessageAnnounce *calculateERBest(void); + + void addForeignMaster(PTPMessageAnnounce * msg); + void removeForeignMaster(PTPMessageAnnounce * msg); + void removeForeignMasterAll(void); + + void addQualifiedAnnounce(PTPMessageAnnounce * msg) { + qualified_announce.push_front(msg); + } + + void removeQualifiedAnnounce(PTPMessageAnnounce * msg) { + qualified_announce.remove(msg); + } + + char getSyncInterval(void) { + return log_mean_sync_interval; + } + char getAnnounceInterval(void) { + return log_mean_announce_interval; + } + char getPDelayInterval(void) { + return log_min_mean_pdelay_req_interval; + } + PortState getPortState(void) { + return port_state; + } + void getPortIdentity(PortIdentity & identity) { + identity = this->port_identity; + } + bool burstEnabled(void) { + return burst_enabled; + } + uint16_t getNextAnnounceSequenceId(void) { + return announce_sequence_id++; + } + uint16_t getNextSyncSequenceId(void) { + return sync_sequence_id++; + } + uint16_t getNextPDelaySequenceId(void) { + return pdelay_sequence_id++; + } + + uint16_t getParentLastSyncSequenceNumber(void); + void setParentLastSyncSequenceNumber(uint16_t num); + + IEEE1588Clock *getClock(void); + + void setLastSync(PTPMessageSync * msg) { + last_sync = msg; + } + PTPMessageSync *getLastSync(void) { + return last_sync; + } + + bool getPDelayRxLock() { + return pdelay_rx_lock->lock() == oslock_ok ? true : false; + } + + bool tryPDelayRxLock() { + return pdelay_rx_lock->trylock() == oslock_ok ? true : false; + } + + bool putPDelayRxLock() { + return pdelay_rx_lock->unlock() == oslock_ok ? true : false; + } + + int getTxLock() { + return port_tx_lock->lock() == oslock_ok ? true : false; + } + int putTxLock() { + return port_tx_lock->unlock() == oslock_ok ? true : false; + } + + void setLastPDelayReq(PTPMessagePathDelayReq * msg) { + last_pdelay_req = msg; + } + PTPMessagePathDelayReq *getLastPDelayReq(void) { + return last_pdelay_req; + } + + void setLastPDelayResp(PTPMessagePathDelayResp * msg) { + last_pdelay_resp = msg; + } + PTPMessagePathDelayResp *getLastPDelayResp(void) { + return last_pdelay_resp; + } + + void setLastPDelayRespFollowUp(PTPMessagePathDelayRespFollowUp * msg) { + last_pdelay_resp_fwup = msg; + } + PTPMessagePathDelayRespFollowUp *getLastPDelayRespFollowUp(void) { + return last_pdelay_resp_fwup; + } + + int calcMasterLocalClockRateDifference(signed long long offset, + Timestamp sync_time); + int calcLocalSystemClockRateDifference(signed long long offset, + Timestamp sync_time); + + int32_t getMasterRateOffset(void) { + return _master_rate_offset; + } + void setMasterRateOffset(int32_t offset + /* parts-per-trillion frequency offset */ ) { + _master_rate_offset = offset; + } + int32_t getPeerRateOffset(void) { + return _peer_rate_offset; + } + void setPeerRateOffset(int32_t offset + /* parts-per-trillion frequency offset */ ) { + _peer_rate_offset = offset; + } + void setPeerOffset(Timestamp mine, Timestamp theirs) { + _peer_offset_ts_mine = mine; + _peer_offset_ts_theirs = theirs; + _peer_offset_init = true; + } + bool getPeerOffset(Timestamp & mine, Timestamp & theirs) { + mine = _peer_offset_ts_mine; + theirs = _peer_offset_ts_theirs; + return _peer_offset_init; + } + + bool _adjustClockRate(int32_t freq_offset, unsigned counter_value, + Timestamp master_timestamp, int64_t offset, + bool change_master) { + if (_hw_timestamper) { + return + _hw_timestamper->HWTimestamper_adjclockrate + (freq_offset, counter_value, master_timestamp, + offset, change_master); + } + return false; + } + bool adjustClockRate(int32_t freq_offset, unsigned counter_value, + Timestamp master_timestamp, int64_t offset, + bool change_master) { + return _adjustClockRate(freq_offset, counter_value, + master_timestamp, offset, + change_master); + } + + bool doSyntonization(void) { + return _syntonize; + } + + void getExtendedError(char *msg) { + if (_hw_timestamper) { + _hw_timestamper->HWTimestamper_get_extderror(msg); + } else { + *msg = '\0'; + } + } + + bool getExternalClockRate(Timestamp & local_time, + int64_t & external_local_offset, + int32_t & external_local_freq_offset) { + bool s = false; + if (_hw_timestamper) { + s = _hw_timestamper->HWTimestamper_get_extclk_offset + (&local_time, &external_local_offset, + &external_local_freq_offset); + } + return s; + } + + int getRxTimestamp(PortIdentity * sourcePortIdentity, + uint16_t sequenceId, Timestamp & timestamp, + unsigned &counter_value, bool last); + int getTxTimestamp(PortIdentity * sourcePortIdentity, + uint16_t sequenceId, Timestamp & timestamp, + unsigned &counter_value, bool last); + + int getTxTimestamp(PTPMessageCommon * msg, Timestamp & timestamp, + unsigned &counter_value, bool last); + int getRxTimestamp(PTPMessageCommon * msg, Timestamp & timestamp, + unsigned &counter_value, bool last); + + void getDeviceTime(Timestamp & system_time, Timestamp & device_time, + uint32_t & local_clock, + uint32_t & nominal_clock_rate); + + int64_t getLinkDelay(void) { + return one_way_delay; + } + void setLinkDelay(int64_t delay) { + one_way_delay = delay; + } + + void recommendState(PortState state, bool changed_master); + + void mapSocketAddr(PortIdentity * destIdentity, + LinkLayerAddress * remote); + void addSockAddrMap(PortIdentity * destIdentity, + LinkLayerAddress * remote); +}; + +#endif diff --git a/daemons/gptp/common/ieee1588.hpp b/daemons/gptp/common/ieee1588.hpp new file mode 100644 index 0000000..bffd555 --- /dev/null +++ b/daemons/gptp/common/ieee1588.hpp @@ -0,0 +1,231 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef IEEE1588_HPP +#define IEEE1588_HPP + +#include + +#include + +#include + +#include + +#include + +#define MAX_PORTS 32 + +#define PTP_CLOCK_IDENTITY_LENGTH 8 + +class LinkLayerAddress; +struct ClockQuality; +class PortIdentity; +class IEEE1588Clock; +class PTPMessageCommon; +class PTPMessageSync; +class PTPMessageAnnounce; +class PTPMessagePathDelayReq; +class PTPMessagePathDelayResp; +class PTPMessagePathDelayRespFollowUp; +class IEEE1588Port; + +typedef enum { + NULL_EVENT = 0, + POWERUP = 5, + INITIALIZE, + STATE_CHANGE_EVENT, + SYNC_INTERVAL_TIMEOUT_EXPIRES, + PDELAY_INTERVAL_TIMEOUT_EXPIRES, + SYNC_RECEIPT_TIMEOUT_EXPIRES, + QUALIFICATION_TIMEOUT_EXPIRES, + ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES, + ANNOUNCE_INTERVAL_TIMEOUT_EXPIRES, + FAULT_DETECTED, + PDELAY_DEFERRED_PROCESSING, + PDELAY_RESP_RECEIPT_TIMEOUT_EXPIRES, +} Event; + +typedef struct { + IEEE1588Port *port; + Event event; +} event_descriptor_t; + +class InterfaceLabel { + public: + virtual ~ InterfaceLabel() { + }; +}; + +class ClockIdentity { + private: + uint8_t id[PTP_CLOCK_IDENTITY_LENGTH]; + public: + bool operator==(const ClockIdentity & cmp) const { + return memcmp(this->id, cmp.id, + PTP_CLOCK_IDENTITY_LENGTH) == 0 ? true : false; + } + bool operator!=( const ClockIdentity &cmp ) const { + return memcmp( this->id, cmp.id, PTP_CLOCK_IDENTITY_LENGTH ) != 0 ? true : false; + } + bool operator<(const ClockIdentity & cmp)const { + return memcmp(this->id, cmp.id, + PTP_CLOCK_IDENTITY_LENGTH) < 0 ? true : false; + } + bool operator>(const ClockIdentity & cmp)const { + return memcmp(this->id, cmp.id, + PTP_CLOCK_IDENTITY_LENGTH) < 0 ? true : false; + } + void getIdentityString(char *id) { + memcpy(id, this->id, PTP_CLOCK_IDENTITY_LENGTH); + } + void set(uint8_t * id) { + memcpy(this->id, id, PTP_CLOCK_IDENTITY_LENGTH); + } + void set(LinkLayerAddress * address); +}; + +class Timestamp { + public: + Timestamp(uint32_t ns, uint32_t s_l, uint16_t s_m) { + nanoseconds = ns; + seconds_ls = s_l; + seconds_ms = s_m; + } + Timestamp() { + }; + uint32_t nanoseconds; + uint32_t seconds_ls; + uint16_t seconds_ms; +}; + +#define INVALID_TIMESTAMP (Timestamp( 0xC0000000, 0, 0 )) +#define PDELAY_PENDING_TIMESTAMP (Timestamp( 0xC0000001, 0, 0 )) + +#define TIMESTAMP_TO_NS(ts) (((static_cast(ts.seconds_ms) << sizeof(ts.seconds_ls)*8) + ts.seconds_ls)*1000000000LL + \ + ts.nanoseconds) + +static inline uint64_t bswap_64(uint64_t in) +{ + uint8_t *s = (uint8_t *) & in; + uint8_t *e = s + 7; + while (e > s) { + uint8_t t; + t = *s; + *s = *e; + *e = t; + ++s; + --e; + } + return in; +} + +static inline void TIMESTAMP_SUB_NS(Timestamp & ts, uint64_t ns) +{ + while (ns >= 1000000000) { + if (ts.seconds_ls != 0) { + --ts.seconds_ls; + } else { + --ts.seconds_ms; + ts.seconds_ls = 0xFFFFFFFF; + } + ns -= 1000000000; + } + ts.nanoseconds -= (uint32_t) ns; +} + +#define XPTPD_ERROR(fmt,...) fprintf( stderr, "ERROR at %u in %s: " fmt "\n", __LINE__, __FILE__ ,## __VA_ARGS__) +#ifdef PTP_DEBUG +#define XPTPD_INFO(fmt,...) fprintf( stderr, "DEBUG at %u in %s: " fmt "\n", __LINE__, __FILE__ ,## __VA_ARGS__) +#else +#define XPTPD_INFO(fmt,...) +#endif + +#define HWTIMESTAMPER_EXTENDED_MESSAGE_SIZE 4096 + +class HWTimestamper { + public: + virtual bool HWTimestamper_init(InterfaceLabel * iface_label) { + return true; + } + virtual void HWTimestamper_final(void) { + } + + virtual bool HWTimestamper_adjclockrate(int32_t frequency_offset, + unsigned counter_value, + Timestamp master_timestamp, + int64_t offset, + bool changed_master) { + return false; + } + virtual bool HWTimestamper_adjclockrate2(int32_t ppt_adjustment) { + return false; + } + + virtual bool HWTimestamper_gettime(Timestamp * system_time, + Timestamp * device_time, + uint32_t * local_clock, + uint32_t * nominal_clock_rate) = 0; + + virtual int HWTimestamper_txtimestamp(PortIdentity * identity, + uint16_t sequenceId, + Timestamp & timestamp, + unsigned &clock_value, + bool last) = 0; + + virtual int HWTimestamper_rxtimestamp(PortIdentity * identity, + uint16_t sequenceId, + Timestamp & timestamp, + unsigned &clock_value, + bool last) = 0; + + virtual bool HWTimestamper_get_extclk_offset(Timestamp * local_time, + int64_t * clk_offset, + int32_t * + ppt_freq_offset) { + return false; + } + + virtual void HWTimestamper_get_extderror(char *msg) { + *msg = '\0'; + } + + virtual ~ HWTimestamper() { + } +}; + +PTPMessageCommon *buildPTPMessage(char *buf, int size, + LinkLayerAddress * remote, + IEEE1588Port * port); + +#endif diff --git a/daemons/gptp/common/ieee1588clock.cpp b/daemons/gptp/common/ieee1588clock.cpp new file mode 100644 index 0000000..6f289fc --- /dev/null +++ b/daemons/gptp/common/ieee1588clock.cpp @@ -0,0 +1,197 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#include + +#include +#include +#include + +#include + +#include + +#include + +#include + +void ClockIdentity::set(LinkLayerAddress * addr) +{ + uint64_t tmp1 = 0; + uint32_t tmp2; + addr->toOctetArray((uint8_t *) & tmp1); + tmp2 = tmp1 & 0xFFFFFF; + tmp1 >>= 24; + tmp1 <<= 16; + tmp1 |= 0xFEFF; + tmp1 <<= 24; + tmp1 |= tmp2; + memcpy(id, &tmp1, PTP_CLOCK_IDENTITY_LENGTH); +} + +IEEE1588Clock::IEEE1588Clock(bool forceOrdinarySlave, + OSTimerQueueFactory * timerq_factory, OS_IPC * ipc) +{ + timerq = timerq_factory->createOSTimerQueue(); + + priority1 = 248; + priority2 = 248; + + master_local_offset_nrst125us_initialized = false; + + number_ports = 0; + + this->forceOrdinarySlave = forceOrdinarySlave; + + clock_quality.clockAccuracy = 0xfe; + clock_quality.cq_class = 248; + clock_quality.offsetScaledLogVariance = 16640; + + time_source = 160; + + domain_number = 0; + + this->ipc = ipc; + + memset( &LastEBestIdentity, 0xFF, sizeof( LastEBestIdentity )); + return; +} + +Timestamp IEEE1588Clock::getSystemTime(void) +{ + return (Timestamp(0, 0, 0)); +} + +void timerq_handler(void *arg) +{ + event_descriptor_t *event_descriptor = (event_descriptor_t *) arg; + event_descriptor->port->processEvent(event_descriptor->event); +} + +void IEEE1588Clock::addEventTimer(IEEE1588Port * target, Event e, + unsigned long long time_ns) +{ + event_descriptor_t *event_descriptor = new event_descriptor_t(); + event_descriptor->event = e; + event_descriptor->port = target; + timerq->addEvent((unsigned)time_ns / 1000, (int)e, timerq_handler, + event_descriptor, true, NULL); +} + +void IEEE1588Clock::deleteEventTimer(IEEE1588Port * target, Event event) +{ + timerq->cancelEvent((int)event, NULL); +} + +// Sync clock to argument time +void IEEE1588Clock::setMasterOffset(int64_t master_local_offset, + Timestamp local_time, + int32_t master_local_freq_offset, + int64_t local_system_offset, + Timestamp system_time, + int32_t local_system_freq_offset, + uint32_t nominal_clock_rate, + uint32_t local_clock) +{ + + if (ipc != NULL) + ipc->update(master_local_offset, local_system_offset, + master_local_freq_offset, local_system_freq_offset, + TIMESTAMP_TO_NS(local_time)); + return; +} + +void IEEE1588Clock::getGrandmasterIdentity(char *id) +{ + grandmaster_port_identity.getClockIdentityString(id); +} + +// Get current time from system clock +Timestamp IEEE1588Clock::getTime(void) +{ + return getSystemTime(); +} + +// Get timestamp from hardware +Timestamp IEEE1588Clock::getPreciseTime(void) +{ + return getSystemTime(); +} + +bool IEEE1588Clock::isBetterThan(PTPMessageAnnounce * msg) +{ + unsigned char this1[14]; + unsigned char that1[14]; + uint16_t tmp; + + this1[0] = priority1; + that1[0] = msg->getGrandmasterPriority1(); + + this1[1] = clock_quality.cq_class; + that1[1] = msg->getGrandmasterClockQuality()->cq_class; + + this1[2] = clock_quality.clockAccuracy; + that1[2] = msg->getGrandmasterClockQuality()->clockAccuracy; + + tmp = clock_quality.offsetScaledLogVariance; + tmp = PLAT_htons(tmp); + memcpy(this1 + 3, &tmp, sizeof(tmp)); + tmp = msg->getGrandmasterClockQuality()->offsetScaledLogVariance; + tmp = PLAT_htons(tmp); + memcpy(that1 + 3, &tmp, sizeof(tmp)); + + this1[5] = priority2; + that1[5] = msg->getGrandmasterPriority2(); + + clock_identity.getIdentityString((char *)this1 + 6); + //memcpy( this1+6, clock_identity, PTP_CLOCK_IDENTITY_LENGTH ); + msg->getGrandmasterIdentity((char *)that1 + 6); + +#if 0 + fprintf(stderr, "(Clk)Us: "); + for (int i = 0; i < 14; ++i) + fprintf(stderr, "%hhx ", this1[i]); + fprintf(stderr, "\n"); + fprintf(stderr, "(Clk)Them: "); + for (int i = 0; i < 14; ++i) + fprintf(stderr, "%hhx ", that1[i]); + fprintf(stderr, "\n"); +#endif + + return (memcmp(this1, that1, 14) < 0) ? true : false; +} + +IEEE1588Clock::~IEEE1588Clock(void) +{ + // Unmap shared memory +} diff --git a/daemons/gptp/common/ieee1588port.cpp b/daemons/gptp/common/ieee1588port.cpp new file mode 100644 index 0000000..cf62fd2 --- /dev/null +++ b/daemons/gptp/common/ieee1588port.cpp @@ -0,0 +1,1031 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include + +LinkLayerAddress IEEE1588Port::other_multicast(OTHER_MULTICAST); +LinkLayerAddress IEEE1588Port::pdelay_multicast(PDELAY_MULTICAST); + +OSThreadExitCode openPortWrapper(void *arg) +{ + IEEE1588Port *port; + + port = (IEEE1588Port *) arg; + if (port->openPort() == NULL) + return osthread_ok; + else + return osthread_error; +} + +IEEE1588Port::~IEEE1588Port() +{ + delete port_ready_condition; + delete [] rate_offset_array; +} + +IEEE1588Port::IEEE1588Port(IEEE1588Clock * clock, uint16_t index, + bool forceSlave, HWTimestamper * timestamper, + bool syntonize, int32_t offset, + InterfaceLabel * net_label, + OSConditionFactory * condition_factory, + OSThreadFactory * thread_factory, + OSTimerFactory * timer_factory, + OSLockFactory * lock_factory) +{ + sync_sequence_id = 0; + last_pdelay_req = NULL; + + clock->registerPort(this, index); + this->clock = clock; + ifindex = index; + + this->forceSlave = forceSlave; + + asCapable = false; + + announce_sequence_id = 0; + sync_sequence_id = 0; + pdelay_sequence_id = 0; + + sync_sequence_id = 0; + + log_mean_sync_interval = -3; + log_mean_announce_interval = 0; + log_min_mean_pdelay_req_interval = 0; + + _current_clock_offset = _initial_clock_offset = offset; + + rate_offset_array = NULL; + + _hw_timestamper = timestamper; + + if (_hw_timestamper != NULL) { + if (!_hw_timestamper->HWTimestamper_init(net_label)) { + XPTPD_ERROR + ("Failed to initialize hardware timestamper, falling back to software timestamping"); + _hw_timestamper = NULL; + } + } + + _syntonize = syntonize; + _master_local_freq_offset_init = false; + _local_system_freq_offset_init = false; + + one_way_delay = 3600000000000; + + _peer_rate_offset = 0; + _master_rate_offset = 0; + + last_sync = NULL; + last_pdelay_req = NULL; + last_pdelay_resp = NULL; + last_pdelay_resp_fwup = NULL; + + this->net_label = net_label; + + this->timer_factory = timer_factory; + this->thread_factory = thread_factory; + + this->condition_factory = condition_factory; + this->lock_factory = lock_factory; +} + +bool IEEE1588Port::init_port() +{ + if (!OSNetworkInterfaceFactory::buildInterface + (&net_iface, factory_name_t("default"), net_label, _hw_timestamper)) + return false; + + this->net_iface = net_iface; + this->net_iface->getLinkLayerAddress(&local_addr); + clock->setClockIdentity(&local_addr); + + pdelay_rx_lock = lock_factory->createLock(oslock_recursive); + port_tx_lock = lock_factory->createLock(oslock_recursive); + + port_identity.setClockIdentity(clock->getClockIdentity()); + port_identity.setPortNumber(&ifindex); + + port_ready_condition = condition_factory->createCondition(); + + return true; +} + +void *IEEE1588Port::openPort(void) +{ + fprintf(stderr, "openPort: thread started\n"); + + port_ready_condition->signal(); + + while (1) { + PTPMessageCommon *msg; + uint8_t buf[128]; + LinkLayerAddress remote; + net_result rrecv; + size_t length = sizeof(buf); + + if ((rrecv = net_iface->recv(&remote, buf, length)) == net_succeed) { + XPTPD_INFO("Processing network buffer"); + msg = buildPTPMessage((char *)buf, (int)length, &remote, + this); + if (msg != NULL) { + XPTPD_INFO("Processing message"); + msg->processMessage(this); + if (msg->garbage()) { + delete msg; + } + } else { + XPTPD_ERROR("Discarding invalid message"); + } + } else if (rrecv == net_fatal) { + XPTPD_ERROR("read from network interface failed"); + this->processEvent(FAULT_DETECTED); + break; + } + } + + return NULL; +} + +net_result IEEE1588Port::port_send(uint8_t * buf, int size, + MulticastType mcast_type, + PortIdentity * destIdentity, bool timestamp) +{ + LinkLayerAddress dest; + + if (mcast_type != MCAST_NONE) { + if (mcast_type == MCAST_PDELAY) { + dest = pdelay_multicast; + } else { + dest = other_multicast; + } + } else { + mapSocketAddr(destIdentity, &dest); + } + + return net_iface->send(&dest, (uint8_t *) buf, size, timestamp); +} + +unsigned IEEE1588Port::getPayloadOffset() +{ + return net_iface->getPayloadOffset(); +} + +void IEEE1588Port::sendEventPort(uint8_t * buf, int size, + MulticastType mcast_type, + PortIdentity * destIdentity) +{ + net_result rtx = port_send(buf, size, mcast_type, destIdentity, true); + if (rtx != net_succeed) { + XPTPD_ERROR("sendEventPort(): failure"); + } + + return; +} + +void IEEE1588Port::sendGeneralPort(uint8_t * buf, int size, + MulticastType mcast_type, + PortIdentity * destIdentity) +{ + net_result rtx = port_send(buf, size, mcast_type, destIdentity, false); + if (rtx != net_succeed) { + XPTPD_ERROR("sendEventPort(): failure"); + } + + return; +} + +void IEEE1588Port::processEvent(Event e) +{ + bool changed_master; + OSTimer *timer = timer_factory->createTimer(); + + switch (e) { + case POWERUP: + case INITIALIZE: + XPTPD_INFO("Received POWERUP/INITIALIZE event"); + { + unsigned long long interval1; + unsigned long long interval3; + unsigned long long interval4; + Event e1 = NULL_EVENT; + Event e3 = NULL_EVENT; + Event e4 = NULL_EVENT; + + if (forceSlave) { + port_state = PTP_SLAVE; + e1 = PDELAY_INTERVAL_TIMEOUT_EXPIRES; + interval1 = + (unsigned long + long)(pow((double)2, + getPDelayInterval()) * + 1000000000.0); + } else { + port_state = PTP_LISTENING; + e1 = PDELAY_INTERVAL_TIMEOUT_EXPIRES; + e3 = SYNC_RECEIPT_TIMEOUT_EXPIRES; + e4 = ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES; + interval1 = + (unsigned long + long)(pow((double)2, + getPDelayInterval()) * + 1000000000.0); + interval3 = + (unsigned long + long)(SYNC_RECEIPT_TIMEOUT_MULTIPLIER * + pow((double)2, + getSyncInterval()) * + 1000000000.0); + interval4 = + (unsigned long + long)(ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER * + pow((double)2, + getAnnounceInterval()) * + 1000000000.0); + } + + fprintf(stderr, "Starting port thread\n"); + port_ready_condition->wait_prelock(); + listening_thread = thread_factory->createThread(); + if (!listening_thread->start + (openPortWrapper, (void *)this)) { + XPTPD_ERROR("Error creating port thread\n"); + return; + } + port_ready_condition->wait(); + + if (e1 != NULL_EVENT) + clock->addEventTimer(this, e1, interval1); + if (e3 != NULL_EVENT) + clock->addEventTimer(this, e3, interval3); + if (e4 != NULL_EVENT) + clock->addEventTimer(this, e4, interval4); + } + break; + case STATE_CHANGE_EVENT: + if (!forceSlave) { + int number_ports, j; + PTPMessageAnnounce *EBest = NULL; + PortIdentity EBestPortIdentity; + + IEEE1588Port **ports; + clock->getPortList(number_ports, ports); + // If ANY ports are in PTP_INTIALIZING state, STATE_CHANGE_EVENT cannot be processed +#if 0 + for (int i = 0; i < number_ports; ++i) { + if (ports[i]->port_state == PTP_INITIALIZING) { + break; + } + } +#endif + //fprintf( stderr, "State Change Event\n" ); + + // Find EBest for all ports + j = 0; + for (int i = 0; i < number_ports; ++i) { + while (ports[j] == NULL) + ++j; + if (ports[j]->port_state == PTP_DISABLED + || ports[j]->port_state == PTP_FAULTY) { + continue; + } + if (EBest == NULL) { + EBest = ports[j]->calculateERBest(); + } else { + if (ports[j]->calculateERBest()->isBetterThan(EBest)) { + EBest = ports[j]->calculateERBest(); + } + } + } + + // Check if we've changed + EBest->getPortIdentity( &EBestPortIdentity ); + if( EBestPortIdentity.getClockIdentity() != clock->getLastEBestIdentity() ) { + fprintf( stderr, "Changed master!\n" ); + changed_master = true; + } else { + changed_master = false; + } + + j = 0; + for (int i = 0; i < number_ports; ++i) { + while (ports[j] == NULL) + ++j; + if (ports[j]->port_state == PTP_DISABLED + || ports[j]->port_state == PTP_FAULTY) { + continue; + } + if (clock->isBetterThan(EBest)) { + // We are the GrandMaster, all ports are master + fprintf(stderr, "\n"); + EBest = NULL; // EBest == NULL : we were grandmaster + ports[j]->recommendState(PTP_MASTER, + changed_master); + } else { + if (EBest == calculateERBest()) { + // The "best" sync was recieved on this port + ports[j]->recommendState + (PTP_SLAVE, changed_master); + } else { + // Otherwise we are the master because we have sync'd to a better clock + ports[j]->recommendState + (PTP_MASTER, + changed_master); + } + } + } + clock->setLastEBestIdentity( EBestPortIdentity.getClockIdentity() ); + } + break; + case ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES: + case SYNC_RECEIPT_TIMEOUT_EXPIRES: + { + if (forceSlave) { + break; + } + if (port_state == PTP_LISTENING + || port_state == PTP_UNCALIBRATED + || port_state == PTP_SLAVE + || port_state == PTP_PRE_MASTER) { + fprintf(stderr, + "***Sync Timeout Expired - Becoming Master: %d\n", + e); + port_state = PTP_MASTER; + Timestamp system_time; + Timestamp device_time; + int64_t local_system_offset; + +#if 0 + Timestamp crstamp_device_time; + int64_t external_local_offset; + int32_t external_local_freq_offset; +#endif + uint32_t local_clock, nominal_clock_rate; + + getDeviceTime(system_time, device_time, + local_clock, nominal_clock_rate); + + local_system_offset = + TIMESTAMP_TO_NS(system_time) - + TIMESTAMP_TO_NS(device_time); +#if 0 + local_system_offset = device_time.nanoseconds + + (((unsigned long long)device_time.seconds_ms + << sizeof(device_time.seconds_ls) * 8) + + device_time.seconds_ls) * 1000000000LL; + local_system_offset -= + system_time.nanoseconds + + (((unsigned long long)system_time.seconds_ms + << sizeof(system_time.seconds_ls) * 8) + + system_time.seconds_ls) * 1000000000LL; +#endif + + (void) + calcLocalSystemClockRateDifference + (local_system_offset, system_time); + + //getExternalClockRate( crstamp_device_time, external_local_offset, external_local_freq_offset ); + +#if 0 + clock->setMasterOffset(0, device_time, 0, + local_system_offset, + system_time, + local_system_freq_offset, + nominal_clock_rate, + local_clock); +#endif + if (this->doSyntonization()) { + this->adjustClockRate(0, local_clock, + device_time, 0, + false); + } + // "Expire" all previously received announce messages on this port + while (!qualified_announce.empty()) { + delete qualified_announce.back(); + qualified_announce.pop_back(); + } + + // Add timers for Announce and Sync, this is as close to immediately as we get + clock->addEventTimer(this, + ANNOUNCE_INTERVAL_TIMEOUT_EXPIRES, + 8000000); + clock->addEventTimer(this, + SYNC_INTERVAL_TIMEOUT_EXPIRES, + 8000000); + } + break; + case PDELAY_INTERVAL_TIMEOUT_EXPIRES: + XPTPD_INFO("PDELAY_INTERVAL_TIMEOUT_EXPIRES occured"); + { + int ts_good; + int iter = 2; + Timestamp req_timestamp; + long req = 1000; // = 1 ms + unsigned req_timestamp_counter_value; + + PTPMessagePathDelayReq *pdelay_req = + new PTPMessagePathDelayReq(this); + PortIdentity dest_id; + getPortIdentity(dest_id); + pdelay_req->setPortIdentity(&dest_id); + + { + Timestamp pending = + PDELAY_PENDING_TIMESTAMP; + pdelay_req->setTimestamp(pending); + } + + if (last_pdelay_req != NULL) { + delete last_pdelay_req; + } + setLastPDelayReq(pdelay_req); + + XPTPD_INFO("Preparing to send PDelay Request"); + getTxLock(); + pdelay_req->sendPort(this, NULL); + XPTPD_INFO("Sent PDelay Request"); + + ts_good = + getTxTimestamp(pdelay_req, req_timestamp, + req_timestamp_counter_value, + false); + while (ts_good != 0 && iter-- != 0) { + timer->sleep(req); + if (ts_good != -72 && iter < 1) + fprintf(stderr, + "Error (TX) timestamping PDelay request (Retrying-%d), error=%d\n", + iter, ts_good); + ts_good = + getTxTimestamp(pdelay_req, + req_timestamp, + req_timestamp_counter_value, + iter == 0); + req *= 2; + } + putTxLock(); + + //fprintf( stderr, "Sequence = %hu\n", pdelay_req->getSequenceId() ); + + if (pdelay_req == NULL) { + fprintf(stderr, + "PDelay request is NULL!\n"); + abort(); + } + + if (ts_good == 0) { + pdelay_req->setTimestamp(req_timestamp); + } else { + Timestamp failed = INVALID_TIMESTAMP; + pdelay_req->setTimestamp(failed); + } + + if (ts_good != 0) { + char msg + [HWTIMESTAMPER_EXTENDED_MESSAGE_SIZE]; + getExtendedError(msg); + fprintf(stderr, + "Error (TX) timestamping PDelay request, error=%d\n%s", + ts_good, msg); + //_exit(-1); + } +#ifdef DEBUG + if (ts_good == 0) { + XPTPD_INFO + ("Successful PDelay Req timestamp, %u,%u", + req_timestamp.seconds_ls, + req_timestamp.nanoseconds); + } else { + XPTPD_INFO + ("*** Unsuccessful PDelay Req timestamp"); + } +#endif + + } + clock->addEventTimer(this, + PDELAY_INTERVAL_TIMEOUT_EXPIRES, + (unsigned long + long)(pow((double)2, + getPDelayInterval()) * + 1000000000.0)); + break; + case SYNC_INTERVAL_TIMEOUT_EXPIRES: + XPTPD_INFO("SYNC_INTERVAL_TIMEOUT_EXPIRES occured"); + // Set offset from master to zero, update device vs system time offset + Timestamp system_time; + Timestamp device_time; + int32_t local_system_freq_offset; + int64_t local_system_offset; + static bool adj_up = true; + +#if 0 + Timestamp crstamp_device_time; + int64_t external_local_offset; + int32_t external_local_freq_offset; +#endif + uint32_t local_clock, nominal_clock_rate; + + getDeviceTime(system_time, device_time, local_clock, + nominal_clock_rate); + //fprintf( stderr, "Device Time = %llu,System Time = %llu\n", TIMESTAMP_TO_NS(device_time), TIMESTAMP_TO_NS(system_time)); + + XPTPD_INFO + ("port::processEvent(): System time: %u,%u Device Time: %u,%u", + system_time.seconds_ls, system_time.nanoseconds, + device_time.seconds_ls, device_time.nanoseconds); + + local_system_offset = + TIMESTAMP_TO_NS(system_time) - + TIMESTAMP_TO_NS(device_time); +#if 0 + local_system_offset = device_time.nanoseconds + + (((unsigned long long)device_time.seconds_ms << + sizeof(device_time.seconds_ls) * 8) + + device_time.seconds_ls) * 1000000000LL; + local_system_offset -= + system_time.nanoseconds + + (((unsigned long long)system_time.seconds_ms << + sizeof(system_time.seconds_ls) * 8) + + system_time.seconds_ls) * 1000000000LL; +#endif + + local_system_freq_offset = + calcLocalSystemClockRateDifference + (local_system_offset, system_time); + + //getExternalClockRate( crstamp_device_time, external_local_offset, external_local_freq_offset ); + + clock->setMasterOffset(0, device_time, 0, + local_system_offset, system_time, + local_system_freq_offset, + nominal_clock_rate, local_clock); + if (this->doSyntonization()) { + this->adjustClockRate(0, local_clock, + device_time, 0, false); + } + + if (_hw_timestamper != NULL + && _initial_clock_offset != 0) { + if (adj_up) { + _hw_timestamper->HWTimestamper_adjclockrate2 + (_current_clock_offset += 2500); + } else { + _hw_timestamper->HWTimestamper_adjclockrate2 + (_current_clock_offset -= 2500); + } + XPTPD_INFO("Adjust clock rate current: %d", + _current_clock_offset); + } + if (_current_clock_offset > + _initial_clock_offset + 5000000 + || _current_clock_offset < + _initial_clock_offset - 5000000) { + adj_up = !adj_up; + } + // Send a sync message and then a followup to broadcast + if (asCapable) { + PTPMessageSync *sync = new PTPMessageSync(this); + PortIdentity dest_id; + getPortIdentity(dest_id); + sync->setPortIdentity(&dest_id); + getTxLock(); + sync->sendPort(this, NULL); + XPTPD_INFO("Sent SYNC message"); + + int ts_good; + int iter = 2; + Timestamp sync_timestamp; + unsigned sync_timestamp_counter_value; + long req = 1000; // = 1 ms + XPTPD_INFO("Start TS Read"); + ts_good = + getTxTimestamp(sync, sync_timestamp, + sync_timestamp_counter_value, + false); + XPTPD_INFO("Done TS Read"); + while (ts_good != 0 && iter-- != 0) { + timer->sleep(req); + if (ts_good != -72 && iter < 1) + fprintf(stderr, + "Error (TX) timestamping Sync (Retrying), error=%d\n", + ts_good); + ts_good = + getTxTimestamp(sync, sync_timestamp, + sync_timestamp_counter_value, + iter == 0); + req *= 2; + } + putTxLock(); + + if (ts_good != 0) { + char msg + [HWTIMESTAMPER_EXTENDED_MESSAGE_SIZE]; + getExtendedError(msg); + fprintf(stderr, + "Error (TX) timestamping Sync, error=%d\n%s", + ts_good, msg); + //_exit(-1); + } + + if (ts_good == 0) { + XPTPD_INFO("Successful Sync timestamp"); + XPTPD_INFO("Seconds: %u", + sync_timestamp.seconds_ls); + XPTPD_INFO("Nanoseconds: %u", + sync_timestamp.nanoseconds); + } else { + XPTPD_INFO + ("*** Unsuccessful Sync timestamp"); + } + + PTPMessageFollowUp *follow_up; + if (ts_good == 0) { + follow_up = + new PTPMessageFollowUp(this); + PortIdentity dest_id; + getPortIdentity(dest_id); + follow_up->setPortIdentity(&dest_id); + follow_up->setSequenceId(sync->getSequenceId()); + follow_up->setPreciseOriginTimestamp(sync_timestamp); + follow_up->sendPort(this, NULL); + delete follow_up; + } else { + // Re-add the timer, since we failed re-send sooner? + //clock->addEventTimer( this, SYNC_INTERVAL_TIMEOUT_EXPIRES, (unsigned long long) (pow(2,getSyncInterval())*1000000000.0)); + } + delete sync; + } + } + clock->addEventTimer(this, SYNC_INTERVAL_TIMEOUT_EXPIRES, + (unsigned long + long)(pow((double)2, + getSyncInterval()) * + 1000000000.0)); + break; + case ANNOUNCE_INTERVAL_TIMEOUT_EXPIRES: + if (asCapable) { + // Send an announce message + PTPMessageAnnounce *annc = new PTPMessageAnnounce(this); + PortIdentity dest_id; + PortIdentity gmId; + ClockIdentity clock_id = clock->getClockIdentity(); + gmId.setClockIdentity(clock_id); + clock->setGrandmasterPortIdentity(gmId); + getPortIdentity(dest_id); + annc->setPortIdentity(&dest_id); + annc->sendPort(this, NULL); + delete annc; + } + clock->addEventTimer(this, ANNOUNCE_INTERVAL_TIMEOUT_EXPIRES, + (unsigned)(pow + ((double)2, + getAnnounceInterval()) * + 1000000000.0)); + break; + case FAULT_DETECTED: + XPTPD_INFO("Received FAULT_DETECTED event"); + break; + case PDELAY_DEFERRED_PROCESSING: + pdelay_rx_lock->lock(); + if (last_pdelay_resp_fwup == NULL) { + fprintf(stderr, "PDelay Response Followup is NULL!\n"); + abort(); + } + last_pdelay_resp_fwup->processMessage(this); + if (last_pdelay_resp_fwup->garbage()) { + delete last_pdelay_resp_fwup; + this->setLastPDelayRespFollowUp(NULL); + } + pdelay_rx_lock->unlock(); + break; + case PDELAY_RESP_RECEIPT_TIMEOUT_EXPIRES: + setAsCapable(false); + break; + default: + XPTPD_INFO + ("Unhandled event type in IEEE1588Port::processEvent(), %d", + e); + break; + } + return; +} + +PTPMessageAnnounce *IEEE1588Port::calculateERBest(void) +{ + if (qualified_announce.empty()) { + return NULL; + } + if (qualified_announce.size() == 1) { + return qualified_announce.front(); + } + + AnnounceList_t::iterator iter_l = qualified_announce.begin(); + PTPMessageAnnounce *best = *iter_l; + ++iter_l; + while (iter_l != qualified_announce.end()) { + if ((*iter_l)->isBetterThan(best)) + best = *iter_l; + iter_l = qualified_announce.erase(iter_l); + } + + return best; +} + +void IEEE1588Port::recoverPort(void) +{ + return; +} + +IEEE1588Clock *IEEE1588Port::getClock(void) +{ + return clock; +} + +void IEEE1588Port::getDeviceTime(Timestamp & system_time, + Timestamp & device_time, + uint32_t & local_clock, + uint32_t & nominal_clock_rate) +{ + if (_hw_timestamper) { + _hw_timestamper->HWTimestamper_gettime(&system_time, + &device_time, + &local_clock, + &nominal_clock_rate); + } else { + device_time = system_time = clock->getSystemTime(); + local_clock = nominal_clock_rate = 0; + } + return; +} + +void IEEE1588Port::recommendState(PortState state, bool changed_master) +{ + switch (state) { + case PTP_MASTER: + if (port_state != PTP_MASTER) { + port_state = PTP_MASTER; + // Start announce receipt timeout timer + // Start sync receipt timeout timer + clock->addEventTimer(this, + ANNOUNCE_INTERVAL_TIMEOUT_EXPIRES, + 8000000); + clock->addEventTimer(this, + SYNC_INTERVAL_TIMEOUT_EXPIRES, + 8000000); + fprintf(stderr, "Switching to Master\n"); + } else { + port_state = PTP_MASTER; + } + break; + case PTP_SLAVE: + // Stop sending announce messages + // Stop sending sync messages + if (port_state != PTP_SLAVE) { + port_state = PTP_SLAVE; + clock->deleteEventTimer(this, + ANNOUNCE_INTERVAL_TIMEOUT_EXPIRES); + clock->deleteEventTimer(this, + SYNC_INTERVAL_TIMEOUT_EXPIRES); + clock->addEventTimer(this, SYNC_RECEIPT_TIMEOUT_EXPIRES, + (SYNC_RECEIPT_TIMEOUT_MULTIPLIER * + (unsigned long + long)(pow((double)2, + getSyncInterval()) * + 1000000000.0))); + clock->addEventTimer(this, + ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES, + (ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER + * + (unsigned long + long)(pow((double)2, + getAnnounceInterval()) + * 1000000000.0))); + fprintf(stderr, "Switching to Slave\n"); + } else { + if (changed_master) { + port_state = PTP_SLAVE; + } else { + port_state = PTP_SLAVE; + } + } + break; + default: + XPTPD_INFO + ("Invalid state change requested by call to 1588Port::recommendState()"); + break; + } + return; +} + +#define FIR_SAMPLE_TIME 1 // Seconds + +int IEEE1588Port::calcMasterLocalClockRateDifference(signed long long offset, + Timestamp sync_time) +{ + long long inter_sync_time; + signed long long offset_delta; + int ppt_offset; + + XPTPD_INFO("Calculated master to local PTP clock rate difference"); + + if (!_master_local_freq_offset_init) { + _prev_sync_time = sync_time; + _prev_master_local_offset = offset; + + _master_local_freq_offset_init = true; + + return 0; + } + + inter_sync_time = + TIMESTAMP_TO_NS(sync_time) - TIMESTAMP_TO_NS(_prev_sync_time); + offset_delta = offset - _prev_master_local_offset; + + if (rate_offset_array == NULL) { + rate_offset_array_size = 4; + ++rate_offset_array_size; + rate_offset_array = new int32_t[rate_offset_array_size]; + rate_offset_count = 0; + rate_offset_index = 0; + } + //fprintf( stderr, "Calculated master to local PTP clock rate difference, offset=%lld,sync_time = %lld\n", offset_delta, inter_sync_time ); + + if (inter_sync_time != 0) { + //fprintf( stderr, "Offset Delta: %lld, IST: %llu(%llu,%llu)\n", offset_delta, inter_sync_time, TIMESTAMP_TO_NS(sync_time),TIMESTAMP_TO_NS(_prev_sync_time) ); + ppt_offset = + int (((offset_delta * 1000000000000LL) / inter_sync_time)); + } else { + ppt_offset = 0; + } + + _prev_sync_time = sync_time; + _prev_master_local_offset = offset; + + return ppt_offset; +} + +int IEEE1588Port::calcLocalSystemClockRateDifference(signed long long offset, + Timestamp system_time) +{ + unsigned long long inter_system_time; + signed long long offset_delta; + int ppt_offset; + + XPTPD_INFO("Calculated local to system clock rate difference"); + + if (!_local_system_freq_offset_init) { + _prev_system_time = system_time; + _prev_local_system_offset = offset; + + _local_system_freq_offset_init = true; + + return 0; + } + + inter_system_time = + TIMESTAMP_TO_NS(system_time) - TIMESTAMP_TO_NS(_prev_system_time); + offset_delta = offset - _prev_local_system_offset; + + if (inter_system_time != 0) { + ppt_offset = + int (((offset_delta * 1000000000000LL) / + (int64_t) inter_system_time)); + XPTPD_INFO + ("IEEE1588Port::calcLocalSystemClockRateDifference() offset delta: %Ld", + offset_delta); + } else { + ppt_offset = 0; + offset_delta = 0; + } + + if (inter_system_time != 0) { + XPTPD_INFO("Calculation Step: %Ld", + ((offset_delta * 1000000000000LL) / + (int64_t) inter_system_time)); + } + XPTPD_INFO + ("IEEE1588Port::calcLocalSystemClockRateDifference() offset: %Ld", + offset); + XPTPD_INFO + ("IEEE1588Port::calcLocalSystemClockRateDifference() prev offset: %ld", + _prev_local_system_offset); + XPTPD_INFO + ("IEEE1588Port::calcLocalSystemClockRateDifference() system time: %u,%u", + system_time.seconds_ls, system_time.nanoseconds); + XPTPD_INFO + ("IEEE1588Port::calcLocalSystemClockRateDifference() prev system time: %u,%u", + _prev_system_time.seconds_ls, _prev_system_time.nanoseconds); + XPTPD_INFO + ("IEEE1588Port::calcLocalSystemClockRateDifference() inter-system time: %Lu", + inter_system_time); + XPTPD_INFO("IEEE1588Port::calcLocalSystemClockRateDifference() PPT: %d", + ppt_offset); + + _prev_system_time = system_time; + _prev_local_system_offset = offset; + + return ppt_offset; +} + +void IEEE1588Port::mapSocketAddr(PortIdentity * destIdentity, + LinkLayerAddress * remote) +{ + *remote = identity_map[*destIdentity]; + return; +} + +void IEEE1588Port::addSockAddrMap(PortIdentity * destIdentity, + LinkLayerAddress * remote) +{ + identity_map[*destIdentity] = *remote; + return; +} + +int IEEE1588Port::getTxTimestamp(PTPMessageCommon * msg, Timestamp & timestamp, + unsigned &counter_value, bool last) +{ + PortIdentity identity; + msg->getPortIdentity(&identity); + return getTxTimestamp(&identity, msg->getSequenceId(), timestamp, + counter_value, last); +} + +int IEEE1588Port::getRxTimestamp(PTPMessageCommon * msg, Timestamp & timestamp, + unsigned &counter_value, bool last) +{ + PortIdentity identity; + msg->getPortIdentity(&identity); + return getRxTimestamp(&identity, msg->getSequenceId(), timestamp, + counter_value, last); +} + +int IEEE1588Port::getTxTimestamp(PortIdentity * sourcePortIdentity, + uint16_t sequenceId, Timestamp & timestamp, + unsigned &counter_value, bool last) +{ + if (_hw_timestamper) { + return + _hw_timestamper->HWTimestamper_txtimestamp + (sourcePortIdentity, sequenceId, timestamp, counter_value, + last); + } + timestamp = clock->getSystemTime(); + return true; +} + +int IEEE1588Port::getRxTimestamp(PortIdentity * sourcePortIdentity, + uint16_t sequenceId, Timestamp & timestamp, + unsigned &counter_value, bool last) +{ + if (_hw_timestamper) { + return + _hw_timestamper->HWTimestamper_rxtimestamp + (sourcePortIdentity, sequenceId, timestamp, counter_value, + last); + } + timestamp = clock->getSystemTime(); + return true; +} diff --git a/daemons/gptp/common/ptp_message.cpp b/daemons/gptp/common/ptp_message.cpp new file mode 100644 index 0000000..86ee0ce --- /dev/null +++ b/daemons/gptp/common/ptp_message.cpp @@ -0,0 +1,1718 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +PTPMessageCommon::PTPMessageCommon(IEEE1588Port * port) +{ + // Fill in fields using port/clock dataset as a template + versionPTP = GPTP_VERSION; + versionNetwork = PTP_NETWORK_VERSION; + domainNumber = port->getClock()->getDomain(); + // Set flags as necessary + memset(flags, 0, PTP_FLAGS_LENGTH); + correctionField = 0; + _gc = false; + sourcePortIdentity = new PortIdentity(); + + return; +} + +// Determine whether the message was sent by given communication technology, uuid, and port id fields +bool PTPMessageCommon::isSenderEqual(PortIdentity portIdentity) +{ + return portIdentity == *sourcePortIdentity; +} + +PTPMessageCommon *buildPTPMessage(char *buf, int size, + LinkLayerAddress * remote, + IEEE1588Port * port) +{ + OSTimer *timer = port->getTimerFactory()->createTimer(); + PTPMessageCommon *msg = NULL; + MessageType messageType; + unsigned char tspec_msg_t = 0; + + uint16_t sequenceId; + PortIdentity *sourcePortIdentity; + Timestamp timestamp(0, 0, 0); + unsigned counter_value = 0; + +#if PTP_DEBUG + { + int i; + XPTPD_INFO("Packet Dump:\n"); + for (i = 0; i < size; ++i) { + fprintf(stderr, "%hhx\t", buf[i]); + if (i % 8 == 7) + fprintf(stderr, "\n"); + } + if (i % 8 != 0) + fprintf(stderr, "\n"); + } +#endif + + memcpy(&tspec_msg_t, + buf + PTP_COMMON_HDR_TRANSSPEC_MSGTYPE(PTP_COMMON_HDR_OFFSET), + sizeof(tspec_msg_t)); + messageType = (MessageType) (tspec_msg_t & 0xF); + + sourcePortIdentity = new PortIdentity((uint8_t *) (buf + + PTP_COMMON_HDR_SOURCE_CLOCK_ID + (PTP_COMMON_HDR_OFFSET)), + (uint16_t *) (buf + + PTP_COMMON_HDR_SOURCE_PORT_ID + (PTP_COMMON_HDR_OFFSET))); + + memcpy(&(sequenceId), + buf + PTP_COMMON_HDR_SEQUENCE_ID(PTP_COMMON_HDR_OFFSET), + sizeof(sequenceId)); + sequenceId = PLAT_ntohs(sequenceId); + + //fprintf( stderr, "Captured Sequence Id: %u\n", sequenceId ); + XPTPD_INFO("Captured Sequence Id: %u", sequenceId); + + if (!(messageType >> 3)) { + int iter = 2; + long req = 1000; // = 1 ms + int ts_good = + port->getRxTimestamp(sourcePortIdentity, sequenceId, + timestamp, counter_value, false); + while (ts_good != 0 && iter-- != 0) { + // Waits at least 1 time slice regardless of size of 'req' + timer->sleep(req); + if (ts_good != 72) + fprintf(stderr, + "Error (RX) timestamping RX event packet (Retrying), error=%d\n", + ts_good); + ts_good = + port->getRxTimestamp(sourcePortIdentity, sequenceId, + timestamp, counter_value, + iter == 0); + req *= 2; + } + if (ts_good != 0) { + char msg[HWTIMESTAMPER_EXTENDED_MESSAGE_SIZE]; + port->getExtendedError(msg); + XPTPD_ERROR + ("*** Received an event packet but cannot retrieve timestamp, discarding. messageType=%u,error=%d\n%s", + messageType, ts_good, msg); + //_exit(-1); + return NULL; + } + + else { + XPTPD_INFO("Timestamping event packet"); + } + + } + + switch (messageType) { + case SYNC_MESSAGE: + + //fprintf( stderr, "*** Received Sync message\n" ); + //printf( "Sync RX timestamp = %hu,%u,%u\n", timestamp.seconds_ms, timestamp.seconds_ls, timestamp.nanoseconds ); + XPTPD_INFO("*** Received Sync message"); + + // Be sure buffer is the correction size + if (size < PTP_COMMON_HDR_LENGTH + PTP_SYNC_LENGTH) { + goto done; + } + { + PTPMessageSync *sync_msg = new PTPMessageSync(); + sync_msg->messageType = messageType; + // Copy in v2 sync specific fields + memcpy(&(sync_msg->originTimestamp.seconds_ms), + buf + PTP_SYNC_SEC_MS(PTP_SYNC_OFFSET), + sizeof(sync_msg->originTimestamp.seconds_ms)); + memcpy(&(sync_msg->originTimestamp.seconds_ls), + buf + PTP_SYNC_SEC_LS(PTP_SYNC_OFFSET), + sizeof(sync_msg->originTimestamp.seconds_ls)); + memcpy(&(sync_msg->originTimestamp.nanoseconds), + buf + PTP_SYNC_NSEC(PTP_SYNC_OFFSET), + sizeof(sync_msg->originTimestamp.nanoseconds)); + msg = sync_msg; + } + break; + case FOLLOWUP_MESSAGE: + + XPTPD_INFO("*** Received Follow Up message"); + + // Be sure buffer is the correction size + if (size < PTP_COMMON_HDR_LENGTH + PTP_FOLLOWUP_LENGTH) { + goto done; + } + { + PTPMessageFollowUp *followup_msg = + new PTPMessageFollowUp(); + followup_msg->messageType = messageType; + // Copy in v2 sync specific fields + memcpy(& + (followup_msg-> + preciseOriginTimestamp.seconds_ms), + buf + PTP_FOLLOWUP_SEC_MS(PTP_FOLLOWUP_OFFSET), + sizeof(followup_msg-> + preciseOriginTimestamp.seconds_ms)); + memcpy(& + (followup_msg-> + preciseOriginTimestamp.seconds_ls), + buf + PTP_FOLLOWUP_SEC_LS(PTP_FOLLOWUP_OFFSET), + sizeof(followup_msg-> + preciseOriginTimestamp.seconds_ls)); + memcpy(& + (followup_msg-> + preciseOriginTimestamp.nanoseconds), + buf + PTP_FOLLOWUP_NSEC(PTP_FOLLOWUP_OFFSET), + sizeof(followup_msg-> + preciseOriginTimestamp.nanoseconds)); + + followup_msg->preciseOriginTimestamp.seconds_ms = + PLAT_ntohs(followup_msg-> + preciseOriginTimestamp.seconds_ms); + followup_msg->preciseOriginTimestamp.seconds_ls = + PLAT_ntohl(followup_msg-> + preciseOriginTimestamp.seconds_ls); + followup_msg->preciseOriginTimestamp.nanoseconds = + PLAT_ntohl(followup_msg-> + preciseOriginTimestamp.nanoseconds); + + msg = followup_msg; + } + break; + case PATH_DELAY_REQ_MESSAGE: + + XPTPD_INFO("*** Received PDelay Request message"); + + // Be sure buffer is the correction size + if (size < PTP_COMMON_HDR_LENGTH + PTP_PDELAY_REQ_LENGTH + && /* For Broadcom compatibility */ size != 46) { + goto done; + } + { + PTPMessagePathDelayReq *pdelay_req_msg = + new PTPMessagePathDelayReq(); + pdelay_req_msg->messageType = messageType; + +#if 0 + // The origin timestamp for PDelay Request packets has been eliminated since it is unused + // Copy in v2 PDelay Request specific fields + memcpy(&(pdelay_req_msg->originTimestamp.seconds_ms), + buf + + PTP_PDELAY_REQ_SEC_MS(PTP_PDELAY_REQ_OFFSET), + sizeof(pdelay_req_msg-> + originTimestamp.seconds_ms)); + memcpy(&(pdelay_req_msg->originTimestamp.seconds_ls), + buf + + PTP_PDELAY_REQ_SEC_LS(PTP_PDELAY_REQ_OFFSET), + sizeof(pdelay_req_msg-> + originTimestamp.seconds_ls)); + memcpy(&(pdelay_req_msg->originTimestamp.nanoseconds), + buf + PTP_PDELAY_REQ_NSEC(PTP_PDELAY_REQ_OFFSET), + sizeof(pdelay_req_msg-> + originTimestamp.nanoseconds)); + + pdelay_req_msg->originTimestamp.seconds_ms = + PLAT_ntohs(pdelay_req_msg-> + originTimestamp.seconds_ms); + pdelay_req_msg->originTimestamp.seconds_ls = + PLAT_ntohl(pdelay_req_msg-> + originTimestamp.seconds_ls); + pdelay_req_msg->originTimestamp.nanoseconds = + PLAT_ntohl(pdelay_req_msg-> + originTimestamp.nanoseconds); +#endif + + msg = pdelay_req_msg; + } + break; + case PATH_DELAY_RESP_MESSAGE: + + XPTPD_INFO("*** Received PDelay Response message, %u, %u, %u", + timestamp.seconds_ls, timestamp.nanoseconds, + sequenceId); + + // Be sure buffer is the correction size + if (size < PTP_COMMON_HDR_LENGTH + PTP_PDELAY_RESP_LENGTH) { + goto done; + } + { + PTPMessagePathDelayResp *pdelay_resp_msg = + new PTPMessagePathDelayResp(); + pdelay_resp_msg->messageType = messageType; + // Copy in v2 PDelay Response specific fields + pdelay_resp_msg->requestingPortIdentity = + new PortIdentity((uint8_t *) buf + + PTP_PDELAY_RESP_REQ_CLOCK_ID + (PTP_PDELAY_RESP_OFFSET), + (uint16_t *) (buf + + PTP_PDELAY_RESP_REQ_PORT_ID + (PTP_PDELAY_RESP_OFFSET))); + +#ifdef DEBUG + for (int n = 0; n < PTP_CLOCK_IDENTITY_LENGTH; ++n) { // MMM + fprintf(stderr, "%c", + pdelay_resp_msg-> + requestingPortIdentity.clockIdentity + [n]); + } +#endif + + memcpy(& + (pdelay_resp_msg-> + requestReceiptTimestamp.seconds_ms), + buf + + PTP_PDELAY_RESP_SEC_MS(PTP_PDELAY_RESP_OFFSET), + sizeof(pdelay_resp_msg-> + requestReceiptTimestamp.seconds_ms)); + memcpy(& + (pdelay_resp_msg-> + requestReceiptTimestamp.seconds_ls), + buf + + PTP_PDELAY_RESP_SEC_LS(PTP_PDELAY_RESP_OFFSET), + sizeof(pdelay_resp_msg-> + requestReceiptTimestamp.seconds_ls)); + memcpy(& + (pdelay_resp_msg-> + requestReceiptTimestamp.nanoseconds), + buf + + PTP_PDELAY_RESP_NSEC(PTP_PDELAY_RESP_OFFSET), + sizeof(pdelay_resp_msg-> + requestReceiptTimestamp.nanoseconds)); + + pdelay_resp_msg->requestReceiptTimestamp.seconds_ms = + PLAT_ntohs(pdelay_resp_msg->requestReceiptTimestamp.seconds_ms); + pdelay_resp_msg->requestReceiptTimestamp.seconds_ls = + PLAT_ntohl(pdelay_resp_msg->requestReceiptTimestamp.seconds_ls); + pdelay_resp_msg->requestReceiptTimestamp.nanoseconds = + PLAT_ntohl(pdelay_resp_msg->requestReceiptTimestamp.nanoseconds); + + msg = pdelay_resp_msg; + } + break; + case PATH_DELAY_FOLLOWUP_MESSAGE: + + XPTPD_INFO("*** Received PDelay Response FollowUp message"); + + // Be sure buffer is the correction size +// if( size < PTP_COMMON_HDR_LENGTH + PTP_PDELAY_FOLLOWUP_LENGTH ) { +// goto done; +// } + { + PTPMessagePathDelayRespFollowUp *pdelay_resp_fwup_msg = + new PTPMessagePathDelayRespFollowUp(); + pdelay_resp_fwup_msg->messageType = messageType; + // Copy in v2 PDelay Response specific fields + pdelay_resp_fwup_msg->requestingPortIdentity = + new PortIdentity((uint8_t *) buf + + PTP_PDELAY_FOLLOWUP_REQ_CLOCK_ID + (PTP_PDELAY_RESP_OFFSET), + (uint16_t *) (buf + + PTP_PDELAY_FOLLOWUP_REQ_PORT_ID + (PTP_PDELAY_FOLLOWUP_OFFSET))); + + memcpy(& + (pdelay_resp_fwup_msg-> + responseOriginTimestamp.seconds_ms), + buf + + PTP_PDELAY_FOLLOWUP_SEC_MS + (PTP_PDELAY_FOLLOWUP_OFFSET), + sizeof + (pdelay_resp_fwup_msg->responseOriginTimestamp. + seconds_ms)); + memcpy(& + (pdelay_resp_fwup_msg-> + responseOriginTimestamp.seconds_ls), + buf + + PTP_PDELAY_FOLLOWUP_SEC_LS + (PTP_PDELAY_FOLLOWUP_OFFSET), + sizeof + (pdelay_resp_fwup_msg->responseOriginTimestamp. + seconds_ls)); + memcpy(& + (pdelay_resp_fwup_msg-> + responseOriginTimestamp.nanoseconds), + buf + + PTP_PDELAY_FOLLOWUP_NSEC + (PTP_PDELAY_FOLLOWUP_OFFSET), + sizeof + (pdelay_resp_fwup_msg->responseOriginTimestamp. + nanoseconds)); + + pdelay_resp_fwup_msg-> + responseOriginTimestamp.seconds_ms = + PLAT_ntohs + (pdelay_resp_fwup_msg->responseOriginTimestamp. + seconds_ms); + pdelay_resp_fwup_msg-> + responseOriginTimestamp.seconds_ls = + PLAT_ntohl + (pdelay_resp_fwup_msg->responseOriginTimestamp. + seconds_ls); + pdelay_resp_fwup_msg-> + responseOriginTimestamp.nanoseconds = + PLAT_ntohl + (pdelay_resp_fwup_msg->responseOriginTimestamp. + nanoseconds); + + msg = pdelay_resp_fwup_msg; + } + break; + case ANNOUNCE_MESSAGE: + { + PTPMessageAnnounce *annc = new PTPMessageAnnounce(); + annc->messageType = messageType; + + memcpy(&(annc->currentUtcOffset), + buf + + PTP_ANNOUNCE_CURRENT_UTC_OFFSET + (PTP_ANNOUNCE_OFFSET), + sizeof(annc->currentUtcOffset)); + annc->currentUtcOffset = + PLAT_ntohs(annc->currentUtcOffset); + memcpy(&(annc->grandmasterPriority1), + buf + + PTP_ANNOUNCE_GRANDMASTER_PRIORITY1 + (PTP_ANNOUNCE_OFFSET), + sizeof(annc->grandmasterPriority1)); + memcpy(annc->clockQuality, + buf + + PTP_ANNOUNCE_GRANDMASTER_CLOCK_QUALITY + (PTP_ANNOUNCE_OFFSET), + sizeof(annc->clockQuality)); + annc->clockQuality->offsetScaledLogVariance = + PLAT_ntohs(annc-> + clockQuality->offsetScaledLogVariance); + memcpy(&(annc->grandmasterPriority2), + buf + + PTP_ANNOUNCE_GRANDMASTER_PRIORITY2 + (PTP_ANNOUNCE_OFFSET), + sizeof(annc->grandmasterPriority2)); + memcpy(&(annc->grandmasterIdentity), + buf + + PTP_ANNOUNCE_GRANDMASTER_IDENTITY + (PTP_ANNOUNCE_OFFSET), + PTP_CLOCK_IDENTITY_LENGTH); + memcpy(&(annc->stepsRemoved), + buf + + PTP_ANNOUNCE_STEPS_REMOVED(PTP_ANNOUNCE_OFFSET), + sizeof(annc->stepsRemoved)); + annc->stepsRemoved = PLAT_ntohs(annc->stepsRemoved); + memcpy(&(annc->timeSource), + buf + + PTP_ANNOUNCE_TIME_SOURCE(PTP_ANNOUNCE_OFFSET), + sizeof(annc->timeSource)); + + msg = annc; + } + break; + default: + + XPTPD_ERROR("Received unsupported message type, %d", + (int)messageType); + + goto done; + } + + msg->_gc = false; + + // Copy in common header fields + memcpy(&(msg->versionPTP), + buf + PTP_COMMON_HDR_PTP_VERSION(PTP_COMMON_HDR_OFFSET), + sizeof(msg->versionPTP)); + memcpy(&(msg->messageLength), + buf + PTP_COMMON_HDR_MSG_LENGTH(PTP_COMMON_HDR_OFFSET), + sizeof(msg->messageLength)); + msg->messageLength = PLAT_ntohs(msg->messageLength); + memcpy(&(msg->domainNumber), + buf + PTP_COMMON_HDR_DOMAIN_NUMBER(PTP_COMMON_HDR_OFFSET), + sizeof(msg->domainNumber)); + memcpy(&(msg->flags), buf + PTP_COMMON_HDR_FLAGS(PTP_COMMON_HDR_OFFSET), + PTP_FLAGS_LENGTH); + memcpy(&(msg->correctionField), + buf + PTP_COMMON_HDR_CORRECTION(PTP_COMMON_HDR_OFFSET), + sizeof(msg->correctionField)); + msg->correctionField = bswap_64(msg->correctionField); // Assume LE machine + msg->sourcePortIdentity = sourcePortIdentity; + msg->sequenceId = sequenceId; + memcpy(&(msg->control), + buf + PTP_COMMON_HDR_CONTROL(PTP_COMMON_HDR_OFFSET), + sizeof(msg->control)); + memcpy(&(msg->logMeanMessageInterval), + buf + PTP_COMMON_HDR_LOG_MSG_INTRVL(PTP_COMMON_HDR_OFFSET), + sizeof(msg->logMeanMessageInterval)); + + port->addSockAddrMap(msg->sourcePortIdentity, remote); + + msg->_timestamp = timestamp; + msg->_timestamp_counter_value = counter_value; + + done: + return msg; +} + +void PTPMessageCommon::processMessage(IEEE1588Port * port) +{ + _gc = true; + return; +} + +void PTPMessageCommon::buildCommonHeader(uint8_t * buf) +{ + unsigned char tspec_msg_t; + tspec_msg_t = messageType | 0x10; + //tspec_msg_t = messageType; + long long correctionField_BE = bswap_64(correctionField); // Assume LE machine + uint16_t messageLength_NO = PLAT_htons(messageLength); + + memcpy(buf + PTP_COMMON_HDR_TRANSSPEC_MSGTYPE(PTP_COMMON_HDR_OFFSET), + &tspec_msg_t, sizeof(tspec_msg_t)); + memcpy(buf + PTP_COMMON_HDR_PTP_VERSION(PTP_COMMON_HDR_OFFSET), + &versionPTP, sizeof(versionPTP)); + memcpy(buf + PTP_COMMON_HDR_MSG_LENGTH(PTP_COMMON_HDR_OFFSET), + &messageLength_NO, sizeof(messageLength_NO)); + memcpy(buf + PTP_COMMON_HDR_DOMAIN_NUMBER(PTP_COMMON_HDR_OFFSET), + &domainNumber, sizeof(domainNumber)); + memcpy(buf + PTP_COMMON_HDR_FLAGS(PTP_COMMON_HDR_OFFSET), &flags, + PTP_FLAGS_LENGTH); + memcpy(buf + PTP_COMMON_HDR_CORRECTION(PTP_COMMON_HDR_OFFSET), + &correctionField_BE, sizeof(correctionField)); + + sourcePortIdentity->getClockIdentityString((char *)buf + + PTP_COMMON_HDR_SOURCE_CLOCK_ID + (PTP_COMMON_HDR_OFFSET)); + sourcePortIdentity->getPortNumberNO((uint16_t *) (buf + + PTP_COMMON_HDR_SOURCE_PORT_ID + (PTP_COMMON_HDR_OFFSET))); + + XPTPD_INFO("Sending Sequence Id: %u", sequenceId); + sequenceId = PLAT_htons(sequenceId); + memcpy(buf + PTP_COMMON_HDR_SEQUENCE_ID(PTP_COMMON_HDR_OFFSET), + &sequenceId, sizeof(sequenceId)); + sequenceId = PLAT_ntohs(sequenceId); + memcpy(buf + PTP_COMMON_HDR_CONTROL(PTP_COMMON_HDR_OFFSET), &control, + sizeof(control)); + memcpy(buf + PTP_COMMON_HDR_LOG_MSG_INTRVL(PTP_COMMON_HDR_OFFSET), + &logMeanMessageInterval, sizeof(logMeanMessageInterval)); + + return; +} + +void PTPMessageCommon::getPortIdentity(PortIdentity * identity) +{ + *identity = *sourcePortIdentity; +} + +void PTPMessageCommon::setPortIdentity(PortIdentity * identity) +{ + *sourcePortIdentity = *identity; +} + +PTPMessageCommon::~PTPMessageCommon(void) +{ + delete sourcePortIdentity; + return; +} + +PTPMessageAnnounce::PTPMessageAnnounce(void) +{ + clockQuality = new ClockQuality(); +} + +PTPMessageAnnounce::~PTPMessageAnnounce(void) +{ + delete clockQuality; +} + +bool PTPMessageAnnounce::isBetterThan(PTPMessageAnnounce * msg) +{ + unsigned char this1[14]; + unsigned char that1[14]; + uint16_t tmp; + + this1[0] = grandmasterPriority1; + that1[0] = msg->getGrandmasterPriority1(); + + this1[1] = clockQuality->cq_class; + that1[1] = msg->getGrandmasterClockQuality()->cq_class; + + this1[2] = clockQuality->clockAccuracy; + that1[2] = msg->getGrandmasterClockQuality()->clockAccuracy; + + tmp = clockQuality->offsetScaledLogVariance; + tmp = PLAT_htons(tmp); + memcpy(this1 + 3, &tmp, sizeof(tmp)); + tmp = msg->getGrandmasterClockQuality()->offsetScaledLogVariance; + tmp = PLAT_htons(tmp); + memcpy(that1 + 3, &tmp, sizeof(tmp)); + + this1[5] = grandmasterPriority2; + that1[5] = msg->getGrandmasterPriority2(); + + this->getGrandmasterIdentity((char *)this1 + 6); + msg->getGrandmasterIdentity((char *)that1 + 6); + +#if 0 + fprintf(stderr, "Us: "); + for (int i = 0; i < 14; ++i) + fprintf(stderr, "%hhx", this1[i]); + fprintf(stderr, "\n"); + fprintf(stderr, "Them: "); + for (int i = 0; i < 14; ++i) + fprintf(stderr, "%hhx", that1[i]); + fprintf(stderr, "\n"); +#endif + + return (memcmp(this1, that1, 14) < 0) ? true : false; +} + + PTPMessageSync::PTPMessageSync(IEEE1588Port * port):PTPMessageCommon(port) +{ + messageType = SYNC_MESSAGE; // This is an event message + sequenceId = port->getNextSyncSequenceId(); + control = SYNC; + + flags[PTP_ASSIST_BYTE] |= (0x1 << PTP_ASSIST_BIT); + + originTimestamp = port->getClock()->getTime(); + + logMeanMessageInterval = port->getSyncInterval(); + return; +} + +void PTPMessageSync::sendPort(IEEE1588Port * port, PortIdentity * destIdentity) +{ + uint8_t buf_t[256]; + uint8_t *buf_ptr = buf_t + port->getPayloadOffset(); + unsigned char tspec_msg_t = 0x0; + Timestamp originTimestamp_BE; + memset(buf_t, 0, 256); + // Create packet in buf + // Copy in common header + messageLength = PTP_COMMON_HDR_LENGTH + PTP_SYNC_LENGTH; + tspec_msg_t |= messageType & 0xF; + buildCommonHeader(buf_ptr); + // Get timestamp + originTimestamp = port->getClock()->getTime(); + originTimestamp_BE.seconds_ms = PLAT_htons(originTimestamp.seconds_ms); + originTimestamp_BE.seconds_ls = PLAT_htonl(originTimestamp.seconds_ls); + originTimestamp_BE.nanoseconds = + PLAT_htonl(originTimestamp.nanoseconds); + // Copy in v2 sync specific fields + memcpy(buf_ptr + PTP_SYNC_SEC_MS(PTP_SYNC_OFFSET), + &(originTimestamp_BE.seconds_ms), + sizeof(originTimestamp.seconds_ms)); + memcpy(buf_ptr + PTP_SYNC_SEC_LS(PTP_SYNC_OFFSET), + &(originTimestamp_BE.seconds_ls), + sizeof(originTimestamp.seconds_ls)); + memcpy(buf_ptr + PTP_SYNC_NSEC(PTP_SYNC_OFFSET), + &(originTimestamp_BE.nanoseconds), + sizeof(originTimestamp.nanoseconds)); + + port->sendEventPort(buf_t, messageLength, MCAST_OTHER, destIdentity); + + return; +} + + PTPMessageAnnounce::PTPMessageAnnounce(IEEE1588Port * port):PTPMessageCommon + (port) +{ + messageType = ANNOUNCE_MESSAGE; // This is an event message + sequenceId = port->getNextAnnounceSequenceId(); + ClockIdentity id; + control = MESSAGE_OTHER; + + id = port->getClock()->getClockIdentity(); + tlv.setClockIdentity(&id); + + currentUtcOffset = port->getClock()->getCurrentUtcOffset(); + grandmasterPriority1 = port->getClock()->getPriority1(); + grandmasterPriority2 = port->getClock()->getPriority2(); + clockQuality = new ClockQuality(); + *clockQuality = port->getClock()->getClockQuality(); + stepsRemoved = 0; + timeSource = port->getClock()->getTimeSource(); + ClockIdentity clock_identity; + clock_identity = port->getClock()->getClockIdentity(); + clock_identity.getIdentityString(grandmasterIdentity); + + logMeanMessageInterval = port->getAnnounceInterval(); + return; +} + +void PTPMessageAnnounce::sendPort(IEEE1588Port * port, + PortIdentity * destIdentity) +{ + uint8_t buf_t[256]; + uint8_t *buf_ptr = buf_t + port->getPayloadOffset(); + unsigned char tspec_msg_t = 0x0; + + uint16_t currentUtcOffset_l = PLAT_htons(currentUtcOffset); + uint16_t stepsRemoved_l = PLAT_htons(stepsRemoved); + ClockQuality clockQuality_l = *clockQuality; + clockQuality_l.offsetScaledLogVariance = + PLAT_htons(clockQuality_l.offsetScaledLogVariance); + + memset(buf_t, 0, 256); + // Create packet in buf + // Copy in common header + messageLength = + PTP_COMMON_HDR_LENGTH + PTP_ANNOUNCE_LENGTH + sizeof(tlv); + tspec_msg_t |= messageType & 0xF; + buildCommonHeader(buf_ptr); + memcpy(buf_ptr + PTP_ANNOUNCE_CURRENT_UTC_OFFSET(PTP_ANNOUNCE_OFFSET), + ¤tUtcOffset_l, sizeof(currentUtcOffset)); + memcpy(buf_ptr + + PTP_ANNOUNCE_GRANDMASTER_PRIORITY1(PTP_ANNOUNCE_OFFSET), + &grandmasterPriority1, sizeof(grandmasterPriority1)); + memcpy(buf_ptr + + PTP_ANNOUNCE_GRANDMASTER_CLOCK_QUALITY(PTP_ANNOUNCE_OFFSET), + &clockQuality_l, sizeof(clockQuality)); + memcpy(buf_ptr + + PTP_ANNOUNCE_GRANDMASTER_PRIORITY2(PTP_ANNOUNCE_OFFSET), + &grandmasterPriority2, sizeof(grandmasterPriority2)); + port->getClock()->getGrandmasterIdentity((char *)buf_ptr + + PTP_ANNOUNCE_GRANDMASTER_IDENTITY + (PTP_ANNOUNCE_OFFSET)); + memcpy(buf_ptr + PTP_ANNOUNCE_STEPS_REMOVED(PTP_ANNOUNCE_OFFSET), + &stepsRemoved_l, sizeof(stepsRemoved)); + memcpy(buf_ptr + PTP_ANNOUNCE_TIME_SOURCE(PTP_ANNOUNCE_OFFSET), + &timeSource, sizeof(timeSource)); + tlv.toByteString(buf_ptr + PTP_COMMON_HDR_LENGTH + PTP_ANNOUNCE_LENGTH); + + port->sendGeneralPort(buf_t, messageLength, MCAST_OTHER, destIdentity); + + return; +} + +void PTPMessageAnnounce::processMessage(IEEE1588Port * port) +{ + // Delete announce receipt timeout + port->getClock()->deleteEventTimer(port, + ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES); + + // Add message to the list + port->addQualifiedAnnounce(this); + + port->getClock()->addEventTimer(port, ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES, + (unsigned long long) + (ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER * + (pow + ((double)2, + port->getAnnounceInterval()) * + 1000000000.0))); + port->getClock()->addEventTimer(port, STATE_CHANGE_EVENT, 16000000); +} + +void PTPMessageSync::processMessage(IEEE1588Port * port) +{ + Timestamp system_time; + Timestamp device_time; + int64_t delay; + + signed long long local_system_offset; + signed long long scalar_offset; + + int32_t local_clock_adjustment; + int32_t local_system_freq_offset; + + // Expire any SYNC_RECEIPT timers that exist + port->getClock()->deleteEventTimer(port, SYNC_RECEIPT_TIMEOUT_EXPIRES); + if (port->getPortState() == PTP_INITIALIZING + || port->getPortState() == PTP_DISABLED) { + // Do nothing Sync messages should be ignored when in this state + return; + } + if (port->getPortState() == PTP_FAULTY) { + // According to spec recovery is implementation specific + port->recoverPort(); + return; + } + + XPTPD_INFO("PTP assist flag is not set, FLAGS[0,1] = %u,%u", flags[0], + flags[1]); + +// if( flags[PTP_ASSIST_BYTE] & (0x1<getLastSync(); + if (old_sync != NULL) { + delete old_sync; + } + port->setLastSync(this); + _gc = false; + goto done; + } else { + _gc = true; + } + + // Indicates invalid link delay, wait until link delay had been calculated + if ((delay = port->getLinkDelay()) == 3600000000000) { + printf + ("Got Sync/Follow-Up but Link Delay has not been computed\n"); + goto done; + } +#if 0 + scalar_offset = _timestamp.nanoseconds + + (((unsigned long long)_timestamp.seconds_ms << + sizeof(_timestamp.seconds_ls) * 8) + + _timestamp.seconds_ls) * 1000000000LL; + scalar_offset -= + originTimestamp.nanoseconds + + (((unsigned long long)originTimestamp.seconds_ms << + sizeof(originTimestamp.seconds_ls) * 8) + + originTimestamp.seconds_ls) * 1000000000LL; +#endif + scalar_offset = + TIMESTAMP_TO_NS(_timestamp) - TIMESTAMP_TO_NS(originTimestamp); + scalar_offset -= delay; +#if 0 + scalar_offset -= delay.nanoseconds + + (((unsigned long long)delay.seconds_ms << sizeof(delay.seconds_ls) * + 8) + delay.seconds_ls) * 1000000000LL; +#endif + scalar_offset -= correctionField >> 16; // Fractional nanoseconds are dropped + + // Otherwise synchronize clock with approximate time from Sync message + uint32_t local_clock; + uint32_t nominal_clock_rate; + uint32_t device_sync_time_offset; + +#if 0 + Timestamp crstamp_device_time; + int64_t external_local_offset; + int32_t external_local_freq_offset; +#endif + + port->getDeviceTime(system_time, device_time, local_clock, + nominal_clock_rate); + + // Adjust local_clock to correspond to _timestamp + device_sync_time_offset = + TIMESTAMP_TO_NS(device_time) - TIMESTAMP_TO_NS(_timestamp); + local_clock -= + device_sync_time_offset / (1000000000 / nominal_clock_rate); + + XPTPD_INFO + ("ptp_message::sync::processMessage System time: %u,%u Device Time: %u,%u", + system_time.seconds_ls, system_time.nanoseconds, + device_time.seconds_ls, device_time.nanoseconds); + + local_clock_adjustment = + port->calcMasterLocalClockRateDifference(scalar_offset, _timestamp); + port->setMasterRateOffset(local_clock_adjustment); + + local_system_offset = + TIMESTAMP_TO_NS(system_time) - TIMESTAMP_TO_NS(device_time); +#if 0 + local_system_offset = system_time.nanoseconds + + (((unsigned long long)system_time.seconds_ms << + sizeof(system_time.seconds_ls) * 8) + + system_time.seconds_ls) * 1000000000LL; + local_system_offset -= + device_time.nanoseconds + + (((unsigned long long)device_time.seconds_ms << + sizeof(device_time.seconds_ls) * 8) + + device_time.seconds_ls) * 1000000000LL; +#endif + + local_system_freq_offset = + port->calcLocalSystemClockRateDifference(local_system_offset, + system_time); + + TIMESTAMP_SUB_NS(system_time, + (device_sync_time_offset * + (1000000000000ULL + + local_system_freq_offset)) / 1000000000000ULL); + + //port->getExternalClockRate( crstamp_device_time, external_local_offset, external_local_freq_offset ); + +#if 0 + port->getClock()->setMasterOffset(scalar_offset, _timestamp, + local_clock_adjustment, + local_system_offset, system_time, + local_system_freq_offset, + external_local_offset, + crstamp_device_time, + external_local_freq_offset); +#endif + port->getClock()->setMasterOffset(scalar_offset, _timestamp, + local_clock_adjustment, + local_system_offset, system_time, + local_system_freq_offset, + nominal_clock_rate, local_clock); + //fprintf( stderr, "Master-Local Offset=%d\n", local_clock_adjustment ); + + if (port->doSyntonization()) { + port->adjustClockRate(local_clock_adjustment, + _timestamp_counter_value, originTimestamp, + scalar_offset, false); + } + + done: + // Restart the SYNC_RECEIPT timer + port->getClock()->addEventTimer(port, SYNC_RECEIPT_TIMEOUT_EXPIRES, + (unsigned long long) + (SYNC_RECEIPT_TIMEOUT_MULTIPLIER * + ((double) + pow((double)2, + port->getSyncInterval()) * + 1000000000.0))); + + return; +} + + PTPMessageFollowUp::PTPMessageFollowUp(IEEE1588Port * port):PTPMessageCommon + (port) +{ + messageType = FOLLOWUP_MESSAGE; // This is an event message + control = FOLLOWUP; + + logMeanMessageInterval = port->getSyncInterval(); + + return; +} + +void PTPMessageFollowUp::sendPort(IEEE1588Port * port, + PortIdentity * destIdentity) +{ + uint8_t buf_t[256]; + uint8_t *buf_ptr = buf_t + port->getPayloadOffset(); + unsigned char tspec_msg_t = 0x0; + Timestamp preciseOriginTimestamp_BE; + memset(buf_t, 0, 256); + // Create packet in buf + // Copy in common header + messageLength = + PTP_COMMON_HDR_LENGTH + PTP_FOLLOWUP_LENGTH + sizeof(tlv); + tspec_msg_t |= messageType & 0xF; + buildCommonHeader(buf_ptr); + preciseOriginTimestamp_BE.seconds_ms = + PLAT_htons(preciseOriginTimestamp.seconds_ms); + preciseOriginTimestamp_BE.seconds_ls = + PLAT_htonl(preciseOriginTimestamp.seconds_ls); + preciseOriginTimestamp_BE.nanoseconds = + PLAT_htonl(preciseOriginTimestamp.nanoseconds); + // Copy in v2 sync specific fields + memcpy(buf_ptr + PTP_FOLLOWUP_SEC_MS(PTP_FOLLOWUP_OFFSET), + &(preciseOriginTimestamp_BE.seconds_ms), + sizeof(preciseOriginTimestamp.seconds_ms)); + memcpy(buf_ptr + PTP_FOLLOWUP_SEC_LS(PTP_FOLLOWUP_OFFSET), + &(preciseOriginTimestamp_BE.seconds_ls), + sizeof(preciseOriginTimestamp.seconds_ls)); + memcpy(buf_ptr + PTP_FOLLOWUP_NSEC(PTP_FOLLOWUP_OFFSET), + &(preciseOriginTimestamp_BE.nanoseconds), + sizeof(preciseOriginTimestamp.nanoseconds)); + tlv.toByteString(buf_ptr + PTP_COMMON_HDR_LENGTH + PTP_FOLLOWUP_LENGTH); + + XPTPD_INFO("Follow-Up Time: %u seconds(hi)", + preciseOriginTimestamp.seconds_ms); + XPTPD_INFO("Follow-Up Time: %u seconds", + preciseOriginTimestamp.seconds_ls); + XPTPD_INFO("F-UP Time: %u nanoseconds", + preciseOriginTimestamp.nanoseconds); + XPTPD_INFO("F-UP Time: %x seconds", preciseOriginTimestamp.seconds_ls); + XPTPD_INFO("F-UP Time: %x nanoseconds", + preciseOriginTimestamp.nanoseconds); +#if 0 + XPTPD_INFO("Follow-up Dump:\n"); +#ifdef DEBUG + for (int i = 0; i < messageLength; ++i) { + fprintf(stderr, "%d:%02x ", i, (unsigned char)buf[i]); + } + fprintf(stderr, "\n"); +#endif +#endif + + port->sendGeneralPort(buf_t, messageLength, MCAST_OTHER, destIdentity); + + return; +} + +void PTPMessageFollowUp::processMessage(IEEE1588Port * port) +{ + int64_t delay; + Timestamp sync_arrival; + Timestamp system_time(0, 0, 0); + Timestamp device_time(0, 0, 0); + + signed long long local_system_offset; + signed long long scalar_offset; + + int32_t local_clock_adjustment; + int32_t local_system_freq_offset; + + XPTPD_INFO("Processing a follow-up message"); + + if (port->getPortState() == PTP_INITIALIZING + || port->getPortState() == PTP_DISABLED) { + // Do nothing Sync messages should be ignored when in this state + return; + } + if (port->getPortState() == PTP_FAULTY) { + // According to spec recovery is implementation specific + port->recoverPort(); + return; + } + + PortIdentity sync_id; + PTPMessageSync *sync = port->getLastSync(); + if (sync == NULL) { + + XPTPD_ERROR("Received Follow Up but there is no sync message"); + + return; + } + sync->getPortIdentity(&sync_id); + + if (sync->getSequenceId() != sequenceId + || sync_id != *sourcePortIdentity) { + + XPTPD_ERROR + ("Received Follow Up but cannot find corresponding Sync"); + + goto done; + } + + sync_arrival = sync->getTimestamp(); + + XPTPD_INFO("Sync Arrival: %u seconds", sync_arrival.seconds_ls); + XPTPD_INFO("Sync Arrival: %u nanoseconds", sync_arrival.nanoseconds); + XPTPD_INFO("Sync Depart: %u seconds", + preciseOriginTimestamp.seconds_ls); + XPTPD_INFO("Sync Depart: %u nanoseconds", + preciseOriginTimestamp.nanoseconds); + XPTPD_INFO("Sync Depart: %x seconds", + preciseOriginTimestamp.seconds_ls); + XPTPD_INFO("Sync Depart: %x nanoseconds", + preciseOriginTimestamp.nanoseconds); + + delay = port->getLinkDelay(); + if ((delay = port->getLinkDelay()) == 3600000000000) { + goto done; + } + //fprintf( stderr, "Local Time = %llu,Master Time = %llu\n", TIMESTAMP_TO_NS(sync_arrival)-delay, TIMESTAMP_TO_NS( preciseOriginTimestamp)); + + scalar_offset = + TIMESTAMP_TO_NS(sync_arrival) - + TIMESTAMP_TO_NS(preciseOriginTimestamp); + + //printf( "Followup timestamp: %u,%u,%u\n", preciseOriginTimestamp.seconds_ms, preciseOriginTimestamp.seconds_ls, preciseOriginTimestamp.nanoseconds ); + //printf( "Sync Arrival timestamp: %u,%u,%u\n", sync_arrival.seconds_ms, sync_arrival.seconds_ls, sync_arrival.nanoseconds ); + //printf( "FollowUp Scalar = %Ld\n", scalar_offset ); + + scalar_offset -= delay; + scalar_offset -= correctionField >> 16; // Fractional nanoseconds are dropped + //fprintf( stderr, "Scalar offset = %lld\n", scalar_offset ); + //scalar_offset -= sync->getCorrectionField() >> 16; // Fractional nanoseconds are dropped + //fprintf( stderr, "Correction Field (ns): %lld\n", correctionField >> 16 ); + //fprintf( stderr, "Link Delay (ns): %lld(%llu,%llu)\n", delay, TIMESTAMP_TO_NS( sync_arrival), TIMESTAMP_TO_NS(preciseOriginTimestamp) ); + + XPTPD_INFO("Followup Correction Field: %Ld,%lu", correctionField >> 16, + delay); + XPTPD_INFO("FollowUp Scalar = %lld", scalar_offset); + + // Otherwise synchronize clock with approximate time from Sync message + uint32_t local_clock, nominal_clock_rate; + uint32_t device_sync_time_offset; + +#if 0 + Timestamp crstamp_device_time; + int64_t external_local_offset; + int32_t external_local_freq_offset; +#endif + + port->getDeviceTime(system_time, device_time, local_clock, + nominal_clock_rate); + //fprintf( stderr, "Device Time = %llu,System Time = %llu\n", TIMESTAMP_TO_NS(device_time), TIMESTAMP_TO_NS(system_time)); + + // Adjust local_clock to correspond to sync_arrival + device_sync_time_offset = + TIMESTAMP_TO_NS(device_time) - TIMESTAMP_TO_NS(sync_arrival); + //local_clock -= device_sync_time_offset/(1000000000/nominal_clock_rate); + + XPTPD_INFO + ("ptp_message::FollowUp::processMessage System time: %u,%u Device Time: %u,%u", + system_time.seconds_ls, system_time.nanoseconds, + device_time.seconds_ls, device_time.nanoseconds); + + local_system_offset = + TIMESTAMP_TO_NS(system_time) - TIMESTAMP_TO_NS(device_time); + +#if 0 + local_system_offset = system_time.nanoseconds + + (((unsigned long long)system_time.seconds_ms << + sizeof(system_time.seconds_ls) * 8) + + system_time.seconds_ls) * 1000000000LL; + local_system_offset -= + device_time.nanoseconds + + (((unsigned long long)device_time.seconds_ms << + sizeof(device_time.seconds_ls) * 8) + + device_time.seconds_ls) * 1000000000LL; +#endif + + local_clock_adjustment = + port->calcMasterLocalClockRateDifference(scalar_offset, + sync_arrival); + port->setMasterRateOffset(local_clock_adjustment); + + local_system_freq_offset = + port->calcLocalSystemClockRateDifference(local_system_offset, + system_time); + //fprintf( stderr, "Master-Local Freq Offset = %d\n", local_clock_adjustment ); + //fprintf( stderr, "Local-System Freq Offset = %d\n", local_system_freq_offset ); + + TIMESTAMP_SUB_NS(system_time, + (device_sync_time_offset * + (1000000000000ULL + + local_system_freq_offset)) / 1000000000000ULL); + + //port->getExternalClockRate( crstamp_device_time, external_local_offset, external_local_freq_offset ); + +#if 0 + port->getClock()->setMasterOffset(scalar_offset, sync_arrival, + local_clock_adjustment, + local_system_offset, system_time, + local_system_freq_offset, + external_local_offset, + crstamp_device_time, + external_local_freq_offset); +#endif + port->getClock()->setMasterOffset(scalar_offset, sync_arrival, + local_clock_adjustment, + local_system_offset, system_time, + local_system_freq_offset, + nominal_clock_rate, local_clock); + //fprintf( stderr, "Master-Local Offset=%lld,%d,%llu\n", scalar_offset, local_clock_adjustment, TIMESTAMP_TO_NS(sync_arrival) ); + + if (port->doSyntonization()) { + port->adjustClockRate(local_clock_adjustment, + sync->getTimestampCounterValue(), + preciseOriginTimestamp, scalar_offset, + false); + } + + done: + _gc = true; + delete sync; + port->setLastSync(NULL); + + return; +} + + PTPMessagePathDelayReq::PTPMessagePathDelayReq(IEEE1588Port * port):PTPMessageCommon + (port) +{ + logMeanMessageInterval = 0; + control = MESSAGE_OTHER; + messageType = PATH_DELAY_REQ_MESSAGE; + sequenceId = port->getNextPDelaySequenceId(); + return; +} + +void PTPMessagePathDelayReq::processMessage(IEEE1588Port * port) +{ + OSTimer *timer = port->getTimerFactory()->createTimer(); + + if (port->getPortState() == PTP_INITIALIZING + || port->getPortState() == PTP_DISABLED) { + // Do nothing all messages should be ignored when in this state + return; + } + + if (port->getPortState() == PTP_FAULTY) { + // According to spec recovery is implementation specific + port->recoverPort(); + return; + } + + port->getClock()->deleteEventTimer(port, + PDELAY_RESP_RECEIPT_TIMEOUT_EXPIRES); + port->getClock()->addEventTimer(port, + PDELAY_RESP_RECEIPT_TIMEOUT_EXPIRES, + (unsigned long long) + (PDELAY_RESP_RECEIPT_TIMEOUT_MULTIPLIER + * + (pow + ((double)2, + port->getPDelayInterval()) * + 1000000000.0))); + // Generate and send message + PTPMessagePathDelayResp *resp = new PTPMessagePathDelayResp(port); + PortIdentity resp_id; + port->getPortIdentity(resp_id); + resp->setPortIdentity(&resp_id); + resp->setSequenceId(sequenceId); + + XPTPD_INFO("Process PDelay Request SeqId: %u\t", sequenceId); + //XPTPD_INFO( "Process PDelay Request Port: %u\t", ); + //XPTPD_INFO( "Process PDelay Request Identity: \"" ); + +#ifdef DEBUG + for (int n = 0; n < PTP_CLOCK_IDENTITY_LENGTH; ++n) { + fprintf(stderr, "%c", resp_id.clockIdentity[n]); + } + fprintf(stderr, "\"\n"); +#endif + + PortIdentity requestingPortIdentity_p; + this->getPortIdentity(&requestingPortIdentity_p); + resp->setRequestingPortIdentity(&requestingPortIdentity_p); + resp->setRequestReceiptTimestamp(_timestamp); + port->getTxLock(); + resp->sendPort(port, sourcePortIdentity); + + XPTPD_INFO("Sent path delay response"); + + int ts_good; + int iter = 2; + Timestamp resp_timestamp; + unsigned resp_timestamp_counter_value; + unsigned req = 1000; // = 1 ms + + XPTPD_INFO("Start TS Read"); + ts_good = + port->getTxTimestamp(resp, resp_timestamp, + resp_timestamp_counter_value, false); + + XPTPD_INFO("Done TS Read"); + + while (ts_good != 0 && iter-- != 0) { + timer->sleep(req); + if (ts_good == -72 && iter < 1) + fprintf(stderr, + "Error (TX) timestamping PDelay Response (Retrying-%d), error=%d\n", + iter, ts_good); + ts_good = + port->getTxTimestamp(resp, resp_timestamp, + resp_timestamp_counter_value, + iter == 0); + req *= 2; + } + port->putTxLock(); + + if (ts_good != 0) { + char msg[HWTIMESTAMPER_EXTENDED_MESSAGE_SIZE]; + port->getExtendedError(msg); + fprintf(stderr, + "Error (TX) timestamping PDelay Response, error=%d\n%s", + ts_good, msg); + delete resp; + return; + } + + PTPMessagePathDelayRespFollowUp *resp_fwup = + new PTPMessagePathDelayRespFollowUp(port); + PortIdentity resp_fwup_id; + port->getPortIdentity(resp_fwup_id); + resp_fwup->setPortIdentity(&resp_fwup_id); + resp_fwup->setSequenceId(sequenceId); + resp_fwup->setRequestingPortIdentity(sourcePortIdentity); + resp_fwup->setResponseOriginTimestamp(resp_timestamp); + long long turnaround; + turnaround = + (resp_timestamp.seconds_ls - _timestamp.seconds_ls) * 1000000000LL; + + XPTPD_INFO("Response Depart(sec): %u", resp_timestamp.seconds_ls); + XPTPD_INFO("Request Arrival(sec): %u", _timestamp.seconds_ls); + XPTPD_INFO("#1 Correction Field: %Ld", turnaround); + + turnaround += resp_timestamp.nanoseconds; + + XPTPD_INFO("#2 Correction Field: %Ld", turnaround); + + turnaround -= _timestamp.nanoseconds; + + XPTPD_INFO("#3 Correction Field: %Ld", turnaround); + + //resp_fwup->setCorrectionField( turnaround << 16 ); + resp_fwup->setCorrectionField(0); // MMM + resp_fwup->sendPort(port, sourcePortIdentity); + + XPTPD_INFO("Sent path delay response fwup"); + + delete resp; + delete resp_fwup; + + _gc = true; +} + +void PTPMessagePathDelayReq::sendPort(IEEE1588Port * port, + PortIdentity * destIdentity) +{ + uint8_t buf_t[256]; + uint8_t *buf_ptr = buf_t + port->getPayloadOffset(); + unsigned char tspec_msg_t = 0; + memset(buf_t, 0, 256); + // Create packet in buf + // Copy in common header + messageLength = PTP_COMMON_HDR_LENGTH + PTP_PDELAY_REQ_LENGTH; + tspec_msg_t |= messageType & 0xF; + buildCommonHeader(buf_ptr); + // Get timestamp + //originTimestamp = port->getClock()->getTime(); + // These fields are not used +// memcpy( buf+PTP_PDELAY_REQ_SEC_MS(PTP_PDELAY_REQ_OFFSET), &(originTimestamp.seconds_ms), sizeof(originTimestamp.seconds_ms)); +// memcpy( buf+PTP_PDELAY_REQ_SEC_LS(PTP_PDELAY_REQ_OFFSET), &(originTimestamp.seconds_ls), sizeof(originTimestamp.seconds_ls)); +// memcpy( buf+PTP_PDELAY_REQ_NSEC(PTP_PDELAY_REQ_OFFSET), &(originTimestamp.nanoseconds), sizeof(originTimestamp.nanoseconds)); +// memcpy( buf+PTP_PDELAY_REQ_TS_OFFSET(PTP_PDELAY_REQ_OFFSET), &(originTimestamp.timescaleOffset), sizeof(originTimestamp.timescaleOffset)); + + port->sendEventPort(buf_t, messageLength, MCAST_PDELAY, destIdentity); + return; +} + + PTPMessagePathDelayResp::PTPMessagePathDelayResp(IEEE1588Port * port): +PTPMessageCommon(port) +{ + logMeanMessageInterval = 0; + control = MESSAGE_OTHER; + messageType = PATH_DELAY_RESP_MESSAGE; + versionPTP = GPTP_VERSION; + requestingPortIdentity = new PortIdentity(); + + flags[PTP_ASSIST_BYTE] |= (0x1 << PTP_ASSIST_BIT); + + return; +} + +PTPMessagePathDelayResp::~PTPMessagePathDelayResp() +{ + delete requestingPortIdentity; +} + +void PTPMessagePathDelayResp::processMessage(IEEE1588Port * port) +{ + if (port->getPortState() == PTP_INITIALIZING + || port->getPortState() == PTP_DISABLED) { + // Do nothing all messages should be ignored when in this state + return; + } + if (port->getPortState() == PTP_FAULTY) { + // According to spec recovery is implementation specific + port->recoverPort(); + return; + } + + if (port->tryPDelayRxLock() != true) { + fprintf(stderr, "Failed to get PDelay RX Lock\n"); + return; + } + + port->getClock()->deleteEventTimer(port, + PDELAY_RESP_RECEIPT_TIMEOUT_EXPIRES); + port->getClock()->addEventTimer(port, + PDELAY_RESP_RECEIPT_TIMEOUT_EXPIRES, + (unsigned long long) + (PDELAY_RESP_RECEIPT_TIMEOUT_MULTIPLIER + * + (pow + ((double)2, + port->getPDelayInterval()) * + 1000000000.0))); + PTPMessagePathDelayResp *old_pdelay_resp = port->getLastPDelayResp(); + if (old_pdelay_resp != NULL) { + delete old_pdelay_resp; + } + port->setLastPDelayResp(this); + + port->putPDelayRxLock(); + _gc = false; + + return; +} + +void PTPMessagePathDelayResp::sendPort(IEEE1588Port * port, + PortIdentity * destIdentity) +{ + uint8_t buf_t[256]; + uint8_t *buf_ptr = buf_t + port->getPayloadOffset(); + unsigned char tspec_msg_t = 0; + Timestamp requestReceiptTimestamp_BE; + memset(buf_t, 0, 256); + // Create packet in buf + // Copy in common header + messageLength = PTP_COMMON_HDR_LENGTH + PTP_PDELAY_RESP_LENGTH; + tspec_msg_t |= messageType & 0xF; + buildCommonHeader(buf_ptr); + requestReceiptTimestamp_BE.seconds_ms = + PLAT_htons(requestReceiptTimestamp.seconds_ms); + requestReceiptTimestamp_BE.seconds_ls = + PLAT_htonl(requestReceiptTimestamp.seconds_ls); + requestReceiptTimestamp_BE.nanoseconds = + PLAT_htonl(requestReceiptTimestamp.nanoseconds); + + // Copy in v2 PDelay_Req specific fields + requestingPortIdentity->getClockIdentityString((char *)buf_ptr + + PTP_PDELAY_RESP_REQ_CLOCK_ID + (PTP_PDELAY_RESP_OFFSET)); + requestingPortIdentity->getPortNumberNO((uint16_t *) (buf_ptr + + PTP_PDELAY_RESP_REQ_PORT_ID + (PTP_PDELAY_RESP_OFFSET))); + memcpy(buf_ptr + PTP_PDELAY_RESP_SEC_MS(PTP_PDELAY_RESP_OFFSET), + &(requestReceiptTimestamp_BE.seconds_ms), + sizeof(requestReceiptTimestamp.seconds_ms)); + memcpy(buf_ptr + PTP_PDELAY_RESP_SEC_LS(PTP_PDELAY_RESP_OFFSET), + &(requestReceiptTimestamp_BE.seconds_ls), + sizeof(requestReceiptTimestamp.seconds_ls)); + memcpy(buf_ptr + PTP_PDELAY_RESP_NSEC(PTP_PDELAY_RESP_OFFSET), + &(requestReceiptTimestamp_BE.nanoseconds), + sizeof(requestReceiptTimestamp.nanoseconds)); + + XPTPD_INFO("PDelay Resp Timestamp: %u,%u", + requestReceiptTimestamp.seconds_ls, + requestReceiptTimestamp.nanoseconds); + + port->sendEventPort(buf_t, messageLength, MCAST_PDELAY, destIdentity); + return; +} + +void PTPMessagePathDelayResp::setRequestingPortIdentity(PortIdentity * identity) +{ + *requestingPortIdentity = *identity; +} + +void PTPMessagePathDelayResp::getRequestingPortIdentity(PortIdentity * identity) +{ + *identity = *requestingPortIdentity; +} + + PTPMessagePathDelayRespFollowUp::PTPMessagePathDelayRespFollowUp(IEEE1588Port * port): +PTPMessageCommon + (port) +{ + logMeanMessageInterval = 0; + control = MESSAGE_OTHER; + messageType = PATH_DELAY_FOLLOWUP_MESSAGE; + versionPTP = GPTP_VERSION; + requestingPortIdentity = new PortIdentity(); + + return; +} + +PTPMessagePathDelayRespFollowUp::~PTPMessagePathDelayRespFollowUp() +{ + delete requestingPortIdentity; +} + +void PTPMessagePathDelayRespFollowUp::processMessage(IEEE1588Port * port) +{ + Timestamp remote_resp_tx_timestamp(0, 0, 0); + Timestamp request_tx_timestamp(0, 0, 0); + Timestamp remote_req_rx_timestamp(0, 0, 0); + Timestamp response_rx_timestamp(0, 0, 0); + + if (port->getPortState() == PTP_INITIALIZING + || port->getPortState() == PTP_DISABLED) { + // Do nothing all messages should be ignored when in this state + return; + } + if (port->getPortState() == PTP_FAULTY) { + // According to spec recovery is implementation specific + port->recoverPort(); + return; + } + + if (port->tryPDelayRxLock() != true) + return; + + PTPMessagePathDelayReq *req = port->getLastPDelayReq(); + PTPMessagePathDelayResp *resp = port->getLastPDelayResp(); + + PortIdentity req_id; + PortIdentity resp_id; + + if (req == NULL) { + // Shouldn't happen + XPTPD_ERROR + (">>> Received PDelay followup but no REQUEST exists"); + goto abort; + } + + if (resp == NULL) { + // Probably shouldn't happen either + XPTPD_ERROR + (">>> Received PDelay followup but no RESPONSE exists"); + goto abort; + } + + req->getPortIdentity(&req_id); + resp->getRequestingPortIdentity(&resp_id); + + // Check if we have sent a request +// if( req->getSequenceId() != sequenceId || req_id.portNumber != requestingPortIdentity.portNumber || +// memcmp( req_id.clockIdentity, requestingPortIdentity.clockIdentity, PTP_CLOCK_IDENTITY_LENGTH ) != 0 ) { + if (req->getSequenceId() != sequenceId) { + + XPTPD_ERROR + ("Received PDelay Response Follow Up but cannot find corresponding request"); + // XPTPD_INFO( "Requesting SeqId: %u ", req->getSequenceId() ); + // XPTPD_INFO( "Requesting SeqId: %u ", sequenceId ); + XPTPD_ERROR("My SeqId: %u ", req->getSequenceId()); + XPTPD_ERROR("Their SeqId: %u ", sequenceId); + //XPTPD_INFO( "Requesting Port: %u ", requestingPortIdentity.portNumber ); + XPTPD_INFO("Requesting Identity: \""); + +#ifdef DEBUG + for (int n = 0; n < PTP_CLOCK_IDENTITY_LENGTH; ++n) { + fprintf(stderr, "%u: %x ", n, + requestingPortIdentity.clockIdentity[n]); + } + fprintf(stderr, " \"\n"); +#endif + + //XPTPD_INFO( "Requesting SeqId: %u ", req->getSequenceId() ); + //XPTPD_INFO( "Requesting Port: %u ", req_id.portNumber ); + XPTPD_INFO("Requesting Identity: \""); + +#ifdef DEBUG + for (int n = 0; n < PTP_CLOCK_IDENTITY_LENGTH; ++n) { + fprintf(stderr, "%u:%x ", n, req_id.clockIdentity[n]); + } + fprintf(stderr, " \"\n"); +#endif + + goto abort; + } + //fprintf( stderr, "PDelay Follow-Up OK\n" ); + + // Check if we have received a response + if (resp->getSequenceId() != sequenceId + || resp_id != *requestingPortIdentity) { + uint16_t resp_port_number; + uint16_t req_port_number; + resp_id.getPortNumber(&resp_port_number); + requestingPortIdentity->getPortNumber(&req_port_number); + XPTPD_ERROR + ("Received PDelay Response Follow Up but cannot find corresponding response"); + XPTPD_ERROR("%hu, %hu, %hu, %hu", resp->getSequenceId(), + sequenceId, resp_port_number, req_port_number); + + goto abort; + } + + XPTPD_INFO("Request Sequence Id: %u", req->getSequenceId()); + XPTPD_INFO("Response Sequence Id: %u", resp->getSequenceId()); + XPTPD_INFO("Follow-Up Sequence Id: %u", req->getSequenceId()); + + port->setAsCapable(true); + + int64_t link_delay; + unsigned long long turn_around; + + // Assume that we are a two step clock, otherwise originTimestamp may be used + request_tx_timestamp = req->getTimestamp(); + if (request_tx_timestamp.nanoseconds == + PDELAY_PENDING_TIMESTAMP.nanoseconds) { + // Defer processing + if (port->getLastPDelayRespFollowUp() != NULL) { + delete port->getLastPDelayRespFollowUp(); + } + port->setLastPDelayRespFollowUp(this); + port->getClock()->addEventTimer(port, + PDELAY_DEFERRED_PROCESSING, + 1000000); + goto defer; + } + if (request_tx_timestamp.nanoseconds == INVALID_TIMESTAMP.nanoseconds) { + // Stop processing the packet + goto abort; + } + remote_req_rx_timestamp = resp->getRequestReceiptTimestamp(); + response_rx_timestamp = resp->getTimestamp(); + remote_resp_tx_timestamp = responseOriginTimestamp; + +#if 0 + fprintf(stderr, "@REQT,%u,%u,%u,%hu\n", request_tx_timestamp.seconds_ms, + request_tx_timestamp.seconds_ls, + request_tx_timestamp.nanoseconds, req->getSequenceId()); + fprintf(stderr, "@REQR,%u,%u,%u,%hu\n", + remote_req_rx_timestamp.seconds_ms, + remote_req_rx_timestamp.seconds_ls, + remote_req_rx_timestamp.nanoseconds, resp->getSequenceId()); + fprintf(stderr, "@RESR,%u,%u,%u,%hu\n", + response_rx_timestamp.seconds_ms, + response_rx_timestamp.seconds_ls, + response_rx_timestamp.nanoseconds, resp->getSequenceId()); + fprintf(stderr, "@REST,%u,%u,%u,%hu\n", + remote_resp_tx_timestamp.seconds_ms, + remote_resp_tx_timestamp.seconds_ls, + remote_resp_tx_timestamp.nanoseconds, sequenceId); +#endif + + link_delay = + ((response_rx_timestamp.seconds_ms * 1LL - + request_tx_timestamp.seconds_ms) << 32) * 1000000000; + link_delay += + (response_rx_timestamp.seconds_ls * 1LL - + request_tx_timestamp.seconds_ls) * 1000000000; + link_delay += + (response_rx_timestamp.nanoseconds * 1LL - + request_tx_timestamp.nanoseconds); + //fprintf( stderr, "Send/Recv Delay is %lld\n", link_delay ); + + turn_around = + ((remote_resp_tx_timestamp.seconds_ms * 1LL - + remote_req_rx_timestamp.seconds_ms) << 32) * 1000000000; + turn_around += + (remote_resp_tx_timestamp.seconds_ls * 1LL - + remote_req_rx_timestamp.seconds_ls) * 1000000000; + turn_around += + (remote_resp_tx_timestamp.nanoseconds * 1LL - + remote_req_rx_timestamp.nanoseconds); + //fprintf( stderr, "Unadjusted Turn Around is %llu\n", turn_around ); + //fprintf( stderr, "Unadjusted Link Delay *2 %lld\n", link_delay - turn_around ); + + //fprintf( stderr, "Peer Rate Offset: %d", port->getPeerRateOffset() ); + + // Adjust turn-around time for peer to local clock rate difference + turn_around += + (((long long)turn_around) * port->getPeerRateOffset()) / + 1000000000000LL; + + XPTPD_INFO("Turn Around Adjustment %Ld", + ((long long)turn_around * port->getPeerRateOffset()) / + 1000000000000LL); + XPTPD_INFO("Step #1: Turn Around Adjustment %Ld", + ((long long)turn_around * port->getPeerRateOffset())); + XPTPD_INFO("Adjusted Peer turn around is %Lu", turn_around); + + // Subtract turn-around time from link delay after rate adjustment + link_delay -= turn_around; + + link_delay /= 2; + + { + uint64_t mine_elapsed; + uint64_t theirs_elapsed; + Timestamp prev_peer_ts_mine; + Timestamp prev_peer_ts_theirs; + int32_t rate_offset; + if (port->getPeerOffset(prev_peer_ts_mine, prev_peer_ts_theirs)) { + mine_elapsed = + TIMESTAMP_TO_NS(request_tx_timestamp) - + TIMESTAMP_TO_NS(prev_peer_ts_mine); + theirs_elapsed = + TIMESTAMP_TO_NS(remote_req_rx_timestamp) - + TIMESTAMP_TO_NS(prev_peer_ts_theirs); +#if 0 + rate_offset = + (int32_t) ((mine_elapsed * 1000000000ULL) / + theirs_elapsed); + rate_offset -= 1000000000; +#endif + theirs_elapsed -= port->getLinkDelay() - link_delay; + rate_offset = + (int32_t) ((mine_elapsed * 1000000000ULL) / + theirs_elapsed); + rate_offset -= 1000000000; + rate_offset *= 1000; + port->setPeerRateOffset(rate_offset); + //fprintf( stderr, "(2)PDelay rate offset: %d\n", rate_offset ); + } + } + port->setLinkDelay(link_delay); + port->setPeerOffset(request_tx_timestamp, remote_req_rx_timestamp); + + abort: + delete req; + port->setLastPDelayReq(NULL); + delete resp; + port->setLastPDelayResp(NULL); + + _gc = true; + + defer: + port->putPDelayRxLock(); + + return; +} + +void PTPMessagePathDelayRespFollowUp::sendPort(IEEE1588Port * port, + PortIdentity * destIdentity) +{ + uint8_t buf_t[256]; + uint8_t *buf_ptr = buf_t + port->getPayloadOffset(); + unsigned char tspec_msg_t = 0; + Timestamp responseOriginTimestamp_BE; + memset(buf_t, 0, 256); + // Create packet in buf + // Copy in common header + messageLength = PTP_COMMON_HDR_LENGTH + PTP_PDELAY_RESP_LENGTH; + tspec_msg_t |= messageType & 0xF; + buildCommonHeader(buf_ptr); + responseOriginTimestamp_BE.seconds_ms = + PLAT_htons(responseOriginTimestamp.seconds_ms); + responseOriginTimestamp_BE.seconds_ls = + PLAT_htonl(responseOriginTimestamp.seconds_ls); + responseOriginTimestamp_BE.nanoseconds = + PLAT_htonl(responseOriginTimestamp.nanoseconds); + + // Copy in v2 PDelay_Req specific fields + requestingPortIdentity->getClockIdentityString((char *)buf_ptr + + PTP_PDELAY_FOLLOWUP_REQ_CLOCK_ID + (PTP_PDELAY_FOLLOWUP_OFFSET)); + requestingPortIdentity->getPortNumberNO((uint16_t *) (buf_ptr + + PTP_PDELAY_FOLLOWUP_REQ_PORT_ID + (PTP_PDELAY_FOLLOWUP_OFFSET))); + memcpy(buf_ptr + PTP_PDELAY_FOLLOWUP_SEC_MS(PTP_PDELAY_FOLLOWUP_OFFSET), + &(responseOriginTimestamp_BE.seconds_ms), + sizeof(responseOriginTimestamp.seconds_ms)); + memcpy(buf_ptr + PTP_PDELAY_FOLLOWUP_SEC_LS(PTP_PDELAY_FOLLOWUP_OFFSET), + &(responseOriginTimestamp_BE.seconds_ls), + sizeof(responseOriginTimestamp.seconds_ls)); + memcpy(buf_ptr + PTP_PDELAY_FOLLOWUP_NSEC(PTP_PDELAY_FOLLOWUP_OFFSET), + &(responseOriginTimestamp_BE.nanoseconds), + sizeof(responseOriginTimestamp.nanoseconds)); + + XPTPD_INFO("PDelay Resp Timestamp: %u,%u", + responseOriginTimestamp.seconds_ls, + responseOriginTimestamp.nanoseconds); + + port->sendGeneralPort(buf_t, messageLength, MCAST_PDELAY, destIdentity); + return; +} + +void PTPMessagePathDelayRespFollowUp::setRequestingPortIdentity(PortIdentity * + identity) +{ + *requestingPortIdentity = *identity; +} diff --git a/daemons/gptp/linux/CVS/Entries b/daemons/gptp/linux/CVS/Entries new file mode 100644 index 0000000..b44d45c --- /dev/null +++ b/daemons/gptp/linux/CVS/Entries @@ -0,0 +1,2 @@ +D/src//// +D/build//// diff --git a/daemons/gptp/linux/CVS/Repository b/daemons/gptp/linux/CVS/Repository new file mode 100644 index 0000000..34ff17b --- /dev/null +++ b/daemons/gptp/linux/CVS/Repository @@ -0,0 +1 @@ +linux_igb_avb/daemons/gptp/linux diff --git a/daemons/gptp/linux/CVS/Root b/daemons/gptp/linux/CVS/Root new file mode 100644 index 0000000..beb56f8 --- /dev/null +++ b/daemons/gptp/linux/CVS/Root @@ -0,0 +1 @@ +:pserver:ekmann@azusa.jf.intel.com:/home/cvsroot/ladsw diff --git a/daemons/gptp/linux/build/CVS/Entries b/daemons/gptp/linux/build/CVS/Entries new file mode 100644 index 0000000..8933c8a --- /dev/null +++ b/daemons/gptp/linux/build/CVS/Entries @@ -0,0 +1,2 @@ +D/obj//// +/Makefile/1.1/Fri Sep 7 18:53:45 2012// diff --git a/daemons/gptp/linux/build/CVS/Repository b/daemons/gptp/linux/build/CVS/Repository new file mode 100644 index 0000000..8599377 --- /dev/null +++ b/daemons/gptp/linux/build/CVS/Repository @@ -0,0 +1 @@ +linux_igb_avb/daemons/gptp/linux/build diff --git a/daemons/gptp/linux/build/CVS/Root b/daemons/gptp/linux/build/CVS/Root new file mode 100644 index 0000000..beb56f8 --- /dev/null +++ b/daemons/gptp/linux/build/CVS/Root @@ -0,0 +1 @@ +:pserver:ekmann@azusa.jf.intel.com:/home/cvsroot/ladsw diff --git a/daemons/gptp/linux/build/Makefile b/daemons/gptp/linux/build/Makefile new file mode 100644 index 0000000..8d13174 --- /dev/null +++ b/daemons/gptp/linux/build/Makefile @@ -0,0 +1,84 @@ +# +# Copyright (c) 2012 Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +CXX = g++ + +CFLAGS_G = -Wall -g -I. -I../../common -I../src +LDFLAGS_G = -lpthread -lrt + +CFLAGS = $(CFLAGS_G) +LDFLAGS = $(LDFLAGS_G) + +CXXFLAGS = +COMMON_DIR = ../../common +SRC_DIR = ../src +OBJ_DIR = obj + +OBJ_FILES = $(OBJ_DIR)/ptp_message.o\ + $(OBJ_DIR)/avbts_osnet.o\ + $(OBJ_DIR)/ieee1588port.o\ + $(OBJ_DIR)/ieee1588clock.o + +HEADER_FILES = $(COMMON_DIR)/avbts_port.hpp\ + $(COMMON_DIR)/avbts_ostimerq.hpp\ + $(COMMON_DIR)/avbts_ostimer.hpp\ + $(COMMON_DIR)/avbts_osthread.hpp\ + $(COMMON_DIR)/avbts_osnet.hpp\ + $(COMMON_DIR)/avbts_oslock.hpp\ + $(COMMON_DIR)/avbts_osipc.hpp\ + $(COMMON_DIR)/avbts_oscondition.hpp\ + $(COMMON_DIR)/avbts_message.hpp\ + $(COMMON_DIR)/avbts_clock.hpp\ + $(COMMON_DIR)/ieee1588.hpp\ + $(SRC_DIR)/linux_hal.hpp\ + $(SRC_DIR)/platform.hpp + +all: daemon_cl + +rebuild: clean all + +daemon_cl: $(SRC_DIR)/daemon_cl.cpp $(OBJ_FILES) + $(CXX) $(CFLAGS) $(CXXFLAGS) $(OBJ_FILES) $(SRC_DIR)/daemon_cl.cpp -o $(OBJ_DIR)/daemon_cl $(LDFLAGS) + +$(OBJ_DIR)/ieee1588port.o: $(COMMON_DIR)/ieee1588port.cpp $(HEADER_FILES) + $(CXX) $(CFLAGS) $(CXXFLAGS) -c $(COMMON_DIR)/ieee1588port.cpp -o $(OBJ_DIR)/ieee1588port.o + +$(OBJ_DIR)/ieee1588clock.o: $(COMMON_DIR)/ieee1588clock.cpp $(HEADER_FILES) + $(CXX) $(CFLAGS) $(CXXFLAGS) -c $(COMMON_DIR)/ieee1588clock.cpp -o $(OBJ_DIR)/ieee1588clock.o + +$(OBJ_DIR)/ptp_message.o: $(COMMON_DIR)/ptp_message.cpp $(HEADER_FILES) + $(CXX) $(CFLAGS) $(CXXFLAGS) -c $(COMMON_DIR)/ptp_message.cpp -o $(OBJ_DIR)/ptp_message.o + +$(OBJ_DIR)/avbts_osnet.o: $(COMMON_DIR)/avbts_osnet.cpp $(HEADER_FILES) + $(CXX) $(CFLAGS) $(CXXFLAGS) -c $(COMMON_DIR)/avbts_osnet.cpp -o $(OBJ_DIR)/avbts_osnet.o + +clean: + /bin/rm -f *~ $(OBJ_DIR)/*.o $(OBJ_DIR)/daemon_cl + diff --git a/daemons/gptp/linux/build/obj/.dir b/daemons/gptp/linux/build/obj/.dir new file mode 100644 index 0000000..e69de29 diff --git a/daemons/gptp/linux/build/obj/CVS/Entries b/daemons/gptp/linux/build/obj/CVS/Entries new file mode 100644 index 0000000..508243e --- /dev/null +++ b/daemons/gptp/linux/build/obj/CVS/Entries @@ -0,0 +1,2 @@ +/.dir/1.1/Fri Sep 21 21:13:03 2012// +D diff --git a/daemons/gptp/linux/build/obj/CVS/Repository b/daemons/gptp/linux/build/obj/CVS/Repository new file mode 100644 index 0000000..1b22db8 --- /dev/null +++ b/daemons/gptp/linux/build/obj/CVS/Repository @@ -0,0 +1 @@ +linux_igb_avb/daemons/gptp/linux/build/obj diff --git a/daemons/gptp/linux/build/obj/CVS/Root b/daemons/gptp/linux/build/obj/CVS/Root new file mode 100644 index 0000000..beb56f8 --- /dev/null +++ b/daemons/gptp/linux/build/obj/CVS/Root @@ -0,0 +1 @@ +:pserver:ekmann@azusa.jf.intel.com:/home/cvsroot/ladsw diff --git a/daemons/gptp/linux/build/obj/avbts_osnet.o b/daemons/gptp/linux/build/obj/avbts_osnet.o new file mode 100644 index 0000000000000000000000000000000000000000..5aab873e412002c23be2c3bd5e4ce5840be99117 GIT binary patch literal 63064 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 z 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

n>d1n*Z}P{I^}3*Zup)D^;ELcKHJr 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 z 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 z%0D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 D%@^1Nt{sFBn(UHRZofx$(+PRBn=T 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 Fcs;ks-cRGv?(#;T1GTtfez@FQZg0BOTW)W< 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 literal 0 HcmV?d00001 diff --git a/daemons/gptp/linux/src/CVS/Entries b/daemons/gptp/linux/src/CVS/Entries new file mode 100644 index 0000000..1c3e6b1 --- /dev/null +++ b/daemons/gptp/linux/src/CVS/Entries @@ -0,0 +1,4 @@ +/daemon_cl.cpp/1.1/Fri Sep 7 18:52:56 2012// +/linux_hal.hpp/1.1/Fri Sep 21 17:52:55 2012// +/platform.hpp/1.1/Fri Sep 7 18:52:41 2012// +D diff --git a/daemons/gptp/linux/src/CVS/Repository b/daemons/gptp/linux/src/CVS/Repository new file mode 100644 index 0000000..8a8b350 --- /dev/null +++ b/daemons/gptp/linux/src/CVS/Repository @@ -0,0 +1 @@ +linux_igb_avb/daemons/gptp/linux/src diff --git a/daemons/gptp/linux/src/CVS/Root b/daemons/gptp/linux/src/CVS/Root new file mode 100644 index 0000000..beb56f8 --- /dev/null +++ b/daemons/gptp/linux/src/CVS/Root @@ -0,0 +1 @@ +:pserver:ekmann@azusa.jf.intel.com:/home/cvsroot/ladsw diff --git a/daemons/gptp/linux/src/daemon_cl.cpp b/daemons/gptp/linux/src/daemon_cl.cpp new file mode 100644 index 0000000..dc3f85a --- /dev/null +++ b/daemons/gptp/linux/src/daemon_cl.cpp @@ -0,0 +1,89 @@ +/****************************************************************************** + + Copyright (c) 2012 Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#include "ieee1588.hpp" +#include "avbts_clock.hpp" +#include "avbts_osnet.hpp" +#include "avbts_oslock.hpp" +#include "linux_hal.hpp" + +int main(int argc, char **argv) +{ + sigset_t set; + InterfaceName *ifname; + int sig; + + LinuxPCAPNetworkInterfaceFactory *default_factory = + new LinuxPCAPNetworkInterfaceFactory; + OSNetworkInterfaceFactory::registerFactory(factory_name_t("default"), + default_factory); + LinuxThreadFactory *thread_factory = new LinuxThreadFactory(); + LinuxTimerQueueFactory *timerq_factory = new LinuxTimerQueueFactory(); + LinuxLockFactory *lock_factory = new LinuxLockFactory(); + LinuxTimerFactory *timer_factory = new LinuxTimerFactory(); + LinuxConditionFactory *condition_factory = new LinuxConditionFactory(); + LinuxSimpleIPC *ipc = new LinuxSimpleIPC(); + + if (argc < 2) + return -1; + ifname = new InterfaceName(argv[1], strlen(argv[1])); + + IEEE1588Clock *clock = new IEEE1588Clock(false, timerq_factory, ipc); + HWTimestamper *timestamper = new LinuxTimestamper(); + IEEE1588Port *port = + new IEEE1588Port(clock, 1, false, timestamper, false, 0, ifname, + condition_factory, thread_factory, timer_factory, + lock_factory); + + if (!port->init_port()) { + printf("failed to initialize port \n"); + return -1; + } + port->processEvent(POWERUP); + + sigemptyset(&set); + sigaddset(&set, SIGINT); + if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) { + perror("pthread_sigmask()"); + return -1; + } + + if (sigwait(&set, &sig) != 0) { + perror("sigwait()"); + return -1; + } + + fprintf(stderr, "Exiting on %d\n", sig); + + return 0; +} diff --git a/daemons/gptp/linux/src/linux_hal.hpp b/daemons/gptp/linux/src/linux_hal.hpp new file mode 100644 index 0000000..1d3d269 --- /dev/null +++ b/daemons/gptp/linux/src/linux_hal.hpp @@ -0,0 +1,875 @@ +/****************************************************************************** + + Copyright (c) 2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef LINUX_HAL_HPP +#define LINUX_HAL_HPP + +#include "avbts_osnet.hpp" +#include "avbts_oslock.hpp" +#include "avbts_oscondition.hpp" +#include "avbts_ostimerq.hpp" +#include "avbts_ostimer.hpp" +#include "ieee1588.hpp" + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define ONE_WAY_PHY_DELAY 400 +#define P8021AS_MULTICAST "\x01\x80\xC2\x00\x00\x0E" + +static inline Timestamp tsToTimestamp(struct timespec *ts) +{ + Timestamp ret; + if (sizeof(ts->tv_sec) > sizeof(ret.seconds_ls)) { + ret.seconds_ms = ts->tv_sec >> (sizeof(ret.seconds_ls) * 8); + ret.seconds_ls = ts->tv_sec & 0xFFFFFFFF; + ret.nanoseconds = ts->tv_nsec; + } else { + ret.seconds_ms = 0; + ret.seconds_ls = ts->tv_sec; + ret.nanoseconds = ts->tv_nsec; + } + return ret; +} + +class LinuxTimestamper:public HWTimestamper { + private: + int sd; + Timestamp crstamp_system; + Timestamp crstamp_device; + pthread_mutex_t cross_stamp_lock; + bool cross_stamp_good; + std::list < Timestamp > rxTimestampList; + public: + virtual bool HWTimestamper_init(InterfaceLabel * iface_label) { + pthread_mutex_init(&cross_stamp_lock, NULL); + sd = -1; + cross_stamp_good = false; + return true; + } + void setSocketDescriptor(int sd) { + this->sd = sd; + }; + void updateCrossStamp(Timestamp * system_time, Timestamp * device_time) { + pthread_mutex_lock(&cross_stamp_lock); + crstamp_system = *system_time; + crstamp_device = *device_time; + cross_stamp_good = true; + pthread_mutex_unlock(&cross_stamp_lock); + } + void pushRXTimestamp(Timestamp * tstamp) { + rxTimestampList.push_front(*tstamp); + } + bool post_init(int ifindex) { + int timestamp_flags = 0; + struct ifreq device; + struct hwtstamp_config hwconfig; + int err; + + memset(&device, 0, sizeof(device)); + device.ifr_ifindex = ifindex; + err = ioctl(sd, SIOCGIFNAME, &device); + if (err == -1) { + XPTPD_ERROR("Failed to get interface name: %s", + strerror(errno)); + return false; + } + + device.ifr_data = (char *)&hwconfig; + memset(&hwconfig, 0, sizeof(hwconfig)); + hwconfig.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; + hwconfig.tx_type = HWTSTAMP_TX_ON; + printf("TX type = %u\n", hwconfig.tx_type); + printf("RX filter = %u\n", hwconfig.rx_filter); + err = ioctl(sd, SIOCSHWTSTAMP, &device); + if (err == -1) { + XPTPD_ERROR("Failed to configure timestamping: %s", + strerror(errno)); + return false; + } + + timestamp_flags |= SOF_TIMESTAMPING_TX_HARDWARE; + timestamp_flags |= SOF_TIMESTAMPING_RX_HARDWARE; + timestamp_flags |= SOF_TIMESTAMPING_SYS_HARDWARE; + timestamp_flags |= SOF_TIMESTAMPING_RAW_HARDWARE; + err = + setsockopt(sd, SOL_SOCKET, SO_TIMESTAMPING, + ×tamp_flags, sizeof(timestamp_flags)); + if (err == -1) { + XPTPD_ERROR + ("Failed to configure timestamping on socket: %s", + strerror(errno)); + return false; + } + + return true; + } + + virtual bool HWTimestamper_gettime(Timestamp * system_time, + Timestamp * device_time, + uint32_t * local_clock, + uint32_t * nominal_clock_rate) { + bool ret = false; + pthread_mutex_lock(&cross_stamp_lock); + if (cross_stamp_good) { + *system_time = crstamp_system; + *device_time = crstamp_device; + ret = true; + } + pthread_mutex_unlock(&cross_stamp_lock); + return ret; + } + + virtual int HWTimestamper_txtimestamp(PortIdentity * identity, + uint16_t sequenceId, + Timestamp & timestamp, + unsigned &clock_value, + bool last) { + int err; + int ret = -72; + struct msghdr msg; + struct cmsghdr *cmsg; + struct sockaddr_ll remote; + struct iovec sgentry; + struct { + struct cmsghdr cm; + char control[256]; + } control; + + if (sd == -1) + return -1; + memset(&msg, 0, sizeof(msg)); + + msg.msg_iov = &sgentry; + msg.msg_iovlen = 1; + + sgentry.iov_base = NULL; + sgentry.iov_len = 0; + + memset(&remote, 0, sizeof(remote)); + msg.msg_name = (caddr_t) & remote; + msg.msg_namelen = sizeof(remote); + msg.msg_control = &control; + msg.msg_controllen = sizeof(control); + + err = recvmsg(sd, &msg, MSG_ERRQUEUE); + if (err == -1) { + if (errno == EAGAIN) + return -72; + else + return -1; + } + + cmsg = CMSG_FIRSTHDR(&msg); + while (cmsg != NULL) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SO_TIMESTAMPING) { + struct timespec *ts_device, *ts_system; + Timestamp device, system; + ts_system = + ((struct timespec *)CMSG_DATA(cmsg)) + 1; + system = tsToTimestamp(ts_system); + ts_device = ts_system + 1; + device = tsToTimestamp(ts_device); + updateCrossStamp(&system, &device); + timestamp = device; + ret = 0; + break; + } + cmsg = CMSG_NXTHDR(&msg, cmsg); + } + + return ret; + } + + virtual int HWTimestamper_rxtimestamp(PortIdentity * identity, + uint16_t sequenceId, + Timestamp & timestamp, + unsigned &clock_value, + bool last) { + if (rxTimestampList.empty()) + return -72; + timestamp = rxTimestampList.back(); + rxTimestampList.pop_back(); + + return 0; + } +}; + +class LinuxLock:public OSLock { + friend class LinuxLockFactory; + private: + OSLockType type; + pthread_t thread_id; + int lock_c; + pthread_mutexattr_t mta; + pthread_mutex_t mutex; + pthread_cond_t port_ready_signal; + protected: + LinuxLock() { + lock_c = NULL; + } + bool initialize(OSLockType type) { + pthread_mutexattr_init(&mta); + if (type == oslock_recursive) + pthread_mutexattr_settype(&mta, + PTHREAD_MUTEX_RECURSIVE); + lock_c = pthread_mutex_init(&mutex, &mta); + if (lock_c != 0) { + XPTPD_ERROR("Mutex initialization faile - %s\n", + strerror(errno)); + return oslock_fail; + } + return oslock_ok; + + } + OSLockResult lock() { + lock_c = pthread_mutex_lock(&mutex); + if (lock_c != 0) + return oslock_fail; + return oslock_ok; + } + OSLockResult trylock() { + lock_c = pthread_mutex_trylock(&mutex); + if (lock_c != 0) + return oslock_fail; + return oslock_ok; + } + OSLockResult unlock() { + lock_c = pthread_mutex_unlock(&mutex); + if (lock_c != 0) + return oslock_fail; + return oslock_ok; + } +}; + +class LinuxLockFactory:public OSLockFactory { + public: + OSLock * createLock(OSLockType type) { + LinuxLock *lock = new LinuxLock(); + if (lock->initialize(type) != oslock_ok) { + delete lock; + lock = NULL; + } + return lock; + } +}; + +class LinuxCondition:public OSCondition { + friend class LinuxConditionFactory; + private: + pthread_cond_t port_ready_signal; + pthread_mutex_t port_lock; + protected: + bool initialize() { + int lock_c; + pthread_cond_init(&port_ready_signal, NULL); + lock_c = pthread_mutex_init(&port_lock, NULL); + if (lock_c != 0) + return false; + return true; + } +public: + bool wait_prelock() { + pthread_mutex_lock(&port_lock); + up(); + return true; + } + bool wait() { + pthread_cond_wait(&port_ready_signal, &port_lock); + down(); + pthread_mutex_unlock(&port_lock); + return true; + } + bool signal() { + pthread_mutex_lock(&port_lock); + if (waiting()) + pthread_cond_broadcast(&port_ready_signal); + pthread_mutex_unlock(&port_lock); + return true; + } +}; + +class LinuxConditionFactory:public OSConditionFactory { + public: + OSCondition * createCondition() { + LinuxCondition *result = new LinuxCondition(); + return result->initialize() ? result : NULL; + } +}; + +class LinuxTimerQueue; + +struct TimerQueue_t; + +struct LinuxTimerQueueHandlerArg { + timer_t timer_handle; + struct sigevent sevp; + event_descriptor_t *inner_arg; + ostimerq_handler func; + int type; + bool rm; + TimerQueue_t *timer_queue; +}; + +typedef std::list < LinuxTimerQueueHandlerArg * >TimerArgList_t; + +struct TimerQueue_t { + TimerArgList_t arg_list; + pthread_mutex_t lock; +}; + +typedef std::map < int, TimerQueue_t > TimerQueueMap_t; + +void LinuxTimerQueueHandler(union sigval arg_in); + +class LinuxTimerQueue:public OSTimerQueue { + friend class LinuxTimerQueueFactory; + friend void LinuxTimerQueueHandler(union sigval arg_in); + private: + TimerQueueMap_t timerQueueMap; + TimerArgList_t retiredTimers; + bool in_callback; + protected: + LinuxTimerQueue() { + int err; + pthread_mutex_t retiredTimersLock; + pthread_mutexattr_t retiredTimersLockAttr; + err = pthread_mutexattr_init(&retiredTimersLockAttr); + if (err != 0) { + XPTPD_ERROR("mutexattr_init()"); + exit(0); + } + err = + pthread_mutexattr_settype(&retiredTimersLockAttr, + PTHREAD_MUTEX_NORMAL); + if (err != 0) { + XPTPD_ERROR("mutexattr_settype()"); + exit(0); + } + err = + pthread_mutex_init(&retiredTimersLock, + &retiredTimersLockAttr); + if (err != 0) { + XPTPD_ERROR("mutex_init()"); + exit(0); + } + }; + public: + bool addEvent(unsigned long micros, int type, ostimerq_handler func, + event_descriptor_t * arg, bool rm, unsigned *event) { + LinuxTimerQueueHandlerArg *outer_arg = + new LinuxTimerQueueHandlerArg(); + if (timerQueueMap.find(type) == timerQueueMap.end()) { + pthread_mutex_init(&timerQueueMap[type].lock, NULL); + } + outer_arg->inner_arg = arg; + outer_arg->func = func; + outer_arg->type = type; + outer_arg->timer_queue = &timerQueueMap[type]; + sigset_t set; + sigset_t oset; + int err; + timer_t timerhandle; + struct sigevent ev; + + sigemptyset(&set); + sigaddset(&set, SIGALRM); + err = pthread_sigmask(SIG_BLOCK, &set, &oset); + if (err != 0) { + XPTPD_ERROR + ("Add timer pthread_sigmask( SIG_BLOCK ... )"); + exit(0); + } + pthread_mutex_lock(&timerQueueMap[type].lock); + { + struct itimerspec its; + memset(&ev, 0, sizeof(ev)); + ev.sigev_notify = SIGEV_THREAD; + ev.sigev_value.sival_ptr = outer_arg; + ev.sigev_notify_function = LinuxTimerQueueHandler; + ev.sigev_notify_attributes = new pthread_attr_t; + pthread_attr_init((pthread_attr_t *) ev.sigev_notify_attributes); + pthread_attr_setdetachstate((pthread_attr_t *) ev.sigev_notify_attributes, + PTHREAD_CREATE_DETACHED); + if (timer_create(CLOCK_MONOTONIC, &ev, &timerhandle) == -1) { + XPTPD_ERROR("timer_create failed - %s\n", + strerror(errno)); + exit(0); + } + outer_arg->timer_handle = timerhandle; + outer_arg->sevp = ev; + + memset(&its, 0, sizeof(its)); + its.it_value.tv_sec = micros / 1000000; + its.it_value.tv_nsec = (micros % 1000000) * 1000; + timer_settime(outer_arg->timer_handle, 0, &its, NULL); + } + timerQueueMap[type].arg_list.push_front(outer_arg); + + pthread_mutex_unlock(&timerQueueMap[type].lock); + + err = pthread_sigmask(SIG_SETMASK, &oset, NULL); + if (err != 0) { + XPTPD_ERROR + ("Add timer pthread_sigmask( SIG_SETMASK ... )"); + exit(0); + } + return true; + } + bool cancelEvent(int type, unsigned *event) { + TimerQueueMap_t::iterator iter = timerQueueMap.find(type); + if (iter == timerQueueMap.end()) + return false; + sigset_t set; + sigset_t oset; + int err; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + err = pthread_sigmask(SIG_BLOCK, &set, &oset); + if (err != 0) { + XPTPD_ERROR + ("Add timer pthread_sigmask( SIG_BLOCK ... )"); + exit(0); + } + pthread_mutex_lock(&timerQueueMap[type].lock); + while (!timerQueueMap[type].arg_list.empty()) { + LinuxTimerQueueHandlerArg *del_arg = + timerQueueMap[type].arg_list.front(); + timerQueueMap[type].arg_list.pop_front(); + timer_delete(del_arg->timer_handle); + delete(pthread_attr_t *) del_arg->sevp. + sigev_notify_attributes; + if (del_arg->rm) + delete del_arg->inner_arg; + delete del_arg; + } + pthread_mutex_unlock(&timerQueueMap[type].lock); + err = pthread_sigmask(SIG_SETMASK, &oset, NULL); + if (err != 0) { + XPTPD_ERROR + ("Add timer pthread_sigmask( SIG_SETMASK ... )"); + exit(0); + } + return true; + } +}; + +void LinuxTimerQueueHandler(union sigval arg_in) +{ + LinuxTimerQueueHandlerArg *arg = + (LinuxTimerQueueHandlerArg *) arg_in.sival_ptr; + bool runnable = false; + unsigned size; + + pthread_mutex_lock(&arg->timer_queue->lock); + size = arg->timer_queue->arg_list.size(); + arg->timer_queue->arg_list.remove(arg); + if (arg->timer_queue->arg_list.size() < size) { + runnable = true; + } + pthread_mutex_unlock(&arg->timer_queue->lock); + + if (runnable) { + arg->func(arg->inner_arg); + if (arg->rm) + delete arg->inner_arg; + delete(pthread_attr_t *) arg->sevp.sigev_notify_attributes; + } +} + +class LinuxTimerQueueFactory:public OSTimerQueueFactory { + public: + virtual OSTimerQueue * createOSTimerQueue() { + LinuxTimerQueue *timerq = new LinuxTimerQueue(); + return timerq; + }; +}; + +class LinuxTimer:public OSTimer { + friend class LinuxTimerFactory; + public: + virtual unsigned long sleep(unsigned long micros) { + struct timespec req = { 0, micros }; /* Should be micros*1000 -Chris */ + nanosleep(&req, NULL); + return micros; + } + protected: + LinuxTimer() {}; +}; + +class LinuxTimerFactory:public OSTimerFactory { + public: + virtual OSTimer * createTimer() { + return new LinuxTimer(); + } +}; + +struct OSThreadArg { + OSThreadFunction func; + void *arg; + OSThreadExitCode ret; +}; + +void *OSThreadCallback(void *input) +{ + OSThreadArg *arg = (OSThreadArg *) input; + arg->ret = arg->func(arg->arg); + return 0; +} + +class LinuxThread:public OSThread { + friend class LinuxThreadFactory; + private: + + pthread_t thread_id; + OSThreadArg *arg_inner; + public: + virtual bool start(OSThreadFunction function, void *arg) { + sigset_t set; + sigset_t oset; + int err; + arg_inner = new OSThreadArg(); + arg_inner->func = function; + arg_inner->arg = arg; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + err = pthread_sigmask(SIG_BLOCK, &set, &oset); + if (err != 0) { + XPTPD_ERROR + ("Add timer pthread_sigmask( SIG_BLOCK ... )"); + return false; + } + err = pthread_create(&thread_id, NULL, OSThreadCallback, + arg_inner); + if (err != 0) + return false; + sigdelset(&oset, SIGALRM); + err = pthread_sigmask(SIG_SETMASK, &oset, NULL); + if (err != 0) { + XPTPD_ERROR + ("Add timer pthread_sigmask( SIG_SETMASK ... )"); + return false; + } + + return true; + } + virtual bool join(OSThreadExitCode & exit_code) { + int err; + err = pthread_join(thread_id, NULL); + if (err != 0) + return false; + exit_code = arg_inner->ret; + delete arg_inner; + return true; + } + protected: + LinuxThread() {}; +}; + +class LinuxThreadFactory:public OSThreadFactory { + public: + OSThread * createThread() { + return new LinuxThread(); + } +}; + +class LinuxPCAPNetworkInterface:public OSNetworkInterface { + friend class LinuxPCAPNetworkInterfaceFactory; + private: + LinkLayerAddress local_addr; + int sd_event; + int sd_general; + LinuxTimestamper *timestamper; + int ifindex; + public: + virtual net_result send(LinkLayerAddress * addr, uint8_t * payload, + size_t length, bool timestamp) { + sockaddr_ll *remote = NULL; + int err; + remote = new struct sockaddr_ll; + memset(remote, 0, sizeof(*remote)); + remote->sll_family = AF_PACKET; + remote->sll_protocol = htons(PTP_ETHERTYPE); + remote->sll_ifindex = ifindex; + remote->sll_halen = ETH_ALEN; + addr->toOctetArray(remote->sll_addr); + if (timestamp) { + err = sendto(sd_event, payload, length, 0, + (sockaddr *) remote, sizeof(*remote)); + } else { + err = sendto(sd_general, payload, length, 0, + (sockaddr *) remote, sizeof(*remote)); + } + delete remote; + if (err == -1) { + XPTPD_ERROR("Failed to send: %s(%d)", strerror(errno), + errno); + return net_fatal; + } + return net_succeed; + } + + virtual net_result recv(LinkLayerAddress * addr, uint8_t * payload, + size_t & length) { + fd_set readfds; + int err; + struct msghdr msg; + struct cmsghdr *cmsg; + struct { + struct cmsghdr cm; + char control[256]; + } control; + struct sockaddr_ll remote; + struct iovec sgentry; + + struct timeval timeout = { 0, 100000 }; + + FD_ZERO(&readfds); + FD_SET(sd_event, &readfds); + + err = select(sd_event + 1, &readfds, NULL, NULL, &timeout); + if (err == 0) { + return net_trfail; + } else if (err == -1) { + if (err == EINTR) { + XPTPD_ERROR("select() recv signal"); + } else { + XPTPD_ERROR("select() failed"); + return net_fatal; + } + } + + memset(&msg, 0, sizeof(msg)); + + msg.msg_iov = &sgentry; + msg.msg_iovlen = 1; + + sgentry.iov_base = payload; + sgentry.iov_len = length; + + memset(&remote, 0, sizeof(remote)); + msg.msg_name = (caddr_t) & remote; + msg.msg_namelen = sizeof(remote); + msg.msg_control = &control; + msg.msg_controllen = sizeof(control); + + err = recvmsg(sd_event, &msg, 0); + if (err < 0) + return net_fatal; + *addr = LinkLayerAddress(remote.sll_addr); + + if (err > 0 && !(payload[0] & 0x8)) { + cmsg = CMSG_FIRSTHDR(&msg); + while (cmsg != NULL) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SO_TIMESTAMPING) { + struct timespec *ts_device, *ts_system; + Timestamp device, system; + ts_system = + ((struct timespec *)CMSG_DATA(cmsg)) + + 1; + system = tsToTimestamp(ts_system); + ts_device = ts_system + 1; + device = tsToTimestamp(ts_device); + timestamper->updateCrossStamp(&system, + &device); + timestamper->pushRXTimestamp(&device); + break; + } + cmsg = CMSG_NXTHDR(&msg, cmsg); + } + } + + length = err; + return net_succeed; + } + + virtual void getLinkLayerAddress(LinkLayerAddress * addr) { + *addr = local_addr; + } + + virtual unsigned getPayloadOffset() { + return 0; + } + + virtual ~ LinuxPCAPNetworkInterface() { + close(sd_event); + close(sd_general); + } + + protected: + LinuxPCAPNetworkInterface() {}; +}; + +class LinuxPCAPNetworkInterfaceFactory:public OSNetworkInterfaceFactory { + public: + virtual bool createInterface(OSNetworkInterface ** net_iface, + InterfaceLabel * label, + HWTimestamper * timestamper) { + struct ifreq device; + int err; + struct sockaddr_ll ifsock_addr; + struct packet_mreq mr_8021as; + LinkLayerAddress addr; + int ifindex; + + LinuxPCAPNetworkInterface *net_iface_l = + new LinuxPCAPNetworkInterface(); + InterfaceName *ifname = dynamic_cast < InterfaceName * >(label); + if (ifname == NULL) { + XPTPD_ERROR("ifame == NULL \n"); + return false; + } + + net_iface_l->sd_general = + socket(PF_PACKET, SOCK_DGRAM, htons(PTP_ETHERTYPE)); + if (net_iface_l->sd_general == -1) { + XPTPD_ERROR("failed to open general socket: %s \n", + strerror(errno)); + return false; + } + net_iface_l->sd_event = + socket(PF_PACKET, SOCK_DGRAM, htons(PTP_ETHERTYPE)); + if (net_iface_l->sd_event == -1) { + XPTPD_ERROR("failed to open event socket: %s \n", + strerror(errno)); + return false; + } + + memset(&device, 0, sizeof(device)); + ifname->toString(device.ifr_name, IFNAMSIZ); + err = ioctl(net_iface_l->sd_event, SIOCGIFHWADDR, &device); + if (err == -1) { + XPTPD_ERROR("Failed to get interface address: %s", + strerror(errno)); + return false; + } + addr = LinkLayerAddress(((struct sockaddr_ll *)&device.ifr_hwaddr)->sll_addr); + net_iface_l->local_addr = addr; + + err = ioctl(net_iface_l->sd_event, SIOCGIFINDEX, &device); + if (err == -1) { + XPTPD_ERROR("Failed to get interface index: %s", + strerror(errno)); + return false; + } + ifindex = device.ifr_ifindex; + net_iface_l->ifindex = ifindex; + memset(&mr_8021as, 0, sizeof(mr_8021as)); + mr_8021as.mr_ifindex = ifindex; + mr_8021as.mr_type = PACKET_MR_MULTICAST; + mr_8021as.mr_alen = 6; + memcpy(mr_8021as.mr_address, P8021AS_MULTICAST, + mr_8021as.mr_alen); + err = setsockopt(net_iface_l->sd_event, SOL_PACKET, + PACKET_ADD_MEMBERSHIP, &mr_8021as, + sizeof(mr_8021as)); + if (err == -1) { + XPTPD_ERROR + ("Unable to add PTP multicast addresses to port id: %u", + ifindex); + return false; + } + + memset(&ifsock_addr, 0, sizeof(ifsock_addr)); + ifsock_addr.sll_family = AF_PACKET; + ifsock_addr.sll_ifindex = ifindex; + ifsock_addr.sll_protocol = htons(PTP_ETHERTYPE); + err = bind(net_iface_l->sd_event, (sockaddr *) & ifsock_addr, + sizeof(ifsock_addr)); + if (err == -1) { + XPTPD_ERROR("Call to bind() failed: %s", + strerror(errno)); + return false; + } + + net_iface_l->timestamper = dynamic_cast < LinuxTimestamper * >(timestamper); + if (net_iface_l->timestamper == NULL) { + XPTPD_ERROR("timestamper == NULL\n"); + return false; + } + net_iface_l->timestamper->setSocketDescriptor(net_iface_l->sd_event); + if (!net_iface_l->timestamper->post_init(ifindex)) { + XPTPD_ERROR("post_init failed\n"); + return false; + } + *net_iface = net_iface_l; + + return true; + } +}; + +class LinuxSimpleIPC:public OS_IPC { + public: + LinuxSimpleIPC() {}; + ~LinuxSimpleIPC() {} + virtual bool init() { + return true; + } + virtual bool update(int64_t ml_phoffset, int64_t ls_phoffset, + int32_t ml_freqoffset, int32_t ls_freqoffset, + uint64_t local_time) { +#ifdef DEBUG + fprintf(stderr, + "Master-Local Phase Offset: %ld\n" + "Master-Local Frequency Offset: %d\n" + "Local-System Phase Offset: %ld\n" + "Local-System Frequency Offset: %d\n" + "Local Time: %lu\n", ml_phoffset, ml_freqoffset, + ls_phoffset, ls_freqoffset, local_time); +#endif + return true; + } +}; + +#endif diff --git a/daemons/gptp/linux/src/platform.hpp b/daemons/gptp/linux/src/platform.hpp new file mode 100644 index 0000000..eaf0654 --- /dev/null +++ b/daemons/gptp/linux/src/platform.hpp @@ -0,0 +1,46 @@ +/****************************************************************************** + + Copyright (c) 2012 Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef PLATFORM_HPP +#define PLATFORM_HPP + +#include + +#define PLAT_strncpy( dest, src, max ) strncpy( dest, src, max+1 ); + +#define PLAT_htons( s ) htons( s ) +#define PLAT_htonl( l ) htonl( l ) +#define PLAT_ntohs( s ) ntohs( s ) +#define PLAT_ntohl( l ) ntohl( l ) + +#endif diff --git a/daemons/gptp/windows/CVS/Entries b/daemons/gptp/windows/CVS/Entries new file mode 100644 index 0000000..68ce4fc --- /dev/null +++ b/daemons/gptp/windows/CVS/Entries @@ -0,0 +1,4 @@ +D/daemon_cl//// +D/x64//// +D/named_pipe_test//// +/gptp.sln/1.1/Fri Sep 21 16:52:20 2012/-kb/ diff --git a/daemons/gptp/windows/CVS/Repository b/daemons/gptp/windows/CVS/Repository new file mode 100644 index 0000000..819fa3a --- /dev/null +++ b/daemons/gptp/windows/CVS/Repository @@ -0,0 +1 @@ +linux_igb_avb/daemons/gptp/windows diff --git a/daemons/gptp/windows/CVS/Root b/daemons/gptp/windows/CVS/Root new file mode 100644 index 0000000..beb56f8 --- /dev/null +++ b/daemons/gptp/windows/CVS/Root @@ -0,0 +1 @@ +:pserver:ekmann@azusa.jf.intel.com:/home/cvsroot/ladsw diff --git a/daemons/gptp/windows/daemon_cl/CVS/Entries b/daemons/gptp/windows/daemon_cl/CVS/Entries new file mode 100644 index 0000000..fb41e46 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/CVS/Entries @@ -0,0 +1,15 @@ +D/x64//// +/ReadMe.txt/1.1/Fri Sep 21 20:19:03 2012// +/daemon_cl.cpp/1.1/Fri Sep 7 19:10:38 2012// +/daemon_cl.vcxproj/1.1/Fri Sep 21 16:50:01 2012// +/daemon_cl.vcxproj.filters/1.1/Fri Sep 21 16:50:23 2012// +/daemon_cl.vcxproj.user/1.1/Fri Sep 7 18:39:51 2012// +/ipcdef.hpp/1.1/Fri Sep 7 19:11:15 2012// +/packet.cpp/1.1/Fri Sep 7 19:10:51 2012// +/packet.hpp/1.1/Fri Sep 7 19:11:26 2012// +/platform.hpp/1.1/Fri Sep 7 19:11:37 2012// +/stdafx.cpp/1.1/Fri Sep 7 18:39:51 2012// +/stdafx.h/1.1/Fri Sep 7 18:39:51 2012// +/targetver.h/1.1/Fri Sep 7 18:39:51 2012// +/tsc.hpp/1.1/Fri Sep 21 16:58:33 2012// +/windows_hal.hpp/1.1/Fri Sep 21 16:53:58 2012// diff --git a/daemons/gptp/windows/daemon_cl/CVS/Repository b/daemons/gptp/windows/daemon_cl/CVS/Repository new file mode 100644 index 0000000..d3b6fa5 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/CVS/Repository @@ -0,0 +1 @@ +linux_igb_avb/daemons/gptp/windows/daemon_cl diff --git a/daemons/gptp/windows/daemon_cl/CVS/Root b/daemons/gptp/windows/daemon_cl/CVS/Root new file mode 100644 index 0000000..beb56f8 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/CVS/Root @@ -0,0 +1 @@ +:pserver:ekmann@azusa.jf.intel.com:/home/cvsroot/ladsw diff --git a/daemons/gptp/windows/daemon_cl/ReadMe.txt b/daemons/gptp/windows/daemon_cl/ReadMe.txt new file mode 100644 index 0000000..ae30f53 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/ReadMe.txt @@ -0,0 +1,56 @@ +======================================================================== +(C) Copyright 2009-2012 Intel Corporation, All Rights Reserved +Author: Christopher Hall +======================================================================== + +======================================================================== + CONSOLE APPLICATION : daemon_cl Project Overview +======================================================================== + +* Dependencies + + WinPCAP Developer's Pack is required for linking (WpdPack_*.zip) + WinPCAP must also be installed on any machine where the daemon runs (WinPcap_*.exe installer for windows) + +To run from the command line: + +daemon_cl.exe xx-xx-xx-xx-xx-xx + + where xx-xx-xx-xx-xx-xx is the mac address of the local interface + +* Terminology + + master - 802.1AS Grandmaster Clock + local - local network device clock (802.1AS timestamp source) + system - clock use elsewhere on a PC-like device, e.g. TSC or HPET timer + +* Interprocess Communication: + +The daemon communicates with other user processes through a named pipe. The pipe name and message format is defined in ipcdef.hpp. + +The pipe name is "gptp-update". An example is in the project named_pipe_test. + +The message format is: + + Integer64 + Integer64 + Integer32 + Integer32 + UInteger64 + +* Meaning of IPC provided values: + + master ~= local - + local ~= system - + Dmaster ~= Dlocal * (1-/1e12) + (where D denotes a delta rather than a specific value) + Dlocal ~= Dsystem * (1-/1e12) + (where D denotes a delta rather than a specific value) + +* Known Limitations: + + * There are problems with timestamp accuracy create problems using switches that impose limits on the peer rate offset + * The current driver version does not allow timestamping between the system clock (e.g. TCS) and the network device clock; + systems offsets are not valid + + diff --git a/daemons/gptp/windows/daemon_cl/daemon_cl.cpp b/daemons/gptp/windows/daemon_cl/daemon_cl.cpp new file mode 100644 index 0000000..146d0d2 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/daemon_cl.cpp @@ -0,0 +1,124 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + + +#include "ieee1588.hpp" +#include "avbts_clock.hpp" +#include "avbts_osnet.hpp" +#include "avbts_oslock.hpp" +#include "windows_hal.hpp" +#include + +#define MACSTR_LENGTH 17 + +static bool exit_flag; + +BOOL WINAPI ctrl_handler( DWORD ctrl_type ) { + bool ret; + if( ctrl_type == CTRL_C_EVENT ) { + exit_flag = true; + ret = true; + } else { + ret = false; + } + return ret; +} + +int parseMacAddr( _TCHAR *macstr, uint8_t *octet_string ) { + int i; + _TCHAR *cur = macstr; + + if( strnlen_s( macstr, MACSTR_LENGTH ) != 17 ) return -1; + + for( i = 0; i < ETHER_ADDR_OCTETS; ++i ) { + octet_string[i] = strtol( cur, &cur, 16 ) & 0xFF; + ++cur; + } + + return 0; +} + +int _tmain(int argc, _TCHAR* argv[]) +{ + bool force_slave = false; + int32_t offset = 0; + bool syntonize = false; + + // Register default network interface + WindowsPCAPNetworkInterfaceFactory *default_factory = new WindowsPCAPNetworkInterfaceFactory(); + OSNetworkInterfaceFactory::registerFactory( factory_name_t( "default" ), default_factory ); + + // Create thread, lock, timer, timerq factories + WindowsThreadFactory *thread_factory = new WindowsThreadFactory(); + WindowsTimerQueueFactory *timerq_factory = new WindowsTimerQueueFactory(); + WindowsLockFactory *lock_factory = new WindowsLockFactory(); + WindowsTimerFactory *timer_factory = new WindowsTimerFactory(); + WindowsConditionFactory *condition_factory = new WindowsConditionFactory(); + WindowsNamedPipeIPC *ipc = new WindowsNamedPipeIPC(); + if( !ipc->init() ) { + delete ipc; + ipc = NULL; + } + + // Create Low level network interface object + uint8_t local_addr_ostr[ETHER_ADDR_OCTETS]; + if( argc < 2 ) return -1; + parseMacAddr( argv[1], local_addr_ostr ); + LinkLayerAddress local_addr(local_addr_ostr); + + // Create Clock object + IEEE1588Clock *clock = new IEEE1588Clock( false, timerq_factory, ipc ); // Do not force slave + // Create HWTimestamper object + HWTimestamper *timestamper = new WindowsTimestamper(); + // Create Port Object linked to clock and low level + IEEE1588Port *port = new IEEE1588Port( clock, 1, false, timestamper, false, 0, &local_addr, + condition_factory, thread_factory, timer_factory, lock_factory ); + if( !port->init_port() ) { + printf( "Failed to initialize port\n" ); + return -1; + } + port->processEvent( POWERUP ); + + // Wait for Ctrl-C + if( !SetConsoleCtrlHandler( ctrl_handler, true )) { + printf( "Unable to register Ctrl-C handler\n" ); + return -1; + } + + while( !exit_flag ) Sleep( 1200 ); + + delete( ipc ); + + return 0; +} + diff --git a/daemons/gptp/windows/daemon_cl/daemon_cl.vcxproj b/daemons/gptp/windows/daemon_cl/daemon_cl.vcxproj new file mode 100644 index 0000000..9473646 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/daemon_cl.vcxproj @@ -0,0 +1,209 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {590D3055-A068-4B31-B4F9-B2ACC5F93663} + Win32Proj + daemon_cl + + + + Application + true + Unicode + v110 + + + Application + true + Unicode + v110 + + + Application + false + true + Unicode + v110 + + + Application + false + true + Unicode + v110 + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;WPCAP;HAVE_REMOTE + C:\Users\John\src\pcap\Include;C:\Users\John\src\gptp\common;%(AdditionalIncludeDirectories) + + + Console + true + wpcap.lib;Iphlpapi.lib;ws2_32.lib;%(AdditionalDependencies) + C:\Users\John\src\pcap\Lib;%(AdditionalLibraryDirectories) + + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;WPCAP;HAVE_REMOTE + C:\Users\John\src\pcap\Include;C:\Users\John\src\gptp\common;%(AdditionalIncludeDirectories) + + + Console + true + wpcap.lib;Iphlpapi.lib;ws2_32.lib;%(AdditionalDependencies) + C:\Users\John\src\pcap\Lib;%(AdditionalLibraryDirectories) + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;WPCAP;HAVE_REMOTE + C:\Users\John\src\pcap\Include;C:\Users\John\src\gptp\common;%(AdditionalIncludeDirectories) + + + Console + true + true + true + C:\Users\John\src\pcap\Lib;%(AdditionalLibraryDirectories) + wpcap.lib;Iphlpapi.lib;ws2_32.lib;%(AdditionalDependencies) + true + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;WPCAP;HAVE_REMOTE + C:\Users\John\src\pcap\Include;$(SolutionDir)\daemon_cl\;..\..\..\common;%(AdditionalIncludeDirectories) + + + Console + true + true + true + C:\Users\John\src\pcap\Lib\x64;%(AdditionalLibraryDirectories) + wpcap.lib;Iphlpapi.lib;ws2_32.lib;%(AdditionalDependencies) + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/daemons/gptp/windows/daemon_cl/daemon_cl.vcxproj.filters b/daemons/gptp/windows/daemon_cl/daemon_cl.vcxproj.filters new file mode 100644 index 0000000..09509ef --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/daemon_cl.vcxproj.filters @@ -0,0 +1,99 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/daemons/gptp/windows/daemon_cl/daemon_cl.vcxproj.user b/daemons/gptp/windows/daemon_cl/daemon_cl.vcxproj.user new file mode 100644 index 0000000..145a860 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/daemon_cl.vcxproj.user @@ -0,0 +1,11 @@ + + + + 88-88-88-88-87-88 + WindowsLocalDebugger + + + 00-13-20-F8-84-41 + WindowsLocalDebugger + + \ No newline at end of file diff --git a/daemons/gptp/windows/daemon_cl/ipcdef.hpp b/daemons/gptp/windows/daemon_cl/ipcdef.hpp new file mode 100644 index 0000000..4ac3e32 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/ipcdef.hpp @@ -0,0 +1,88 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + + +#ifndef IPCDEF_HPP +#define IPCDEF_HPP + +#include + +#define PIPE_PREFIX "\\\\.\\pipe\\" +#define P802_1AS_PIPENAME "gptp-update" +#define OUTSTANDING_MESSAGES 10 + +#pragma pack(push,1) + +class WindowsNPipeMessage { +private: + int64_t ml_phoffset; + int64_t ls_phoffset; + int32_t ml_freqoffset; + int32_t ls_freq_offset; + uint64_t local_time; +public: + WindowsNPipeMessage() {}; + WindowsNPipeMessage( int64_t ml_phoffset, int64_t ls_phoffset, int32_t ml_freqoffset, int32_t ls_freq_offset, uint64_t local_time ) { + this->ml_phoffset = ml_phoffset; + this->ls_phoffset = ls_phoffset; + this->ml_freqoffset = ml_freqoffset; + this->ls_freq_offset = ls_freq_offset; + this->local_time = local_time; + + } + bool write( HANDLE pipe ) { + DWORD bytes_written; + DWORD last_error = ERROR_SUCCESS; + if( WriteFile( pipe, this, sizeof(*this), &bytes_written, NULL ) == 0 ) { + last_error = GetLastError(); + } + if( last_error == ERROR_SUCCESS || last_error == ERROR_PIPE_LISTENING ) { + return true; + } + return false; + } + bool read( HANDLE pipe ) { + DWORD bytes_written; + if( ReadFile( pipe, this, sizeof(*this), &bytes_written, NULL ) == 0 ) return false; + return true; + } + int64_t getMasterLocalOffset() { return ml_phoffset; } + int32_t getMasterLocalFreqOffset() { return ml_freqoffset; } + int64_t getLocalSystemOffset() { return ls_phoffset; } + int32_t getLocalSystemFreqOffset() { return ls_freq_offset; } + uint64_t getLocalTime() { return local_time; } +}; + +#pragma pack(pop) + +#endif diff --git a/daemons/gptp/windows/daemon_cl/packet.cpp b/daemons/gptp/windows/daemon_cl/packet.cpp new file mode 100644 index 0000000..dea9138 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/packet.cpp @@ -0,0 +1,207 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + + +#include "packet.hpp" +#include "platform.hpp" + +#include +#include "iphlpapi.h" + +#define MAX_FRAME_SIZE 96 + +#define WINPCAP_INTERFACENAMEPREFIX "rpcap://\\Device\\NPF_" +#define WINPCAP_INTERFACENAMEPREFIX_LENGTH 20 +#define WINPCAP_INTERFACENAMESUFFIX_LENGTH 38 +#define WINPCAP_INTERFACENAME_LENGTH WINPCAP_INTERFACENAMEPREFIX_LENGTH+WINPCAP_INTERFACENAMESUFFIX_LENGTH + +struct packet_handle { + pcap_t *iface; + char errbuf[PCAP_ERRBUF_SIZE]; + packet_addr_t iface_addr; + HANDLE capture_lock; + uint16_t ethertype; + struct bpf_program filter; +}; + + +packet_error_t mallocPacketHandle( struct packet_handle **phandle ) { + packet_error_t ret = PACKET_NO_ERROR; + + packet_handle *handle = (struct packet_handle *) malloc((size_t) sizeof( *handle )); + if( handle == NULL ) { + ret = PACKET_NOMEMORY_ERROR; + goto fnexit; + } + *phandle = handle; + + if(( handle->capture_lock = CreateMutex( NULL, FALSE, NULL )) == NULL ) { + ret = PACKET_CREATEMUTEX_ERROR; + goto fnexit; + } + handle->iface = NULL; + +fnexit: + return ret; +} + +void freePacketHandle( struct packet_handle *handle ) { + CloseHandle( handle->capture_lock ); + free((void *) handle ); +} + +packet_error_t openInterfaceByAddr( struct packet_handle *handle, packet_addr_t *addr, int timeout ) { + packet_error_t ret = PACKET_NO_ERROR; + char name[WINPCAP_INTERFACENAME_LENGTH+1] = "\0"; + + PIP_ADAPTER_ADDRESSES pAdapterAddress; + IP_ADAPTER_ADDRESSES AdapterAddress[32]; // Allocate information for up to 32 NICs + DWORD dwBufLen = sizeof(AdapterAddress); // Save memory size of buffer + + DWORD dwStatus = GetAdaptersAddresses( AF_UNSPEC, 0, NULL, AdapterAddress, &dwBufLen); + + if( dwStatus != ERROR_SUCCESS ) { + ret = PACKET_IFLOOKUP_ERROR; + goto fnexit; + } + + for( pAdapterAddress = AdapterAddress; pAdapterAddress != NULL; pAdapterAddress = pAdapterAddress->Next ) { + if( pAdapterAddress->PhysicalAddressLength == ETHER_ADDR_OCTETS && + memcmp( pAdapterAddress->PhysicalAddress, addr->addr, ETHER_ADDR_OCTETS ) == 0 ) { + break; + } + } + + if( pAdapterAddress != NULL ) { + strcpy_s( name, WINPCAP_INTERFACENAMEPREFIX ); + strncpy_s( name+WINPCAP_INTERFACENAMEPREFIX_LENGTH, WINPCAP_INTERFACENAMESUFFIX_LENGTH+1, pAdapterAddress->AdapterName, WINPCAP_INTERFACENAMESUFFIX_LENGTH ); + printf( "Opening: %s\n", name ); + handle->iface = pcap_open( name, MAX_FRAME_SIZE, PCAP_OPENFLAG_MAX_RESPONSIVENESS | PCAP_OPENFLAG_PROMISCUOUS | PCAP_OPENFLAG_NOCAPTURE_LOCAL, + timeout, NULL, handle->errbuf ); + if( handle->iface == NULL ) { + ret = PACKET_IFLOOKUP_ERROR; + goto fnexit; + } + handle->iface_addr = *addr; + } else { + ret = PACKET_IFNOTFOUND_ERROR; + goto fnexit; + } + +fnexit: + return ret; +} + +void closeInterface( struct packet_handle *pfhandle ) { + if( pfhandle->iface != NULL ) pcap_close( pfhandle->iface ); +} + + +packet_error_t sendFrame( struct packet_handle *handle, packet_addr_t *addr, uint16_t ethertype, uint8_t *payload, size_t length ) { + packet_error_t ret = PACKET_NO_ERROR; + uint16_t ethertype_no = PLAT_htons( ethertype ); + uint8_t *payload_ptr = payload; + + if( length < PACKET_HDR_LENGTH ) { + ret = PACKET_BADBUFFER_ERROR; + } + + // Build Header + memcpy( payload_ptr, addr->addr, ETHER_ADDR_OCTETS ); payload_ptr+= ETHER_ADDR_OCTETS; + memcpy( payload_ptr, handle->iface_addr.addr, ETHER_ADDR_OCTETS ); payload_ptr+= ETHER_ADDR_OCTETS; + memcpy( payload_ptr, ðertype_no, sizeof( ethertype_no )); + + if( pcap_sendpacket( handle->iface, payload, (int) length+PACKET_HDR_LENGTH ) != 0 ) { + ret = PACKET_XMIT_ERROR; + goto fnexit; + } + +fnexit: + return ret; +} + +packet_error_t packetBind( struct packet_handle *handle, uint16_t ethertype ) { + packet_error_t ret = PACKET_NO_ERROR; + char filter_expression[32] = "ether proto 0x"; + + sprintf_s( filter_expression+strlen(filter_expression), 31-strlen(filter_expression), "%hx", ethertype ); + if( pcap_compile( handle->iface, &handle->filter, filter_expression, 1, 0 ) == -1 ) { + ret = PACKET_BIND_ERROR; + goto fnexit; + } + if( pcap_setfilter( handle->iface, &handle->filter ) == -1 ) { + ret = PACKET_BIND_ERROR; + goto fnexit; + } + + handle->ethertype = ethertype; + +fnexit: + return ret; +} + +// Call to recvFrame must be thread-safe. However call to pcap_next_ex() isn't because of somewhat undefined memory management semantics. +// Wrap call to pcap library with mutex +packet_error_t recvFrame( struct packet_handle *handle, packet_addr_t *addr, uint8_t *payload, size_t &length ) { + packet_error_t ret = PACKET_NO_ERROR; + struct pcap_pkthdr *hdr_r; + u_char *data; + + int pcap_result; + DWORD wait_result; + + wait_result = WaitForSingleObject( handle->capture_lock, 1000 ); + if( wait_result != WAIT_OBJECT_0 ) { + ret = PACKET_GETMUTEX_ERROR; + goto fnexit; + } + + pcap_result = pcap_next_ex( handle->iface, &hdr_r, (const u_char **) &data ); + if( pcap_result == 0 ) { + ret = PACKET_RECVTIMEOUT_ERROR; + } else if( pcap_result < 0 ) { + ret = PACKET_RECVFAILED_ERROR; + } else { + length = hdr_r->len-PACKET_HDR_LENGTH >= length ? length : hdr_r->len-PACKET_HDR_LENGTH; + memcpy( payload, data+PACKET_HDR_LENGTH, length ); + memcpy( addr->addr, data+ETHER_ADDR_OCTETS, ETHER_ADDR_OCTETS ); + } + + if( !ReleaseMutex( handle->capture_lock )) { + ret = PACKET_RLSMUTEX_ERROR; + goto fnexit; + } + +fnexit: + return ret; +} diff --git a/daemons/gptp/windows/daemon_cl/packet.hpp b/daemons/gptp/windows/daemon_cl/packet.hpp new file mode 100644 index 0000000..3e08051 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/packet.hpp @@ -0,0 +1,62 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + + +#ifndef PACKET_H +#define PACKET_H + +#include "stdint.h" + +#define ETHER_ADDR_OCTETS 6 +#define PACKET_HDR_LENGTH 14 + +typedef enum { + PACKET_NO_ERROR = 0, PACKET_NOMEMORY_ERROR, PACKET_BADBUFFER_ERROR, PACKET_XMIT_ERROR, PACKET_IFLOOKUP_ERROR, PACKET_IFNOTFOUND_ERROR, + PACKET_CREATEMUTEX_ERROR, PACKET_GETMUTEX_ERROR, PACKET_RLSMUTEX_ERROR, PACKET_RECVTIMEOUT_ERROR, PACKET_RECVFAILED_ERROR, + PACKET_BIND_ERROR +} packet_error_t; + +typedef struct { uint8_t addr[ETHER_ADDR_OCTETS]; } packet_addr_t; +typedef struct packet_handle * pfhandle_t; + +packet_error_t mallocPacketHandle( pfhandle_t *pfhandle_r ); +void freePacketHandle( pfhandle_t pfhandle ); + +packet_error_t openInterfaceByAddr( pfhandle_t pfhandle, packet_addr_t *addr, int timeout ); +void closeInterface( pfhandle_t pfhandle ); +packet_error_t sendFrame( pfhandle_t pfhandle, packet_addr_t *addr, uint16_t ethertype, uint8_t *payload, size_t length ); +packet_error_t recvFrame( pfhandle_t pfhandle, packet_addr_t *addr, uint8_t *payload, size_t &length ); + +packet_error_t packetBind( struct packet_handle *handle, uint16_t ethertype ); + +#endif /* PACKET_H */ diff --git a/daemons/gptp/windows/daemon_cl/platform.hpp b/daemons/gptp/windows/daemon_cl/platform.hpp new file mode 100644 index 0000000..bbd0fd0 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/platform.hpp @@ -0,0 +1,46 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef PLATFORM_HPP +#define PLATFORM_HPP + +#include + +#define PLAT_strncpy( dest, src, max ) strncpy_s( dest, max+1, src, _TRUNCATE ); + +#define PLAT_htons( s ) htons( s ) +#define PLAT_htonl( l ) htonl( l ) +#define PLAT_ntohs( s ) ntohs( s ) +#define PLAT_ntohl( l ) ntohl( l ) + +#endif diff --git a/daemons/gptp/windows/daemon_cl/stdafx.cpp b/daemons/gptp/windows/daemon_cl/stdafx.cpp new file mode 100644 index 0000000..a3275bf --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// daemon_cl.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/daemons/gptp/windows/daemon_cl/stdafx.h b/daemons/gptp/windows/daemon_cl/stdafx.h new file mode 100644 index 0000000..47a0d02 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/stdafx.h @@ -0,0 +1,15 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include +#include + + + +// TODO: reference additional headers your program requires here diff --git a/daemons/gptp/windows/daemon_cl/targetver.h b/daemons/gptp/windows/daemon_cl/targetver.h new file mode 100644 index 0000000..90e767b --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/daemons/gptp/windows/daemon_cl/tsc.hpp b/daemons/gptp/windows/daemon_cl/tsc.hpp new file mode 100644 index 0000000..9484a09 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/tsc.hpp @@ -0,0 +1,65 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef TSC_HPP +#define TSC_HPP + +#include +#include + +#define CPUFREQ_BASE 133 + +inline unsigned __int64 PLAT_rdtsc() +{ + return __rdtsc(); +} + +// 'millis' argument specifies time to measure TSC over. A longer time is generally more reliable +// Returns TSC frequency +inline uint64_t getTSCFrequency( unsigned millis ) { + uint64_t tsc1, tsc2, multiplier; + unsigned msig, lsig; + + tsc1 = PLAT_rdtsc(); + Sleep( millis ); + tsc2 = PLAT_rdtsc(); + multiplier = (unsigned) (tsc2 - tsc1)/133; + lsig = multiplier % 1000000; + msig = (unsigned) multiplier / 1000000; + if( lsig >= 750000 ) multiplier = (msig+1)*1000000; + else if( lsig < 250000 ) multiplier = msig*1000000; + else multiplier = msig*1000000 + 500000; + return multiplier*CPUFREQ_BASE; +} + +#endif/*TSC_HPP*/ diff --git a/daemons/gptp/windows/daemon_cl/windows_hal.hpp b/daemons/gptp/windows/daemon_cl/windows_hal.hpp new file mode 100644 index 0000000..fdb1172 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/windows_hal.hpp @@ -0,0 +1,576 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef WINDOWS_HAL_HPP +#define WINDOWS_HAL_HPP + +#include "avbts_osnet.hpp" +#include "avbts_oslock.hpp" +#include "avbts_oscondition.hpp" +#include "avbts_ostimerq.hpp" +#include "avbts_ostimer.hpp" +#include "packet.hpp" +#include "ieee1588.hpp" +#include "iphlpapi.h" +#include "ipcdef.hpp" +#include "tsc.hpp" + +#include "avbts_osipc.hpp" + +#include + +#include + +class WindowsPCAPNetworkInterface : public OSNetworkInterface { + friend class WindowsPCAPNetworkInterfaceFactory; +private: + pfhandle_t handle; + LinkLayerAddress local_addr; +public: + virtual net_result send( LinkLayerAddress *addr, uint8_t *payload, size_t length, bool timestamp ) { + packet_addr_t dest; + addr->toOctetArray( dest.addr ); + if( sendFrame( handle, &dest, PTP_ETHERTYPE, payload, length ) != PACKET_NO_ERROR ) return net_fatal; + return net_succeed; + } + virtual net_result recv( LinkLayerAddress *addr, uint8_t *payload, size_t &length ) { + packet_addr_t dest; + packet_error_t pferror = recvFrame( handle, &dest, payload, length ); + if( pferror != PACKET_NO_ERROR && pferror != PACKET_RECVTIMEOUT_ERROR ) return net_fatal; + if( pferror == PACKET_RECVTIMEOUT_ERROR ) return net_trfail; + *addr = LinkLayerAddress( dest.addr ); + return net_succeed; + } + virtual void getLinkLayerAddress( LinkLayerAddress *addr ) { + *addr = local_addr; + } + virtual unsigned getPayloadOffset() { + return PACKET_HDR_LENGTH; + } + virtual ~WindowsPCAPNetworkInterface() { + closeInterface( handle ); + if( handle != NULL ) freePacketHandle( handle ); + } +protected: + WindowsPCAPNetworkInterface() { handle = NULL; }; +}; + +class WindowsPCAPNetworkInterfaceFactory : public OSNetworkInterfaceFactory { +public: + virtual bool createInterface( OSNetworkInterface **net_iface, InterfaceLabel *label, HWTimestamper *timestamper ) { + WindowsPCAPNetworkInterface *net_iface_l = new WindowsPCAPNetworkInterface(); + LinkLayerAddress *addr = dynamic_cast(label); + if( addr == NULL ) goto error_nofree; + net_iface_l->local_addr = *addr; + packet_addr_t pfaddr; + addr->toOctetArray( pfaddr.addr ); + if( mallocPacketHandle( &net_iface_l->handle ) != PACKET_NO_ERROR ) goto error_nofree; + if( openInterfaceByAddr( net_iface_l->handle, &pfaddr, 1 ) != PACKET_NO_ERROR ) goto error_free_handle; + if( packetBind( net_iface_l->handle, PTP_ETHERTYPE ) != PACKET_NO_ERROR ) goto error_free_handle; + *net_iface = net_iface_l; + + return true; + +error_free_handle: +error_nofree: + delete net_iface_l; + + return false; + } +}; + +class WindowsLock : public OSLock { + friend class WindowsLockFactory; +private: + OSLockType type; + DWORD thread_id; + HANDLE lock_c; + OSLockResult lock_l( DWORD timeout ) { + DWORD wait_result = WaitForSingleObject( lock_c, timeout ); + if( wait_result == WAIT_TIMEOUT ) return oslock_held; + else if( wait_result == WAIT_OBJECT_0 ) return oslock_ok; + else return oslock_fail; + + } + OSLockResult nonreentrant_lock_l( DWORD timeout ) { + OSLockResult result; + DWORD wait_result; + wait_result = WaitForSingleObject( lock_c, timeout ); + if( wait_result == WAIT_OBJECT_0 ) { + if( thread_id == GetCurrentThreadId() ) { + result = oslock_self; + ReleaseMutex( lock_c ); + } else { + result = oslock_ok; + thread_id = GetCurrentThreadId(); + } + } else if( wait_result == WAIT_TIMEOUT ) result = oslock_held; + else result = oslock_fail; + + return result; + } +protected: + WindowsLock() { + lock_c = NULL; + } + bool initialize( OSLockType type ) { + lock_c = CreateMutex( NULL, false, NULL ); + if( lock_c == NULL ) return false; + this->type = type; + return true; + } + OSLockResult lock() { + if( type == oslock_recursive ) { + return lock_l( INFINITE ); + } + return nonreentrant_lock_l( INFINITE ); + } + OSLockResult trylock() { + if( type == oslock_recursive ) { + return lock_l( 0 ); + } + return nonreentrant_lock_l( 0 ); + } + OSLockResult unlock() { + ReleaseMutex( lock_c ); + return oslock_ok; + } +}; + +class WindowsLockFactory : public OSLockFactory { +public: + OSLock *createLock( OSLockType type ) { + WindowsLock *lock = new WindowsLock(); + if( !lock->initialize( type )) { + delete lock; + lock = NULL; + } + return lock; + } +}; + +class WindowsCondition : public OSCondition { + friend class WindowsConditionFactory; +private: + SRWLOCK lock; + CONDITION_VARIABLE condition; +protected: + bool initialize() { + InitializeSRWLock( &lock ); + InitializeConditionVariable( &condition ); + return true; + } +public: + bool wait_prelock() { + AcquireSRWLockExclusive( &lock ); + up(); + return true; + } + bool wait() { + BOOL result = SleepConditionVariableSRW( &condition, &lock, INFINITE, 0 ); + bool ret = false; + if( result == TRUE ) { + down(); + ReleaseSRWLockExclusive( &lock ); + ret = true; + } + return ret; + } + bool signal() { + AcquireSRWLockExclusive( &lock ); + if( waiting() ) WakeAllConditionVariable( &condition ); + ReleaseSRWLockExclusive( &lock ); + return true; + } +}; + +class WindowsConditionFactory : public OSConditionFactory { +public: + OSCondition *createCondition() { + WindowsCondition *result = new WindowsCondition(); + return result->initialize() ? result : NULL; + } +}; + +class WindowsTimerQueue; + +struct TimerQueue_t; + +struct WindowsTimerQueueHandlerArg { + HANDLE timer_handle; + HANDLE queue_handle; + event_descriptor_t *inner_arg; + ostimerq_handler func; + int type; + bool rm; + WindowsTimerQueue *queue; + TimerQueue_t *timer_queue; +}; + +typedef std::list TimerArgList_t; + +struct TimerQueue_t { + TimerArgList_t arg_list; + HANDLE queue_handle; + SRWLOCK lock; +}; + + +VOID CALLBACK WindowsTimerQueueHandler( PVOID arg_in, BOOLEAN ignore ); + +typedef std::map TimerQueueMap_t; + +class WindowsTimerQueue : public OSTimerQueue { + friend class WindowsTimerQueueFactory; + friend VOID CALLBACK WindowsTimerQueueHandler( PVOID arg_in, BOOLEAN ignore ); +private: + TimerQueueMap_t timerQueueMap; + TimerArgList_t retiredTimers; + SRWLOCK retiredTimersLock; + void cleanupRetiredTimers() { + AcquireSRWLockExclusive( &retiredTimersLock ); + while( !retiredTimers.empty() ) { + WindowsTimerQueueHandlerArg *retired_arg = retiredTimers.front(); + retiredTimers.pop_front(); + ReleaseSRWLockExclusive( &retiredTimersLock ); + DeleteTimerQueueTimer( retired_arg->queue_handle, retired_arg->timer_handle, INVALID_HANDLE_VALUE ); + if( retired_arg->rm ) delete retired_arg->inner_arg; + delete retired_arg; + AcquireSRWLockExclusive( &retiredTimersLock ); + } + ReleaseSRWLockExclusive( &retiredTimersLock ); + + } +protected: + WindowsTimerQueue() { + InitializeSRWLock( &retiredTimersLock ); + }; +public: + bool addEvent( unsigned long micros, int type, ostimerq_handler func, event_descriptor_t *arg, bool rm, unsigned *event ) { + WindowsTimerQueueHandlerArg *outer_arg = new WindowsTimerQueueHandlerArg(); + cleanupRetiredTimers(); + if( timerQueueMap.find(type) == timerQueueMap.end() ) { + timerQueueMap[type].queue_handle = CreateTimerQueue(); + InitializeSRWLock( &timerQueueMap[type].lock ); + } + outer_arg->queue_handle = timerQueueMap[type].queue_handle; + outer_arg->inner_arg = arg; + outer_arg->func = func; + outer_arg->queue = this; + outer_arg->type = type; + outer_arg->timer_queue = &timerQueueMap[type]; + AcquireSRWLockExclusive( &timerQueueMap[type].lock ); + CreateTimerQueueTimer( &outer_arg->timer_handle, timerQueueMap[type].queue_handle, WindowsTimerQueueHandler, (void *) outer_arg, micros/1000, 0, 0 ); + timerQueueMap[type].arg_list.push_front(outer_arg); + ReleaseSRWLockExclusive( &timerQueueMap[type].lock ); + return true; + } + bool cancelEvent( int type, unsigned *event ) { + TimerQueueMap_t::iterator iter = timerQueueMap.find( type ); + if( iter == timerQueueMap.end() ) return false; + AcquireSRWLockExclusive( &timerQueueMap[type].lock ); + while( ! timerQueueMap[type].arg_list.empty() ) { + WindowsTimerQueueHandlerArg *del_arg = timerQueueMap[type].arg_list.front(); + timerQueueMap[type].arg_list.pop_front(); + ReleaseSRWLockExclusive( &timerQueueMap[type].lock ); + DeleteTimerQueueTimer( del_arg->queue_handle, del_arg->timer_handle, INVALID_HANDLE_VALUE ); + if( del_arg->rm ) delete del_arg->inner_arg; + delete del_arg; + AcquireSRWLockExclusive( &timerQueueMap[type].lock ); + } + ReleaseSRWLockExclusive( &timerQueueMap[type].lock ); + + return true; + } +}; + +VOID CALLBACK WindowsTimerQueueHandler( PVOID arg_in, BOOLEAN ignore ) { + WindowsTimerQueueHandlerArg *arg = (WindowsTimerQueueHandlerArg *) arg_in; + arg->func( arg->inner_arg ); + + // Remove myself from unexpired timer queue + AcquireSRWLockExclusive( &arg->timer_queue->lock ); + arg->timer_queue->arg_list.remove( arg ); + ReleaseSRWLockExclusive( &arg->timer_queue->lock ); + + // Add myself to the expired timer queue + AcquireSRWLockExclusive( &arg->queue->retiredTimersLock ); + arg->queue->retiredTimers.push_front( arg ); + ReleaseSRWLockExclusive( &arg->queue->retiredTimersLock ); +} + +class WindowsTimerQueueFactory : public OSTimerQueueFactory { +public: + virtual OSTimerQueue *createOSTimerQueue() { + WindowsTimerQueue *timerq = new WindowsTimerQueue(); + return timerq; + }; +}; + +class WindowsTimer : public OSTimer { + friend class WindowsTimerFactory; +public: + virtual unsigned long sleep( unsigned long micros ) { + Sleep( micros/1000 ); + return micros; + } +protected: + WindowsTimer() {}; +}; + +class WindowsTimerFactory : public OSTimerFactory { +public: + virtual OSTimer *createTimer() { + return new WindowsTimer(); + } +}; + +struct OSThreadArg { + OSThreadFunction func; + void *arg; + OSThreadExitCode ret; +}; + +DWORD WINAPI OSThreadCallback( LPVOID input ) { + OSThreadArg *arg = (OSThreadArg*) input; + arg->ret = arg->func( arg->arg ); + return 0; +} + +class WindowsThread : public OSThread { + friend class WindowsThreadFactory; +private: + HANDLE thread_id; + OSThreadArg *arg_inner; +public: + virtual bool start( OSThreadFunction function, void *arg ) { + arg_inner = new OSThreadArg(); + arg_inner->func = function; + arg_inner->arg = arg; + thread_id = CreateThread( NULL, 0, OSThreadCallback, arg_inner, 0, NULL ); + if( thread_id == NULL ) return false; + else return true; + } + virtual bool join( OSThreadExitCode &exit_code ) { + if( WaitForSingleObject( thread_id, INFINITE ) != WAIT_OBJECT_0 ) return false; + exit_code = arg_inner->ret; + delete arg_inner; + return true; + } +protected: + WindowsThread() {}; +}; + +class WindowsThreadFactory : public OSThreadFactory { +public: + OSThread *createThread() { + return new WindowsThread(); + } +}; + +#define NETCLOCK_HZ 1056000000 +#define ONE_WAY_PHY_DELAY 8000 + +#define NETWORK_CARD_ID_PREFIX "\\\\.\\" +#define OID_INTEL_GET_RXSTAMP 0xFF020264 +#define OID_INTEL_GET_TXSTAMP 0xFF020263 +#define OID_INTEL_GET_SYSTIM 0xFF020262 +#define OID_INTEL_SET_SYSTIM 0xFF020261 + +class WindowsTimestamper : public HWTimestamper { +private: + // No idea whether the underlying implementation is thread safe + HANDLE miniport; + LARGE_INTEGER tsc_hz; + DWORD readOID( NDIS_OID oid, void *output_buffer, DWORD size, DWORD *size_returned ) { + NDIS_OID oid_l = oid; + DWORD rc = DeviceIoControl( + miniport, + IOCTL_NDIS_QUERY_GLOBAL_STATS, + &oid_l, + sizeof(oid_l), + output_buffer, + size, + size_returned, + NULL ); + if( rc == 0 ) return GetLastError(); + return ERROR_SUCCESS; + } + Timestamp nanoseconds64ToTimestamp( uint64_t time ) { + Timestamp timestamp; + timestamp.nanoseconds = time % 1000000000; + timestamp.seconds_ls = (time / 1000000000) & 0xFFFFFFFF; + timestamp.seconds_ms = (uint16_t)((time / 1000000000) >> 32); + return timestamp; + } + uint64_t scaleNativeClockToNanoseconds( uint64_t time ) { + long double scaled_output = ((long double)NETCLOCK_HZ)/1000000000; + scaled_output = ((long double) time)/scaled_output; + return (uint64_t) scaled_output; + } + uint64_t scaleTSCClockToNanoseconds( uint64_t time ) { + long double scaled_output = ((long double)tsc_hz.QuadPart)/1000000000; + scaled_output = ((long double) time)/scaled_output; + return (uint64_t) scaled_output; + } +public: + virtual bool HWTimestamper_init( InterfaceLabel *iface_label ) { + char network_card_id[64]; + LinkLayerAddress *addr = dynamic_cast(iface_label); + if( addr == NULL ) return false; + PIP_ADAPTER_ADDRESSES pAdapterAddress; + IP_ADAPTER_ADDRESSES AdapterAddress[32]; // Allocate information for up to 32 NICs + DWORD dwBufLen = sizeof(AdapterAddress); // Save memory size of buffer + + DWORD dwStatus = GetAdaptersAddresses( AF_UNSPEC, 0, NULL, AdapterAddress, &dwBufLen); + if( dwStatus != ERROR_SUCCESS ) return false; + + for( pAdapterAddress = AdapterAddress; pAdapterAddress != NULL; pAdapterAddress = pAdapterAddress->Next ) { + if( pAdapterAddress->PhysicalAddressLength == ETHER_ADDR_OCTETS && *addr == LinkLayerAddress( pAdapterAddress->PhysicalAddress )) { + break; + } + } + + if( pAdapterAddress == NULL ) return false; + + PLAT_strncpy( network_card_id, NETWORK_CARD_ID_PREFIX, 63 ); + PLAT_strncpy( network_card_id+strlen(network_card_id), pAdapterAddress->AdapterName, 63-strlen(network_card_id) ); + + miniport = CreateFile( network_card_id, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL ); + if( miniport == INVALID_HANDLE_VALUE ) return false; + + tsc_hz.QuadPart = getTSCFrequency( 1000 ); + + return true; + } + virtual bool HWTimestamper_gettime( Timestamp *system_time, Timestamp *device_time, uint32_t *local_clock, uint32_t *nominal_clock_rate ) + { + DWORD buf[6]; + DWORD returned; + uint64_t now_net, now_tsc; + DWORD result; + + memset( buf, 0xFF, sizeof( buf )); + if(( result = readOID( OID_INTEL_GET_SYSTIM, buf, sizeof(buf), &returned )) != ERROR_SUCCESS ) return false; + + now_net = (((uint64_t)buf[1]) << 32) | buf[0]; + now_net = scaleNativeClockToNanoseconds( now_net ); + *device_time = nanoseconds64ToTimestamp( now_net ); + + now_tsc = (((uint64_t)buf[3]) << 32) | buf[2]; + now_tsc = scaleTSCClockToNanoseconds( now_tsc ); + *system_time = nanoseconds64ToTimestamp( now_tsc ); + + return true; + } + + virtual int HWTimestamper_txtimestamp( PortIdentity *identity, uint16_t sequenceId, Timestamp ×tamp, unsigned &clock_value, bool last ) + { + DWORD buf[8], buf_tmp[8]; + DWORD returned = 0; + uint64_t tx_r,tx_s; + DWORD result; + while(( result = readOID( OID_INTEL_GET_TXSTAMP, buf_tmp, sizeof(buf_tmp), &returned )) == ERROR_SUCCESS ) { + memcpy( buf, buf_tmp, sizeof( buf )); + } + if( result != ERROR_GEN_FAILURE ) return -1; + if( returned == 0 ) return -72; + tx_r = (((uint64_t)buf[1]) << 32) | buf[0]; + tx_s = scaleNativeClockToNanoseconds( tx_r ); + tx_s += ONE_WAY_PHY_DELAY; + timestamp = nanoseconds64ToTimestamp( tx_s ); + //fprintf( stderr, "@@@SI,%hu,%u\n", sequenceId, buf[3] ); + //fprintf( stderr, "@@TXR,%u,%u,%u,%hu,%llu,%hu,%hu\n", timestamp.seconds_ms, timestamp.seconds_ls, + // timestamp.nanoseconds, 0, tx_r, sequenceId, buf[3] ); + + return 0; + } + + virtual int HWTimestamper_rxtimestamp( PortIdentity *identity, uint16_t sequenceId, Timestamp ×tamp, unsigned &clock_value, bool last ) + { + DWORD buf[4], buf_tmp[4]; + DWORD returned; + uint64_t rx_r,rx_s; + DWORD result; + uint16_t packet_sequence_id; + while(( result = readOID( OID_INTEL_GET_RXSTAMP, buf_tmp, sizeof(buf_tmp), &returned )) == ERROR_SUCCESS ) { + memcpy( buf, buf_tmp, sizeof( buf )); + } + if( result != ERROR_GEN_FAILURE ) return -1; + if( returned == 0 ) return -72; + packet_sequence_id = *((uint32_t *) buf+3) >> 16; + if( PLAT_ntohs( packet_sequence_id ) != sequenceId ) return -72; + rx_r = (((uint64_t)buf[1]) << 32) | buf[0]; + rx_s = scaleNativeClockToNanoseconds( rx_r ); + rx_s -= ONE_WAY_PHY_DELAY; + timestamp = nanoseconds64ToTimestamp( rx_s ); + //fprintf( stderr, "@@RXR,%u,%u,%u,%hu,%llu\n", timestamp.seconds_ms, timestamp.seconds_ls, + // timestamp.nanoseconds, PLAT_ntohs(packet_sequence_id), rx_r ); + + return 0; + } +}; + + + +class WindowsNamedPipeIPC : public OS_IPC { +private: + HANDLE pipe; +public: + WindowsNamedPipeIPC() { }; + ~WindowsNamedPipeIPC() { + CloseHandle( pipe ); + } + virtual bool init() { + char pipename[64]; + PLAT_strncpy( pipename, PIPE_PREFIX, 63 ); + PLAT_strncpy( pipename+strlen(pipename), P802_1AS_PIPENAME, 63-strlen(pipename) ); + pipe = CreateNamedPipe( pipename, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE, PIPE_UNLIMITED_INSTANCES, + OUTSTANDING_MESSAGES*sizeof( WindowsNPipeMessage ), 0, 0, NULL ); + if( pipe == INVALID_HANDLE_VALUE ) return false; + return true; + } + virtual bool update( int64_t ml_phoffset, int64_t ls_phoffset, int32_t ml_freqoffset, int32_t ls_freq_offset, uint64_t local_time ) { + WindowsNPipeMessage msg( ml_phoffset, ls_phoffset, ml_freqoffset, ls_freq_offset, local_time ); + if( !msg.write( pipe )) { + CloseHandle(pipe); + return init(); + } + return true; + } +}; + +#endif diff --git a/daemons/gptp/windows/daemon_cl/x64/CVS/Entries b/daemons/gptp/windows/daemon_cl/x64/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/x64/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/daemons/gptp/windows/daemon_cl/x64/CVS/Repository b/daemons/gptp/windows/daemon_cl/x64/CVS/Repository new file mode 100644 index 0000000..0120903 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/x64/CVS/Repository @@ -0,0 +1 @@ +linux_igb_avb/daemons/gptp/windows/daemon_cl/x64 diff --git a/daemons/gptp/windows/daemon_cl/x64/CVS/Root b/daemons/gptp/windows/daemon_cl/x64/CVS/Root new file mode 100644 index 0000000..beb56f8 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/x64/CVS/Root @@ -0,0 +1 @@ +:pserver:ekmann@azusa.jf.intel.com:/home/cvsroot/ladsw diff --git a/daemons/gptp/windows/gptp.sln b/daemons/gptp/windows/gptp.sln new file mode 100644 index 0000000..15e2131 --- /dev/null +++ b/daemons/gptp/windows/gptp.sln @@ -0,0 +1,21 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "daemon_cl", "daemon_cl\daemon_cl.vcxproj", "{590D3055-A068-4B31-B4F9-B2ACC5F93663}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "named_pipe_test", "named_pipe_test\named_pipe_test.vcxproj", "{303FACBB-2A44-4511-A855-2B5B2C0E3A89}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {590D3055-A068-4B31-B4F9-B2ACC5F93663}.Release|x64.ActiveCfg = Release|x64 + {590D3055-A068-4B31-B4F9-B2ACC5F93663}.Release|x64.Build.0 = Release|x64 + {303FACBB-2A44-4511-A855-2B5B2C0E3A89}.Release|x64.ActiveCfg = Release|x64 + {303FACBB-2A44-4511-A855-2B5B2C0E3A89}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/daemons/gptp/windows/named_pipe_test/CVS/Entries b/daemons/gptp/windows/named_pipe_test/CVS/Entries new file mode 100644 index 0000000..95ee666 --- /dev/null +++ b/daemons/gptp/windows/named_pipe_test/CVS/Entries @@ -0,0 +1,9 @@ +/ReadMe.txt/1.1/Fri Sep 21 20:20:49 2012// +/named_pipe_test.cpp/1.1/Fri Sep 21 20:21:47 2012// +/named_pipe_test.vcxproj/1.1/Fri Sep 21 16:52:35 2012// +/named_pipe_test.vcxproj.filters/1.1/Fri Sep 7 18:39:51 2012// +/named_pipe_test.vcxproj.user/1.1/Fri Sep 7 18:39:51 2012// +/stdafx.cpp/1.1/Fri Sep 7 18:39:51 2012// +/stdafx.h/1.1/Fri Sep 7 18:39:51 2012// +/targetver.h/1.1/Fri Sep 7 18:39:51 2012// +D diff --git a/daemons/gptp/windows/named_pipe_test/CVS/Repository b/daemons/gptp/windows/named_pipe_test/CVS/Repository new file mode 100644 index 0000000..2e82842 --- /dev/null +++ b/daemons/gptp/windows/named_pipe_test/CVS/Repository @@ -0,0 +1 @@ +linux_igb_avb/daemons/gptp/windows/named_pipe_test diff --git a/daemons/gptp/windows/named_pipe_test/CVS/Root b/daemons/gptp/windows/named_pipe_test/CVS/Root new file mode 100644 index 0000000..beb56f8 --- /dev/null +++ b/daemons/gptp/windows/named_pipe_test/CVS/Root @@ -0,0 +1 @@ +:pserver:ekmann@azusa.jf.intel.com:/home/cvsroot/ladsw diff --git a/daemons/gptp/windows/named_pipe_test/ReadMe.txt b/daemons/gptp/windows/named_pipe_test/ReadMe.txt new file mode 100644 index 0000000..ad4c1ba --- /dev/null +++ b/daemons/gptp/windows/named_pipe_test/ReadMe.txt @@ -0,0 +1,10 @@ +======================================================================== +(C) Copyright 2009-2012 Intel Corporation, All Rights Reserved +Author: Christopher Hall +======================================================================== + +======================================================================== + CONSOLE APPLICATION : named_pipe_test Project Overview +======================================================================== + +An example application to interface with gptp/daemon_cl. See ipcdef.hpp for message details. diff --git a/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp b/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp new file mode 100644 index 0000000..2247cba --- /dev/null +++ b/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp @@ -0,0 +1,114 @@ +/****************************************************************************** + + Copyright (c) 2009-2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#include "stdafx.h" +#include "ipcdef.hpp" +#include "tsc.hpp" + +#define PIPE_PREFIX "\\\\.\\pipe\\" +#define P802_1AS_PIPENAME "gptp-update" +#define OUTSTANDING_MESSAGES 10 + +static bool exit_flag; + +BOOL WINAPI ctrl_handler( DWORD ctrl_type ) { + bool ret; + if( ctrl_type == CTRL_C_EVENT ) { + exit_flag = true; + ret = true; + } else { + ret = false; + } + return ret; +} + +uint64_t scaleTSCClockToNanoseconds( uint64_t tsc_value, uint64_t tsc_frequency ) { + long double scaled_output = ((long double)tsc_frequency)/1000000000; + scaled_output = ((long double) tsc_value)/scaled_output; + return (uint64_t) scaled_output; +} +int _tmain(int argc, _TCHAR* argv[]) +{ + char pipename[64]; + strcpy_s( pipename, 64, PIPE_PREFIX ); + strcat_s( pipename, 64-strlen(pipename), P802_1AS_PIPENAME ); + WindowsNPipeMessage msg; + HANDLE pipe; + uint64_t tsc_frequency = getTSCFrequency( 1000 ); + + pipe = CreateFile( pipename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + if( pipe == INVALID_HANDLE_VALUE ) { + printf( "Failed to open gptp handle, %d\n", GetLastError() ); + } + + + // Wait for Ctrl-C + if( !SetConsoleCtrlHandler( ctrl_handler, true )) { + printf( "Unable to register Ctrl-C handler\n" ); + return -1; + } + + printf( "TSC Frequency: %llu\n", tsc_frequency ); + while( msg.read( pipe ) == true && !exit_flag ) { + uint64_t now_tscns, now_8021as; + uint64_t update_tscns, update_8021as; + unsigned delta_tscns, delta_8021as; + long double ml_ratio, ls_ratio; +#if 0 + printf( "Master-Local Offset = %lld\n", msg.getMasterLocalOffset() ); + printf( "Master-Local Frequency Offset = %d\n", msg.getMasterLocalFreqOffset() ); + printf( "Local-System Offset = %lld\n", msg.getLocalSystemOffset() ); + printf( "Local-System Frequency Offset = %d\n", msg.getLocalSystemFreqOffset() ); + printf( "Local Time = %llu\n", msg.getLocalTime() ); +#endif + now_tscns = scaleTSCClockToNanoseconds( PLAT_rdtsc(), tsc_frequency ); + update_tscns = msg.getLocalTime() + msg.getLocalSystemOffset(); + update_8021as = msg.getLocalTime() - msg.getMasterLocalOffset(); + delta_tscns = (unsigned)(now_tscns - update_tscns); + ml_ratio = -1*(((long double)msg.getMasterLocalFreqOffset())/1000000000000)+1; + ls_ratio = -1*(((long double)msg.getLocalSystemFreqOffset())/1000000000000)+1; + delta_8021as = (unsigned)(ml_ratio*ls_ratio*delta_tscns); + now_8021as = update_8021as + delta_8021as; + printf( "Time now in terms of TSC scaled to nanoseconds time: %llu\n", now_tscns ); + printf( "Last update time in terms of 802.1AS time: %llu\n", update_8021as ); + printf( "TSC delta scaled to ns: %u\n", delta_tscns ); + printf( "8021as delta scaled: %u\n", delta_8021as ); + printf( "Time now in terms of 802.1AS time: %llu\n", now_8021as ); + } + + printf( "Closing pipe\n" ); + CloseHandle( pipe ); + + return 0; +} + diff --git a/daemons/gptp/windows/named_pipe_test/named_pipe_test.vcxproj b/daemons/gptp/windows/named_pipe_test/named_pipe_test.vcxproj new file mode 100644 index 0000000..05b568f --- /dev/null +++ b/daemons/gptp/windows/named_pipe_test/named_pipe_test.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {303FACBB-2A44-4511-A855-2B5B2C0E3A89} + Win32Proj + named_pipe_test + + + + Application + true + Unicode + v110 + + + Application + true + Unicode + v110 + + + Application + false + true + Unicode + v110 + + + Application + false + true + Unicode + v110 + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Level3 + Disabled + + + C:\Users\John\src\gptp\windows\gptp\daemon_cl;%(AdditionalIncludeDirectories) + + + Console + true + + + + + Use + Level3 + Disabled + + + C:\Users\John\src\gptp\windows\gptp\daemon_cl;%(AdditionalIncludeDirectories) + + + Console + true + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE + C:\Users\John\src\gptp\windows\gptp\daemon_cl;%(AdditionalIncludeDirectories) + + + Console + true + true + true + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE + ..\daemon_cl;%(AdditionalIncludeDirectories) + + + Console + true + true + true + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/daemons/gptp/windows/named_pipe_test/named_pipe_test.vcxproj.filters b/daemons/gptp/windows/named_pipe_test/named_pipe_test.vcxproj.filters new file mode 100644 index 0000000..c03b555 --- /dev/null +++ b/daemons/gptp/windows/named_pipe_test/named_pipe_test.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/daemons/gptp/windows/named_pipe_test/named_pipe_test.vcxproj.user b/daemons/gptp/windows/named_pipe_test/named_pipe_test.vcxproj.user new file mode 100644 index 0000000..695b5c7 --- /dev/null +++ b/daemons/gptp/windows/named_pipe_test/named_pipe_test.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/daemons/gptp/windows/named_pipe_test/stdafx.cpp b/daemons/gptp/windows/named_pipe_test/stdafx.cpp new file mode 100644 index 0000000..26915d0 --- /dev/null +++ b/daemons/gptp/windows/named_pipe_test/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// named_pipe_test.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/daemons/gptp/windows/named_pipe_test/stdafx.h b/daemons/gptp/windows/named_pipe_test/stdafx.h new file mode 100644 index 0000000..d2509e4 --- /dev/null +++ b/daemons/gptp/windows/named_pipe_test/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include +#include +#include + + + +// TODO: reference additional headers your program requires here diff --git a/daemons/gptp/windows/named_pipe_test/targetver.h b/daemons/gptp/windows/named_pipe_test/targetver.h new file mode 100644 index 0000000..90e767b --- /dev/null +++ b/daemons/gptp/windows/named_pipe_test/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/daemons/gptp/windows/named_pipe_test/x64/Release/named_pipe_test.Build.CppClean.log b/daemons/gptp/windows/named_pipe_test/x64/Release/named_pipe_test.Build.CppClean.log new file mode 100644 index 0000000..b1d446f --- /dev/null +++ b/daemons/gptp/windows/named_pipe_test/x64/Release/named_pipe_test.Build.CppClean.log @@ -0,0 +1,16 @@ +\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\x64\Release\cl.command.1.tlog +\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\x64\Release\CL.read.1.tlog +\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\x64\Release\CL.write.1.tlog +\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\x64\Release\link.command.1.tlog +\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\x64\Release\link.read.1.tlog +\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\x64\Release\link.write.1.tlog +\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\x64\Release\mt.command.1.tlog +\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\x64\Release\mt.read.1.tlog +\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\x64\Release\mt.write.1.tlog +\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\x64\Release\named_pipe_test.exe.intermediate.manifest +\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\x64\Release\named_pipe_test.obj +\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\x64\Release\named_pipe_test.pch +\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\x64\Release\stdafx.obj +\\DATAGROVEJF\STG\USERS\CHRISH\GPTP_BZR\MAIN\WINDOWS\GPTP\NAMED_PIPE_TEST\X64\RELEASE\VC100.PDB +\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\x64\Release\named_pipe_test.exe +\\DATAGROVEJF\STG\USERS\CHRISH\GPTP_BZR\MAIN\WINDOWS\GPTP\X64\RELEASE\NAMED_PIPE_TEST.PDB diff --git a/daemons/gptp/windows/named_pipe_test/x64/Release/named_pipe_test.log b/daemons/gptp/windows/named_pipe_test/x64/Release/named_pipe_test.log new file mode 100644 index 0000000..d644b10 --- /dev/null +++ b/daemons/gptp/windows/named_pipe_test/x64/Release/named_pipe_test.log @@ -0,0 +1,9 @@ +Build started 9/5/2012 6:17:28 PM. + 1>Project "\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\named_pipe_test.vcxproj" on node 2 (clean target(s)). + 1>_PrepareForClean: + Deleting file "x64\Release\named_pipe_test.lastbuildstate". + 1>Done Building Project "\\datagrovejf\STG\USERS\Chrish\gptp_bzr\main\windows\gptp\named_pipe_test\named_pipe_test.vcxproj" (clean target(s)). + +Build succeeded. + +Time Elapsed 00:00:00.30 diff --git a/daemons/gptp/windows/x64/CVS/Entries b/daemons/gptp/windows/x64/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/daemons/gptp/windows/x64/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/daemons/gptp/windows/x64/CVS/Repository b/daemons/gptp/windows/x64/CVS/Repository new file mode 100644 index 0000000..76c2143 --- /dev/null +++ b/daemons/gptp/windows/x64/CVS/Repository @@ -0,0 +1 @@ +linux_igb_avb/daemons/gptp/windows/x64 diff --git a/daemons/gptp/windows/x64/CVS/Root b/daemons/gptp/windows/x64/CVS/Root new file mode 100644 index 0000000..beb56f8 --- /dev/null +++ b/daemons/gptp/windows/x64/CVS/Root @@ -0,0 +1 @@ +:pserver:ekmann@azusa.jf.intel.com:/home/cvsroot/ladsw diff --git a/daemons/gptp/windows/x64/Release/daemon_cl.map b/daemons/gptp/windows/x64/Release/daemon_cl.map new file mode 100644 index 0000000..0325541 --- /dev/null +++ b/daemons/gptp/windows/x64/Release/daemon_cl.map @@ -0,0 +1,732 @@ + daemon_cl + + Timestamp is 5047f856 (Wed Sep 05 18:11:50 2012) + + Preferred load address is 0000000140000000 + + Start Length Name Class + 0001:00000000 00009f5eH .text CODE + 0001:00009f60 0000040cH .text$x CODE + 0001:0000a370 000000b2H .text$yc CODE + 0001:0000a430 0000005fH .text$yd CODE + 0002:00000000 00000328H .idata$5 DATA + 0002:00000328 00000008H .CRT$XCA DATA + 0002:00000330 00000008H .CRT$XCAA DATA + 0002:00000338 00000018H .CRT$XCU DATA + 0002:00000350 00000008H .CRT$XCZ DATA + 0002:00000358 00000008H .CRT$XIA DATA + 0002:00000360 00000008H .CRT$XIAA DATA + 0002:00000368 00000008H .CRT$XIY DATA + 0002:00000370 00000008H .CRT$XIZ DATA + 0002:00000380 00001020H .rdata DATA + 0002:000013a0 0000006cH .rdata$debug DATA + 0002:00001410 0000113cH .rdata$r DATA + 0002:00002550 00000008H .rtc$IAA DATA + 0002:00002558 00000008H .rtc$IZZ DATA + 0002:00002560 00000008H .rtc$TAA DATA + 0002:00002568 00000008H .rtc$TZZ DATA + 0002:00002570 000011e0H .xdata DATA + 0002:00003750 00000084H .xdata$x DATA + 0002:000037d4 00000078H .idata$2 DATA + 0002:0000384c 00000014H .idata$3 DATA + 0002:00003860 00000328H .idata$4 DATA + 0002:00003b88 000006daH .idata$6 DATA + 0002:00004262 00000000H .edata DATA + 0003:00000000 00000714H .data DATA + 0003:00000720 00000600H .bss DATA + 0004:00000000 000009e4H .pdata DATA + + Address Publics by Value Rva+Base Lib:Object + + 0000:00000000 ___safe_se_handler_count 0000000000000000 + 0000:00000000 ___safe_se_handler_table 0000000000000000 + 0000:00000000 __ImageBase 0000000140000000 + 0001:00000000 ??1bad_alloc@std@@UEAA@XZ 0000000140001000 f i avbts_osnet.obj + 0001:00000020 ??_Gbad_alloc@std@@UEAAPEAXI@Z 0000000140001020 f i avbts_osnet.obj + 0001:00000020 ??_Ebad_alloc@std@@UEAAPEAXI@Z 0000000140001020 f i CIL library: CIL module + 0001:00000060 ??0?$_Tree@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@QEAA@AEBU?$less@Vfactory_name_t@@@1@AEBV?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@1@@Z 0000000140001060 f i avbts_osnet.obj + 0001:00000100 ??1?$_Tree_val@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@QEAA@XZ 0000000140001100 f i avbts_osnet.obj + 0001:00000110 ?erase@?$_Tree@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@QEAA?AV?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@@2@V?$_Tree_const_iterator@V?$_Tree_val@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@@2@0@Z 0000000140001110 f i avbts_osnet.obj + 0001:000001d0 ?erase@?$_Tree@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@QEAA?AV?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@@2@V?$_Tree_const_iterator@V?$_Tree_val@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@@2@@Z 00000001400011d0 f i avbts_osnet.obj + 0001:00000520 ?clear@?$_Tree@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@QEAAXXZ 0000000140001520 f i avbts_osnet.obj + 0001:000005a0 ?_Erase@?$_Tree@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@IEAAXPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@2@@Z 00000001400015a0 f i avbts_osnet.obj + 0001:00000600 ?_Lrotate@?$_Tree@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@IEAAXPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@2@@Z 0000000140001600 f i avbts_osnet.obj + 0001:00000660 ?_Rrotate@?$_Tree@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@IEAAXPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@2@@Z 0000000140001660 f i avbts_osnet.obj + 0001:000006c0 ??0bad_alloc@std@@QEAA@AEBV01@@Z 00000001400016c0 f i avbts_osnet.obj + 0001:000006f0 ?timerq_handler@@YAXPEAX@Z 00000001400016f0 f ieee1588clock.obj + 0001:00000700 ?addEventTimer@IEEE1588Clock@@QEAAXPEAVIEEE1588Port@@W4Event@@_K@Z 0000000140001700 f ieee1588clock.obj + 0001:000007a0 ?isBetterThan@IEEE1588Clock@@QEAA_NPEAVPTPMessageAnnounce@@@Z 00000001400017a0 f ieee1588clock.obj + 0001:00000870 ??_EInterfaceLabel@@UEAAPEAXI@Z 0000000140001870 f i CIL library: CIL module + 0001:00000870 ??_GLinkLayerAddress@@UEAAPEAXI@Z 0000000140001870 f i ieee1588port.obj + 0001:00000870 ??_ELinkLayerAddress@@UEAAPEAXI@Z 0000000140001870 f i CIL library: CIL module + 0001:00000870 ??_GInterfaceLabel@@UEAAPEAXI@Z 0000000140001870 f i ieee1588port.obj + 0001:000008a0 ?pow@@YANNH@Z 00000001400018a0 f i ieee1588port.obj + 0001:000008e0 ??1LinkLayerAddress@@UEAA@XZ 00000001400018e0 f i ieee1588port.obj + 0001:000008f0 ?openPortWrapper@@YA?AW4OSThreadExitCode@@PEAX@Z 00000001400018f0 f ieee1588port.obj + 0001:00000910 ??1?$map@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@@std@@QEAA@XZ 0000000140001910 f i ieee1588port.obj + 0001:00000950 ??0IEEE1588Port@@QEAA@PEAVIEEE1588Clock@@G_NPEAVHWTimestamper@@1HPEAVInterfaceLabel@@PEAVOSConditionFactory@@PEAVOSThreadFactory@@PEAVOSTimerFactory@@PEAVOSLockFactory@@@Z 0000000140001950 f ieee1588port.obj + 0001:00000b40 ?init_port@IEEE1588Port@@QEAA_NXZ 0000000140001b40 f ieee1588port.obj + 0001:00000cc0 ?openPort@IEEE1588Port@@QEAAPEAXXZ 0000000140001cc0 f ieee1588port.obj + 0001:00000e20 ?processEvent@IEEE1588Port@@QEAAXW4Event@@@Z 0000000140001e20 f ieee1588port.obj + 0001:00001cc0 ?calculateERBest@IEEE1588Port@@QEAAPEAVPTPMessageAnnounce@@XZ 0000000140002cc0 f ieee1588port.obj + 0001:00001d90 ?recommendState@IEEE1588Port@@QEAAXW4PortState@@_N@Z 0000000140002d90 f ieee1588port.obj + 0001:00001fa0 ?calcMasterLocalClockRateDifference@IEEE1588Port@@QEAAH_JVTimestamp@@@Z 0000000140002fa0 f ieee1588port.obj + 0001:000020e0 ?calcLocalSystemClockRateDifference@IEEE1588Port@@QEAAH_JVTimestamp@@@Z 00000001400030e0 f ieee1588port.obj + 0001:000021b0 ??A?$map@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@@std@@QEAAAEAPEAVOSNetworkInterfaceFactory@@AEBVfactory_name_t@@@Z 00000001400031b0 f i ieee1588port.obj + 0001:000022b0 ??A?$map@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@@std@@QEAAAEAVLinkLayerAddress@@AEBVPortIdentity@@@Z 00000001400032b0 f i ieee1588port.obj + 0001:000023a0 ??1?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@QEAA@XZ 00000001400033a0 f i ieee1588port.obj + 0001:000023b0 ??0?$_Tree@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@QEAA@AEBU?$less@VPortIdentity@@@1@AEBV?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@1@@Z 00000001400033b0 f i ieee1588port.obj + 0001:00002440 ??1?$_Tree_val@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@QEAA@XZ 0000000140003440 f i ieee1588port.obj + 0001:00002440 ??1?$_Tree_val@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@QEAA@XZ 0000000140003440 f i daemon_cl.obj + 0001:00002450 ??R?$less@VPortIdentity@@@std@@QEBA_NAEBVPortIdentity@@0@Z 0000000140003450 f i ieee1588port.obj + 0001:000024a0 ?erase@?$_Tree@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@QEAA?AV?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@@2@V?$_Tree_const_iterator@V?$_Tree_val@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@@2@0@Z 00000001400034a0 f i ieee1588port.obj + 0001:00002580 ?_Lbound@?$_Tree@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@IEAAPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@2@AEBVPortIdentity@@@Z 0000000140003580 f i ieee1588port.obj + 0001:00002610 ?erase@?$_Tree@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@QEAA?AV?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@@2@V?$_Tree_const_iterator@V?$_Tree_val@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@@2@@Z 0000000140003610 f i ieee1588port.obj + 0001:00002980 ?clear@?$_Tree@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@QEAAXXZ 0000000140003980 f i ieee1588port.obj + 0001:00002a10 ?_Erase@?$_Tree@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@IEAAXPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@2@@Z 0000000140003a10 f i ieee1588port.obj + 0001:00002a80 ?_Lrotate@?$_Tree@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@IEAAXPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@2@@Z 0000000140003a80 f i ieee1588port.obj + 0001:00002ae0 ?_Rrotate@?$_Tree@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@IEAAXPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@2@@Z 0000000140003ae0 f i ieee1588port.obj + 0001:00002b40 ?_Insert@?$_Tree@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@QEAA?AV?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@@2@V?$_Tree_const_iterator@V?$_Tree_val@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@@2@PEAU_Node@?$_Tree_nod@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@2@@Z 0000000140003b40 f i ieee1588port.obj + 0001:00002de0 ?_Insert@?$_Tree@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@QEAA?AV?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@@2@V?$_Tree_const_iterator@V?$_Tree_val@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@@2@PEAU_Node@?$_Tree_nod@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@2@@Z 0000000140003de0 f i ieee1588port.obj + 0001:00003050 ?_Linsert@?$_Tree@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@QEAA?AU?$pair@V?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@@std@@_N@2@PEAU_Node@?$_Tree_nod@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@2@_N@Z 0000000140004050 f i ieee1588port.obj + 0001:000031b0 ?_Insert@?$_Tree@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@IEAA?AV?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@@2@_NPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@2@1@Z 00000001400041b0 f i ieee1588port.obj + 0001:00003460 ?_Linsert@?$_Tree@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@QEAA?AU?$pair@V?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@@std@@_N@2@PEAU_Node@?$_Tree_nod@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@2@_N@Z 0000000140004460 f i ieee1588port.obj + 0001:00003610 ?_Insert@?$_Tree@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@IEAA?AV?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@@2@_NPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@2@1@Z 0000000140004610 f i ieee1588port.obj + 0001:000038c0 ??$_Buynode@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@?$_Tree_val@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@QEAAPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@1@$$QEAU?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@1@@Z 00000001400048c0 f i ieee1588port.obj + 0001:00003930 ??$_Buynode@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@?$_Tree_val@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@QEAAPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@1@$$QEAU?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@1@@Z 0000000140004930 f i ieee1588port.obj + 0001:00003990 ?_Buynode@?$_Tree_val@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@QEAAPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@2@XZ 0000000140004990 f i ieee1588port.obj + 0001:00003a10 ?_Buynode@?$_Tree_val@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@QEAAPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@2@XZ 0000000140004a10 f i ieee1588port.obj + 0001:00003a90 ??_EPTPMessagePathDelayReq@@UEAAPEAXI@Z 0000000140004a90 f i CIL library: CIL module + 0001:00003a90 ??_GPTPMessageCommon@@UEAAPEAXI@Z 0000000140004a90 f i ptp_message.obj + 0001:00003a90 ??_GPTPMessageSync@@UEAAPEAXI@Z 0000000140004a90 f i ptp_message.obj + 0001:00003a90 ??_EPTPMessageSync@@UEAAPEAXI@Z 0000000140004a90 f i CIL library: CIL module + 0001:00003a90 ??_EPTPMessageFollowUp@@UEAAPEAXI@Z 0000000140004a90 f i CIL library: CIL module + 0001:00003a90 ??_GPTPMessagePathDelayReq@@UEAAPEAXI@Z 0000000140004a90 f i ptp_message.obj + 0001:00003a90 ??_GPTPMessageFollowUp@@UEAAPEAXI@Z 0000000140004a90 f i ptp_message.obj + 0001:00003a90 ??_EPTPMessageCommon@@UEAAPEAXI@Z 0000000140004a90 f i CIL library: CIL module + 0001:00003ad0 ??_GPTPMessagePathDelayResp@@UEAAPEAXI@Z 0000000140004ad0 f i ptp_message.obj + 0001:00003ad0 ??_EPTPMessagePathDelayResp@@UEAAPEAXI@Z 0000000140004ad0 f i CIL library: CIL module + 0001:00003b30 ??_GPTPMessagePathDelayRespFollowUp@@UEAAPEAXI@Z 0000000140004b30 f i ptp_message.obj + 0001:00003b30 ??_EPTPMessagePathDelayRespFollowUp@@UEAAPEAXI@Z 0000000140004b30 f i CIL library: CIL module + 0001:00003b90 ??0PortIdentity@@QEAA@PEAEPEAG@Z 0000000140004b90 f i ptp_message.obj + 0001:00003bd0 ?addQualifiedAnnounce@IEEE1588Port@@QEAAXPEAVPTPMessageAnnounce@@@Z 0000000140004bd0 f i ptp_message.obj + 0001:00003c50 ?buildPTPMessage@@YAPEAVPTPMessageCommon@@PEADHPEAVLinkLayerAddress@@PEAVIEEE1588Port@@@Z 0000000140004c50 f ptp_message.obj + 0001:00004370 ?processMessage@PTPMessageCommon@@UEAAXPEAVIEEE1588Port@@@Z 0000000140005370 f ptp_message.obj + 0001:00004380 ?buildCommonHeader@PTPMessageCommon@@QEAAXPEAE@Z 0000000140005380 f ptp_message.obj + 0001:00004460 ??1PTPMessageCommon@@UEAA@XZ 0000000140005460 f ptp_message.obj + 0001:00004480 ??_EPTPMessageAnnounce@@UEAAPEAXI@Z 0000000140005480 f i CIL library: CIL module + 0001:00004480 ??_GPTPMessageAnnounce@@UEAAPEAXI@Z 0000000140005480 f i ptp_message.obj + 0001:000044e0 ?isBetterThan@PTPMessageAnnounce@@QEAA_NPEAV1@@Z 00000001400054e0 f ptp_message.obj + 0001:000045b0 ??0PTPMessageSync@@QEAA@PEAVIEEE1588Port@@@Z 00000001400055b0 f ptp_message.obj + 0001:00004660 ?sendPort@PTPMessageSync@@QEAAXPEAVIEEE1588Port@@PEAVPortIdentity@@@Z 0000000140005660 f ptp_message.obj + 0001:000047a0 ??0PTPMessageAnnounce@@QEAA@PEAVIEEE1588Port@@@Z 00000001400057a0 f ptp_message.obj + 0001:000048e0 ?sendPort@PTPMessageAnnounce@@QEAAXPEAVIEEE1588Port@@PEAVPortIdentity@@@Z 00000001400058e0 f ptp_message.obj + 0001:00004a60 ?processMessage@PTPMessageAnnounce@@UEAAXPEAVIEEE1588Port@@@Z 0000000140005a60 f ptp_message.obj + 0001:00004b50 ?processMessage@PTPMessageSync@@UEAAXPEAVIEEE1588Port@@@Z 0000000140005b50 f ptp_message.obj + 0001:00004c60 ??0PTPMessageFollowUp@@QEAA@PEAVIEEE1588Port@@@Z 0000000140005c60 f ptp_message.obj + 0001:00004d50 ?sendPort@PTPMessageFollowUp@@QEAAXPEAVIEEE1588Port@@PEAVPortIdentity@@@Z 0000000140005d50 f ptp_message.obj + 0001:00004ea0 ?processMessage@PTPMessageFollowUp@@UEAAXPEAVIEEE1588Port@@@Z 0000000140005ea0 f ptp_message.obj + 0001:00005290 ?processMessage@PTPMessagePathDelayReq@@UEAAXPEAVIEEE1588Port@@@Z 0000000140006290 f ptp_message.obj + 0001:000056b0 ?sendPort@PTPMessagePathDelayReq@@QEAAXPEAVIEEE1588Port@@PEAVPortIdentity@@@Z 00000001400066b0 f ptp_message.obj + 0001:000057b0 ??0PTPMessagePathDelayResp@@QEAA@PEAVIEEE1588Port@@@Z 00000001400067b0 f ptp_message.obj + 0001:00005840 ?processMessage@PTPMessagePathDelayResp@@UEAAXPEAVIEEE1588Port@@@Z 0000000140006840 f ptp_message.obj + 0001:00005990 ?sendPort@PTPMessagePathDelayResp@@QEAAXPEAVIEEE1588Port@@PEAVPortIdentity@@@Z 0000000140006990 f ptp_message.obj + 0001:00005ae0 ??0PTPMessagePathDelayRespFollowUp@@QEAA@PEAVIEEE1588Port@@@Z 0000000140006ae0 f ptp_message.obj + 0001:00005b70 ?processMessage@PTPMessagePathDelayRespFollowUp@@UEAAXPEAVIEEE1588Port@@@Z 0000000140006b70 f ptp_message.obj + 0001:00006070 ?sendPort@PTPMessagePathDelayRespFollowUp@@QEAAXPEAVIEEE1588Port@@PEAVPortIdentity@@@Z 0000000140007070 f ptp_message.obj + 0001:000061c0 ?_Buynode@?$_List_val@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@QEAAPEAU_Node@?$_List_nod@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@2@PEAU342@0AEBQEAUWindowsTimerQueueHandlerArg@@@Z 00000001400071c0 f i daemon_cl.obj + 0001:000061c0 ?_Buynode@?$_List_val@PEAVPTPMessageAnnounce@@V?$allocator@PEAVPTPMessageAnnounce@@@std@@@std@@QEAAPEAU_Node@?$_List_nod@PEAVPTPMessageAnnounce@@V?$allocator@PEAVPTPMessageAnnounce@@@std@@@2@PEAU342@0AEBQEAVPTPMessageAnnounce@@@Z 00000001400071c0 f i ptp_message.obj + 0001:00006260 ?HWTimestamper_init@HWTimestamper@@UEAA_NPEAVInterfaceLabel@@@Z 0000000140007260 f i daemon_cl.obj + 0001:00006270 ??3@YAXPEAX0@Z 0000000140007270 f i ieee1588port.obj + 0001:00006270 ?HWTimestamper_final@HWTimestamper@@UEAAXXZ 0000000140007270 f i daemon_cl.obj + 0001:00006280 ?HWTimestamper_adjclockrate@HWTimestamper@@UEAA_NHIVTimestamp@@_J_N@Z 0000000140007280 f i daemon_cl.obj + 0001:00006280 ?HWTimestamper_adjclockrate2@HWTimestamper@@UEAA_NH@Z 0000000140007280 f i daemon_cl.obj + 0001:00006280 ?HWTimestamper_get_extclk_offset@HWTimestamper@@UEAA_NPEAVTimestamp@@PEA_JPEAH@Z 0000000140007280 f i daemon_cl.obj + 0001:00006290 ?HWTimestamper_get_extderror@HWTimestamper@@UEAAXPEAD@Z 0000000140007290 f i daemon_cl.obj + 0001:000062a0 ??_GHWTimestamper@@UEAAPEAXI@Z 00000001400072a0 f i daemon_cl.obj + 0001:000062a0 ??_GWindowsTimestamper@@UEAAPEAXI@Z 00000001400072a0 f i daemon_cl.obj + 0001:000062a0 ??_EHWTimestamper@@UEAAPEAXI@Z 00000001400072a0 f i CIL library: CIL module + 0001:000062a0 ??_EWindowsTimestamper@@UEAAPEAXI@Z 00000001400072a0 f i CIL library: CIL module + 0001:000062d0 ?send@WindowsPCAPNetworkInterface@@UEAA?AW4net_result@@PEAVLinkLayerAddress@@PEAE_K_N@Z 00000001400072d0 f i daemon_cl.obj + 0001:000063a0 ?recv@WindowsPCAPNetworkInterface@@UEAA?AW4net_result@@PEAVLinkLayerAddress@@PEAEAEA_K@Z 00000001400073a0 f i daemon_cl.obj + 0001:000064d0 ?getLinkLayerAddress@WindowsPCAPNetworkInterface@@UEAAXPEAVLinkLayerAddress@@@Z 00000001400074d0 f i daemon_cl.obj + 0001:00006500 ?getPayloadOffset@WindowsPCAPNetworkInterface@@UEAAIXZ 0000000140007500 f i daemon_cl.obj + 0001:00006510 ??_GWindowsPCAPNetworkInterface@@UEAAPEAXI@Z 0000000140007510 f i daemon_cl.obj + 0001:00006510 ??_EWindowsPCAPNetworkInterface@@UEAAPEAXI@Z 0000000140007510 f i CIL library: CIL module + 0001:00006590 ?createInterface@WindowsPCAPNetworkInterfaceFactory@@UEAA_NPEAPEAVOSNetworkInterface@@PEAVInterfaceLabel@@PEAVHWTimestamper@@@Z 0000000140007590 f i daemon_cl.obj + 0001:000066f0 ?lock@WindowsLock@@MEAA?AW4OSLockResult@@XZ 00000001400076f0 f i daemon_cl.obj + 0001:000067a0 ?trylock@WindowsLock@@MEAA?AW4OSLockResult@@XZ 00000001400077a0 f i daemon_cl.obj + 0001:00006850 ?unlock@WindowsLock@@MEAA?AW4OSLockResult@@XZ 0000000140007850 f i daemon_cl.obj + 0001:00006870 ?createLock@WindowsLockFactory@@UEAAPEAVOSLock@@W4OSLockType@@@Z 0000000140007870 f i daemon_cl.obj + 0001:000068f0 ?wait_prelock@WindowsCondition@@UEAA_NXZ 00000001400078f0 f i daemon_cl.obj + 0001:00006910 ?wait@WindowsCondition@@UEAA_NXZ 0000000140007910 f i daemon_cl.obj + 0001:00006960 ?signal@WindowsCondition@@UEAA_NXZ 0000000140007960 f i daemon_cl.obj + 0001:000069a0 ?createCondition@WindowsConditionFactory@@UEAAPEAVOSCondition@@XZ 00000001400079a0 f i daemon_cl.obj + 0001:000069f0 ?cleanupRetiredTimers@WindowsTimerQueue@@AEAAXXZ 00000001400079f0 f i daemon_cl.obj + 0001:00006ac0 ??0WindowsTimerQueue@@IEAA@XZ 0000000140007ac0 f i daemon_cl.obj + 0001:00006b60 ?addEvent@WindowsTimerQueue@@UEAA_NKHP6AXPEAX@ZPEAUevent_descriptor_t@@_NPEAI@Z 0000000140007b60 f i daemon_cl.obj + 0001:00006da0 ?cancelEvent@WindowsTimerQueue@@UEAA_NHPEAI@Z 0000000140007da0 f i daemon_cl.obj + 0001:00006f40 ??1?$map@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@@std@@QEAA@XZ 0000000140007f40 f i daemon_cl.obj + 0001:00006f80 ?WindowsTimerQueueHandler@@YAXPEAXE@Z 0000000140007f80 f daemon_cl.obj + 0001:00007090 ?createOSTimerQueue@WindowsTimerQueueFactory@@UEAAPEAVOSTimerQueue@@XZ 0000000140008090 f i daemon_cl.obj + 0001:000070d0 ?sleep@WindowsTimer@@UEAAKK@Z 00000001400080d0 f i daemon_cl.obj + 0001:00007100 ?createTimer@WindowsTimerFactory@@UEAAPEAVOSTimer@@XZ 0000000140008100 f i daemon_cl.obj + 0001:00007130 ?OSThreadCallback@@YAKPEAX@Z 0000000140008130 f daemon_cl.obj + 0001:00007150 ?start@WindowsThread@@UEAA_NP6A?AW4OSThreadExitCode@@PEAX@Z0@Z 0000000140008150 f i daemon_cl.obj + 0001:000071e0 ?join@WindowsThread@@UEAA_NAEAW4OSThreadExitCode@@@Z 00000001400081e0 f i daemon_cl.obj + 0001:00007230 ?createThread@WindowsThreadFactory@@UEAAPEAVOSThread@@XZ 0000000140008230 f i daemon_cl.obj + 0001:00007260 ?HWTimestamper_init@WindowsTimestamper@@UEAA_NPEAVInterfaceLabel@@@Z 0000000140008260 f i daemon_cl.obj + 0001:00007550 ?HWTimestamper_gettime@WindowsTimestamper@@UEAA_NPEAVTimestamp@@0PEAI1@Z 0000000140008550 f i daemon_cl.obj + 0001:00007730 ?HWTimestamper_txtimestamp@WindowsTimestamper@@UEAAHPEAVPortIdentity@@GAEAVTimestamp@@AEAI_N@Z 0000000140008730 f i daemon_cl.obj + 0001:000078d0 ?HWTimestamper_rxtimestamp@WindowsTimestamper@@UEAAHPEAVPortIdentity@@GAEAVTimestamp@@AEAI_N@Z 00000001400088d0 f i daemon_cl.obj + 0001:00007a60 ?init@WindowsNamedPipeIPC@@UEAA_NXZ 0000000140008a60 f i daemon_cl.obj + 0001:00007b40 ?update@WindowsNamedPipeIPC@@UEAA_N_J0HH_K@Z 0000000140008b40 f i daemon_cl.obj + 0001:00007bd0 ?ctrl_handler@@YAHK@Z 0000000140008bd0 f daemon_cl.obj + 0001:00007bf0 main 0000000140008bf0 f daemon_cl.obj + 0001:00008020 ??A?$map@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@@std@@QEAAAEAUTimerQueue_t@@AEBH@Z 0000000140009020 f i daemon_cl.obj + 0001:000081e0 ??1TimerQueue_t@@QEAA@XZ 00000001400091e0 f i daemon_cl.obj + 0001:000081e0 ??1?$list@PEAVPTPMessageAnnounce@@V?$allocator@PEAVPTPMessageAnnounce@@@std@@@std@@QEAA@XZ 00000001400091e0 f i ieee1588port.obj + 0001:00008240 ??1?$pair@$$CBHUTimerQueue_t@@@std@@QEAA@XZ 0000000140009240 f i daemon_cl.obj + 0001:00008240 ??$_Dest_val@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@std@@U?$pair@$$CBHUTimerQueue_t@@@2@@std@@YAXAEAV?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@0@PEAU?$pair@$$CBHUTimerQueue_t@@@0@@Z 0000000140009240 f i daemon_cl.obj + 0001:000082a0 ?_Tidy@?$list@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@QEAAXXZ 00000001400092a0 f i daemon_cl.obj + 0001:000082f0 ??1?$_List_val@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@QEAA@XZ 00000001400092f0 f i daemon_cl.obj + 0001:00008300 ??0?$_Tree@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@QEAA@AEBU?$less@H@1@AEBV?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@1@@Z 0000000140009300 f i daemon_cl.obj + 0001:00008390 ?erase@?$_Tree@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@QEAA?AV?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@@2@V?$_Tree_const_iterator@V?$_Tree_val@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@@2@0@Z 0000000140009390 f i daemon_cl.obj + 0001:00008490 ??0?$list@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@QEAA@AEBV01@@Z 0000000140009490 f i daemon_cl.obj + 0001:00008540 ?erase@?$_Tree@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@QEAA?AV?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@@2@V?$_Tree_const_iterator@V?$_Tree_val@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@@2@@Z 0000000140009540 f i daemon_cl.obj + 0001:000088d0 ?_Erase@?$_Tree@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@IEAAXPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@2@@Z 00000001400098d0 f i daemon_cl.obj + 0001:00008970 ?_Lrotate@?$_Tree@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@IEAAXPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@2@@Z 0000000140009970 f i daemon_cl.obj + 0001:000089d0 ?_Rrotate@?$_Tree@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@IEAAXPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@2@@Z 00000001400099d0 f i daemon_cl.obj + 0001:00008a30 ?_Insert@?$_Tree@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@QEAA?AV?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@@2@V?$_Tree_const_iterator@V?$_Tree_val@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@@2@PEAU_Node@?$_Tree_nod@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@2@@Z 0000000140009a30 f i daemon_cl.obj + 0001:00008c40 ?_Linsert@?$_Tree@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@QEAA?AU?$pair@V?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@@std@@_N@2@PEAU_Node@?$_Tree_nod@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@2@_N@Z 0000000140009c40 f i daemon_cl.obj + 0001:00008d70 ?_Insert@?$_Tree@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@IEAA?AV?$_Tree_iterator@V?$_Tree_val@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@@2@_NPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@2@1@Z 0000000140009d70 f i daemon_cl.obj + 0001:00009060 ??$_Buynode@U?$pair@$$CBHUTimerQueue_t@@@std@@@?$_Tree_val@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@QEAAPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@1@$$QEAU?$pair@$$CBHUTimerQueue_t@@@1@@Z 000000014000a060 f i daemon_cl.obj + 0001:000090e0 ??$_Insert@V?$_List_const_iterator@V?$_List_val@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@@std@@@?$list@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@QEAAXV?$_List_const_iterator@V?$_List_val@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@@1@00Uforward_iterator_tag@1@@Z 000000014000a0e0 f i daemon_cl.obj + 0001:00009190 ?_Buynode@?$_Tree_val@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@QEAAPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@2@XZ 000000014000a190 f i daemon_cl.obj + 0001:00009210 ?openInterfaceByAddr@@YA?AW4packet_error_t@@PEAUpacket_handle@@PEAUpacket_addr_t@@H@Z 000000014000a210 f packet.obj + 0001:000093d0 ?packetBind@@YA?AW4packet_error_t@@PEAUpacket_handle@@G@Z 000000014000a3d0 f packet.obj + 0001:000094d0 ??_U@YAPEAX_K@Z 000000014000a4d0 f msvcprt:newaop_s.obj + 0001:000094f0 __security_check_cookie 000000014000a4f0 f MSVCRT:amdsecgs.obj + 0001:00009510 ?what@exception@std@@UEBAPEBDXZ 000000014000a510 f MSVCRT:MSVCR100.dll + 0001:00009516 ??0exception@std@@QEAA@AEBV01@@Z 000000014000a516 f MSVCRT:MSVCR100.dll + 0001:0000951c _onexit 000000014000a51c f MSVCRT:atonexit.obj + 0001:000095cc atexit 000000014000a5cc f MSVCRT:atonexit.obj + 0001:000095e4 ??3@YAXPEAX@Z 000000014000a5e4 f MSVCRT:MSVCR100.dll + 0001:000095ec ??_Etype_info@@UEAAPEAXI@Z 000000014000a5ec f i MSVCRT:ti_inst.obj + 0001:00009656 ??2@YAPEAX_K@Z 000000014000a656 f MSVCRT:MSVCR100.dll + 0001:0000965c _purecall 000000014000a65c f MSVCRT:MSVCR100.dll + 0001:0000991c mainCRTStartup 000000014000a91c f MSVCRT:crtexe.obj + 0001:00009930 __report_gsfailure 000000014000a930 f MSVCRT:gs_report.obj + 0001:00009a7a __C_specific_handler 000000014000aa7a f MSVCRT:MSVCR100.dll + 0001:00009a80 _unlock 000000014000aa80 f MSVCRT:MSVCR100.dll + 0001:00009a86 __dllonexit 000000014000aa86 f MSVCRT:MSVCR100.dll + 0001:00009a8c _lock 000000014000aa8c f MSVCRT:MSVCR100.dll + 0001:00009a94 ?__ArrayUnwind@@YAXPEAX_KHP6AX0@Z@Z 000000014000aa94 f MSVCRT:ehvecdtr.obj + 0001:00009ae0 ??_M@YAXPEAX_KHP6AX0@Z@Z 000000014000aae0 f MSVCRT:ehvecdtr.obj + 0001:00009b40 ?__CxxUnhandledExceptionFilter@@YAJPEAU_EXCEPTION_POINTERS@@@Z 000000014000ab40 f MSVCRT:unhandld.obj + 0001:00009b84 __CxxSetUnhandledExceptionFilter 000000014000ab84 f MSVCRT:unhandld.obj + 0001:00009b9c _amsg_exit 000000014000ab9c f MSVCRT:MSVCR100.dll + 0001:00009ba4 _RTC_Initialize 000000014000aba4 f MSVCRT:_initsect_.obj + 0001:00009bdc _RTC_Terminate 000000014000abdc f MSVCRT:_initsect_.obj + 0001:00009c14 _XcptFilter 000000014000ac14 f MSVCRT:MSVCR100.dll + 0001:00009c20 _ValidateImageBase 000000014000ac20 f MSVCRT:pesect.obj + 0001:00009c50 _FindPESection 000000014000ac50 f MSVCRT:pesect.obj + 0001:00009ca0 _IsNonwritableInCurrentImage 000000014000aca0 f MSVCRT:pesect.obj + 0001:00009ce2 _initterm 000000014000ace2 f MSVCRT:MSVCR100.dll + 0001:00009ce8 _initterm_e 000000014000ace8 f MSVCRT:MSVCR100.dll + 0001:00009cf0 _matherr 000000014000acf0 f MSVCRT:merr.obj + 0001:00009cf0 _setargv 000000014000acf0 f MSVCRT:dllargv.obj + 0001:00009cf4 __security_init_cookie 000000014000acf4 f MSVCRT:gs_support.obj + 0001:00009da8 __crt_debugger_hook 000000014000ada8 f MSVCRT:MSVCR100.dll + 0001:00009dae ?terminate@@YAXXZ 000000014000adae f MSVCRT:MSVCR100.dll + 0001:00009db4 ??1type_info@@UEAA@XZ 000000014000adb4 f MSVCRT:MSVCR100.dll + 0001:00009db4 ?_type_info_dtor_internal_method@type_info@@QEAAXXZ 000000014000adb4 f MSVCRT:MSVCR100.dll + 0001:00009dba RtlVirtualUnwind 000000014000adba f kernel32:KERNEL32.dll + 0001:00009dc0 RtlLookupFunctionEntry 000000014000adc0 f kernel32:KERNEL32.dll + 0001:00009dc6 memcpy 000000014000adc6 f MSVCRT:MSVCR100.dll + 0001:00009dcc __GSHandlerCheckCommon 000000014000adcc f MSVCRT:gshandler.obj + 0001:00009e30 __GSHandlerCheck 000000014000ae30 f MSVCRT:gshandler.obj + 0001:00009e60 __chkstk 000000014000ae60 f MSVCRT:chkstk.obj + 0001:00009e60 _alloca_probe 000000014000ae60 MSVCRT:chkstk.obj + 0001:00009eae memcmp 000000014000aeae f MSVCRT:MSVCR100.dll + 0001:00009eb4 _CxxThrowException 000000014000aeb4 f MSVCRT:MSVCR100.dll + 0001:00009eba __CxxFrameHandler3 000000014000aeba f MSVCRT:MSVCR100.dll + 0001:00009ec0 __RTDynamicCast 000000014000aec0 f MSVCRT:MSVCR100.dll + 0001:00009ec8 __GSHandlerCheck_EH 000000014000aec8 f MSVCRT:gshandlereh.obj + 0001:00009f58 memset 000000014000af58 f MSVCRT:MSVCR100.dll + 0002:00000000 __imp_GetAdaptersAddresses 000000014000c000 Iphlpapi:IPHLPAPI.DLL + 0002:00000008 \177IPHLPAPI_NULL_THUNK_DATA 000000014000c008 Iphlpapi:IPHLPAPI.DLL + 0002:00000010 __imp_GetCurrentProcess 000000014000c010 kernel32:KERNEL32.dll + 0002:00000018 __imp_TerminateProcess 000000014000c018 kernel32:KERNEL32.dll + 0002:00000020 __imp_DecodePointer 000000014000c020 kernel32:KERNEL32.dll + 0002:00000028 __imp_GetSystemTimeAsFileTime 000000014000c028 kernel32:KERNEL32.dll + 0002:00000030 __imp_UnhandledExceptionFilter 000000014000c030 kernel32:KERNEL32.dll + 0002:00000038 __imp_SetUnhandledExceptionFilter 000000014000c038 kernel32:KERNEL32.dll + 0002:00000040 __imp_RtlVirtualUnwind 000000014000c040 kernel32:KERNEL32.dll + 0002:00000048 __imp_GetCurrentProcessId 000000014000c048 kernel32:KERNEL32.dll + 0002:00000050 __imp_GetTickCount 000000014000c050 kernel32:KERNEL32.dll + 0002:00000058 __imp_QueryPerformanceCounter 000000014000c058 kernel32:KERNEL32.dll + 0002:00000060 __imp_RtlCaptureContext 000000014000c060 kernel32:KERNEL32.dll + 0002:00000068 __imp_RtlLookupFunctionEntry 000000014000c068 kernel32:KERNEL32.dll + 0002:00000070 __imp_CreateFileA 000000014000c070 kernel32:KERNEL32.dll + 0002:00000078 __imp_CreateTimerQueue 000000014000c078 kernel32:KERNEL32.dll + 0002:00000080 __imp_WaitForSingleObject 000000014000c080 kernel32:KERNEL32.dll + 0002:00000088 __imp_ReleaseSRWLockExclusive 000000014000c088 kernel32:KERNEL32.dll + 0002:00000090 __imp_WriteFile 000000014000c090 kernel32:KERNEL32.dll + 0002:00000098 __imp_AcquireSRWLockExclusive 000000014000c098 kernel32:KERNEL32.dll + 0002:000000a0 __imp_Sleep 000000014000c0a0 kernel32:KERNEL32.dll + 0002:000000a8 __imp_InitializeConditionVariable 000000014000c0a8 kernel32:KERNEL32.dll + 0002:000000b0 __imp_GetLastError 000000014000c0b0 kernel32:KERNEL32.dll + 0002:000000b8 __imp_SleepConditionVariableSRW 000000014000c0b8 kernel32:KERNEL32.dll + 0002:000000c0 __imp_CreateNamedPipeA 000000014000c0c0 kernel32:KERNEL32.dll + 0002:000000c8 __imp_CreateTimerQueueTimer 000000014000c0c8 kernel32:KERNEL32.dll + 0002:000000d0 __imp_InitializeSRWLock 000000014000c0d0 kernel32:KERNEL32.dll + 0002:000000d8 __imp_SetConsoleCtrlHandler 000000014000c0d8 kernel32:KERNEL32.dll + 0002:000000e0 __imp_DeviceIoControl 000000014000c0e0 kernel32:KERNEL32.dll + 0002:000000e8 __imp_WakeAllConditionVariable 000000014000c0e8 kernel32:KERNEL32.dll + 0002:000000f0 __imp_CreateMutexA 000000014000c0f0 kernel32:KERNEL32.dll + 0002:000000f8 __imp_GetCurrentThreadId 000000014000c0f8 kernel32:KERNEL32.dll + 0002:00000100 __imp_ReleaseMutex 000000014000c100 kernel32:KERNEL32.dll + 0002:00000108 __imp_CloseHandle 000000014000c108 kernel32:KERNEL32.dll + 0002:00000110 __imp_DeleteTimerQueueTimer 000000014000c110 kernel32:KERNEL32.dll + 0002:00000118 __imp_CreateThread 000000014000c118 kernel32:KERNEL32.dll + 0002:00000120 __imp_EncodePointer 000000014000c120 kernel32:KERNEL32.dll + 0002:00000128 __imp_IsDebuggerPresent 000000014000c128 kernel32:KERNEL32.dll + 0002:00000130 \177KERNEL32_NULL_THUNK_DATA 000000014000c130 kernel32:KERNEL32.dll + 0002:00000138 __imp_?_Xout_of_range@std@@YAXPEBD@Z 000000014000c138 msvcprt:MSVCP100.dll + 0002:00000140 __imp_?_Xlength_error@std@@YAXPEBD@Z 000000014000c140 msvcprt:MSVCP100.dll + 0002:00000148 \177MSVCP100_NULL_THUNK_DATA 000000014000c148 msvcprt:MSVCP100.dll + 0002:00000150 __imp_??0exception@std@@QEAA@AEBQEBD@Z 000000014000c150 MSVCRT:MSVCR100.dll + 0002:00000158 __imp_??0exception@std@@QEAA@AEBV01@@Z 000000014000c158 MSVCRT:MSVCR100.dll + 0002:00000160 __imp_??1exception@std@@UEAA@XZ 000000014000c160 MSVCRT:MSVCR100.dll + 0002:00000168 __imp_??2@YAPEAX_K@Z 000000014000c168 MSVCRT:MSVCR100.dll + 0002:00000170 __imp_abort 000000014000c170 MSVCRT:MSVCR100.dll + 0002:00000178 __imp___iob_func 000000014000c178 MSVCRT:MSVCR100.dll + 0002:00000180 __imp_fprintf 000000014000c180 MSVCRT:MSVCR100.dll + 0002:00000188 __imp_strncpy_s 000000014000c188 MSVCRT:MSVCR100.dll + 0002:00000190 __imp_memset 000000014000c190 MSVCRT:MSVCR100.dll + 0002:00000198 __imp_strtol 000000014000c198 MSVCRT:MSVCR100.dll + 0002:000001a0 __imp_strnlen 000000014000c1a0 MSVCRT:MSVCR100.dll + 0002:000001a8 __imp__purecall 000000014000c1a8 MSVCRT:MSVCR100.dll + 0002:000001b0 __imp_sprintf_s 000000014000c1b0 MSVCRT:MSVCR100.dll + 0002:000001b8 __imp_free 000000014000c1b8 MSVCRT:MSVCR100.dll + 0002:000001c0 __imp_malloc 000000014000c1c0 MSVCRT:MSVCR100.dll + 0002:000001c8 __imp_strcpy_s 000000014000c1c8 MSVCRT:MSVCR100.dll + 0002:000001d0 __imp___C_specific_handler 000000014000c1d0 MSVCRT:MSVCR100.dll + 0002:000001d8 __imp__unlock 000000014000c1d8 MSVCRT:MSVCR100.dll + 0002:000001e0 __imp_?what@exception@std@@UEBAPEBDXZ 000000014000c1e0 MSVCRT:MSVCR100.dll + 0002:000001e8 __imp__lock 000000014000c1e8 MSVCRT:MSVCR100.dll + 0002:000001f0 __imp__onexit 000000014000c1f0 MSVCRT:MSVCR100.dll + 0002:000001f8 __imp__amsg_exit 000000014000c1f8 MSVCRT:MSVCR100.dll + 0002:00000200 __imp___getmainargs 000000014000c200 MSVCRT:MSVCR100.dll + 0002:00000208 __imp__XcptFilter 000000014000c208 MSVCRT:MSVCR100.dll + 0002:00000210 __imp__exit 000000014000c210 MSVCRT:MSVCR100.dll + 0002:00000218 __imp__cexit 000000014000c218 MSVCRT:MSVCR100.dll + 0002:00000220 __imp_exit 000000014000c220 MSVCRT:MSVCR100.dll + 0002:00000228 __imp___initenv 000000014000c228 MSVCRT:MSVCR100.dll + 0002:00000230 __imp__initterm 000000014000c230 MSVCRT:MSVCR100.dll + 0002:00000238 __imp__initterm_e 000000014000c238 MSVCRT:MSVCR100.dll + 0002:00000240 __imp__configthreadlocale 000000014000c240 MSVCRT:MSVCR100.dll + 0002:00000248 __imp___setusermatherr 000000014000c248 MSVCRT:MSVCR100.dll + 0002:00000250 __imp__commode 000000014000c250 MSVCRT:MSVCR100.dll + 0002:00000258 __imp__fmode 000000014000c258 MSVCRT:MSVCR100.dll + 0002:00000260 __imp___set_app_type 000000014000c260 MSVCRT:MSVCR100.dll + 0002:00000268 __imp___crt_debugger_hook 000000014000c268 MSVCRT:MSVCR100.dll + 0002:00000270 __imp_?terminate@@YAXXZ 000000014000c270 MSVCRT:MSVCR100.dll + 0002:00000278 __imp_?_type_info_dtor_internal_method@type_info@@QEAAXXZ 000000014000c278 MSVCRT:MSVCR100.dll + 0002:00000280 __imp___dllonexit 000000014000c280 MSVCRT:MSVCR100.dll + 0002:00000288 __imp_memcpy 000000014000c288 MSVCRT:MSVCR100.dll + 0002:00000290 __imp_memcmp 000000014000c290 MSVCRT:MSVCR100.dll + 0002:00000298 __imp__CxxThrowException 000000014000c298 MSVCRT:MSVCR100.dll + 0002:000002a0 __imp___CxxFrameHandler3 000000014000c2a0 MSVCRT:MSVCR100.dll + 0002:000002a8 __imp___RTDynamicCast 000000014000c2a8 MSVCRT:MSVCR100.dll + 0002:000002b0 __imp_??3@YAXPEAX@Z 000000014000c2b0 MSVCRT:MSVCR100.dll + 0002:000002b8 __imp_printf 000000014000c2b8 MSVCRT:MSVCR100.dll + 0002:000002c0 \177MSVCR100_NULL_THUNK_DATA 000000014000c2c0 MSVCRT:MSVCR100.dll + 0002:000002c8 __imp_ntohl 000000014000c2c8 ws2_32:WS2_32.dll + 0002:000002d0 __imp_htonl 000000014000c2d0 ws2_32:WS2_32.dll + 0002:000002d8 __imp_ntohs 000000014000c2d8 ws2_32:WS2_32.dll + 0002:000002e0 __imp_htons 000000014000c2e0 ws2_32:WS2_32.dll + 0002:000002e8 \177WS2_32_NULL_THUNK_DATA 000000014000c2e8 ws2_32:WS2_32.dll + 0002:000002f0 __imp_pcap_close 000000014000c2f0 wpcap:wpcap.dll + 0002:000002f8 __imp_pcap_setfilter 000000014000c2f8 wpcap:wpcap.dll + 0002:00000300 __imp_pcap_next_ex 000000014000c300 wpcap:wpcap.dll + 0002:00000308 __imp_pcap_sendpacket 000000014000c308 wpcap:wpcap.dll + 0002:00000310 __imp_pcap_compile 000000014000c310 wpcap:wpcap.dll + 0002:00000318 __imp_pcap_open 000000014000c318 wpcap:wpcap.dll + 0002:00000320 \177wpcap_NULL_THUNK_DATA 000000014000c320 wpcap:wpcap.dll + 0002:00000328 __xc_a 000000014000c328 MSVCRT:cinitexe.obj + 0002:00000350 __xc_z 000000014000c350 MSVCRT:cinitexe.obj + 0002:00000358 __xi_a 000000014000c358 MSVCRT:cinitexe.obj + 0002:00000370 __xi_z 000000014000c370 MSVCRT:cinitexe.obj + 0002:000003a8 ??_7type_info@@6B@ 000000014000c3a8 MSVCRT:ti_inst.obj + 0002:000003c0 ??_C@_0BM@PAPJHAGI@invalid?5map?1set?$DMT?$DO?5iterator?$AA@ 000000014000c3c0 avbts_osnet.obj + 0002:000003e8 ??_7bad_alloc@std@@6B@ 000000014000c3e8 avbts_osnet.obj + 0002:000003f8 ??_C@_07DOAOMMKG@Enabled?$AA@ 000000014000c3f8 ieee1588port.obj + 0002:00000400 ??_C@_08JGILNPHN@Disabled?$AA@ 000000014000c400 ieee1588port.obj + 0002:00000410 ??_C@_0P@MNFODPHI@AsCapable?3?5?$CFs?6?$AA@ 000000014000c410 ieee1588port.obj + 0002:00000420 ??_C@_0CB@NKHFMBCL@?4?4?2?4?4?2?4?4?2common?2ieee1588port?4cpp@ 000000014000c420 ieee1588port.obj + 0002:00000450 ??_C@_0GF@MDOAJEFI@ERROR?5at?5?$CFu?5in?5?$CFs?3?5Failed?5to?5ini@ 000000014000c450 ieee1588port.obj + 0002:000004b8 ??_C@_07DLHCIBDH@default?$AA@ 000000014000c4b8 ieee1588port.obj + 0002:000004c0 ??_C@_0BK@DFGFBKIK@openPort?3?5thread?5started?6?$AA@ 000000014000c4c0 ieee1588port.obj + 0002:000004e0 ??_C@_0CP@OBBALOF@ERROR?5at?5?$CFu?5in?5?$CFs?3?5Discarding?5in@ 000000014000c4e0 ieee1588port.obj + 0002:00000510 ??_C@_0DH@EKGLNKDB@ERROR?5at?5?$CFu?5in?5?$CFs?3?5read?5from?5net@ 000000014000c510 ieee1588port.obj + 0002:00000548 ??_C@_0CN@JABMLKGN@ERROR?5at?5?$CFu?5in?5?$CFs?3?5sendEventPort@ 000000014000c548 ieee1588port.obj + 0002:00000578 ??_C@_0BG@CGMOFACK@Starting?5port?5thread?6?$AA@ 000000014000c578 ieee1588port.obj + 0002:00000590 ??_C@_0DA@JEOMFCHP@ERROR?5at?5?$CFu?5in?5?$CFs?3?5Error?5creatin@ 000000014000c590 ieee1588port.obj + 0002:000005c0 ??_C@_01EEMJAFIK@?6?$AA@ 000000014000c5c0 ieee1588port.obj + 0002:000005c8 ??_C@_0CP@NPGACHBF@?$CK?$CK?$CKSync?5Timeout?5Expired?5?9?5Becomi@ 000000014000c5c8 ieee1588port.obj + 0002:00000600 ??_C@_0EA@FIDILCHK@Error?5?$CITX?$CJ?5timestamping?5PDelay?5r@ 000000014000c600 ieee1588port.obj + 0002:00000640 ??_C@_0DE@BANOKGOL@Error?5?$CITX?$CJ?5timestamping?5PDelay?5r@ 000000014000c640 ieee1588port.obj + 0002:00000678 ??_C@_0DD@OGAMEIIA@Error?5?$CITX?$CJ?5timestamping?5Sync?5?$CIRe@ 000000014000c678 ieee1588port.obj + 0002:000006b0 ??_C@_0CK@JAJCOOCC@Error?5?$CITX?$CJ?5timestamping?5Sync?0?5er@ 000000014000c6b0 ieee1588port.obj + 0002:000006e0 ??_C@_0CD@KBDOGJHF@PDelay?5Response?5Followup?5is?5NULL@ 000000014000c6e0 ieee1588port.obj + 0002:00000708 ??_C@_0BF@CMGMJLA@Switching?5to?5Master?6?$AA@ 000000014000c708 ieee1588port.obj + 0002:00000720 ??_C@_0BE@JHDPFIAH@Switching?5to?5Slave?6?$AA@ 000000014000c720 ieee1588port.obj + 0002:00000738 ??_C@_0BE@JONHPENG@map?1set?$DMT?$DO?5too?5long?$AA@ 000000014000c738 ieee1588port.obj + 0002:00000758 ??_7LinkLayerAddress@@6B@ 000000014000c758 ieee1588port.obj + 0002:00000768 ??_7InterfaceLabel@@6B@ 000000014000c768 ieee1588port.obj + 0002:00000770 ??_C@_0DO@EBENJJMN@Error?5?$CIRX?$CJ?5timestamping?5RX?5event@ 000000014000c770 ptp_message.obj + 0002:000007b0 ??_C@_0CA@LPEMILMO@?4?4?2?4?4?2?4?4?2common?2ptp_message?4cpp?$AA@ 000000014000c7b0 ptp_message.obj + 0002:000007d0 ??_C@_0HH@MLCBNOMP@ERROR?5at?5?$CFu?5in?5?$CFs?3?5?$CK?$CK?$CK?5Received?5@ 000000014000c7d0 ptp_message.obj + 0002:00000848 ??_C@_0DK@JNCBBDII@ERROR?5at?5?$CFu?5in?5?$CFs?3?5Received?5unsu@ 000000014000c848 ptp_message.obj + 0002:00000890 ??_C@_0EE@GKDAOEGN@ERROR?5at?5?$CFu?5in?5?$CFs?3?5Received?5Foll@ 000000014000c890 ptp_message.obj + 0002:000008e0 ??_C@_0EK@GKBMCKE@ERROR?5at?5?$CFu?5in?5?$CFs?3?5Received?5Foll@ 000000014000c8e0 ptp_message.obj + 0002:00000930 ??_C@_0EB@NKLDNAPB@Error?5?$CITX?$CJ?5timestamping?5PDelay?5R@ 000000014000c930 ptp_message.obj + 0002:00000978 ??_C@_0DF@LCPJCKOO@Error?5?$CITX?$CJ?5timestamping?5PDelay?5R@ 000000014000c978 ptp_message.obj + 0002:000009b0 ??_C@_0BO@DLMECCKO@Failed?5to?5get?5PDelay?5RX?5Lock?6?$AA@ 000000014000c9b0 ptp_message.obj + 0002:000009d0 ??_C@_0EH@DDBECIOJ@ERROR?5at?5?$CFu?5in?5?$CFs?3?5?$DO?$DO?$DO?5Received?5@ 000000014000c9d0 ptp_message.obj + 0002:00000a20 ??_C@_0EI@NNLKEKEC@ERROR?5at?5?$CFu?5in?5?$CFs?3?5?$DO?$DO?$DO?5Received?5@ 000000014000ca20 ptp_message.obj + 0002:00000a70 ??_C@_0FN@HBGJNHBE@ERROR?5at?5?$CFu?5in?5?$CFs?3?5Received?5PDel@ 000000014000ca70 ptp_message.obj + 0002:00000ad0 ??_C@_0CC@DILPCBD@ERROR?5at?5?$CFu?5in?5?$CFs?3?5My?5SeqId?3?5?$CFu?5@ 000000014000cad0 ptp_message.obj + 0002:00000af8 ??_C@_0CF@BHKGMNAC@ERROR?5at?5?$CFu?5in?5?$CFs?3?5Their?5SeqId?3?5@ 000000014000caf8 ptp_message.obj + 0002:00000b20 ??_C@_0FO@BHOOLJAL@ERROR?5at?5?$CFu?5in?5?$CFs?3?5Received?5PDel@ 000000014000cb20 ptp_message.obj + 0002:00000b80 ??_C@_0CH@FHAMNBPF@ERROR?5at?5?$CFu?5in?5?$CFs?3?5?$CFhu?0?5?$CFhu?0?5?$CFhu@ 000000014000cb80 ptp_message.obj + 0002:00000ba8 ??_C@_0BB@MOGOBHAF@list?$DMT?$DO?5too?5long?$AA@ 000000014000cba8 ptp_message.obj + 0002:00000bc8 ??_7PTPMessageCommon@@6B@ 000000014000cbc8 ptp_message.obj + 0002:00000be0 ??_7PTPMessageSync@@6B@ 000000014000cbe0 ptp_message.obj + 0002:00000bf8 ??_7PTPMessageAnnounce@@6B@ 000000014000cbf8 ptp_message.obj + 0002:00000c10 ??_7PTPMessagePathDelayReq@@6B@ 000000014000cc10 ptp_message.obj + 0002:00000c28 ??_7PTPMessagePathDelayResp@@6B@ 000000014000cc28 ptp_message.obj + 0002:00000c40 ??_7PTPMessagePathDelayRespFollowUp@@6B@ 000000014000cc40 ptp_message.obj + 0002:00000c58 ??_7PTPMessageFollowUp@@6B@ 000000014000cc58 ptp_message.obj + 0002:00000c68 ??_C@_04GBDIODIA@?2?2?4?2?$AA@ 000000014000cc68 daemon_cl.obj + 0002:00000c70 ??_C@_09IPJPBDHI@?2?2?4?2pipe?2?$AA@ 000000014000cc70 daemon_cl.obj + 0002:00000c80 ??_C@_0M@FKJIJDJM@gptp?9update?$AA@ 000000014000cc80 daemon_cl.obj + 0002:00000c90 ??_C@_0BL@JMMFGJEE@Failed?5to?5initialize?5port?6?$AA@ 000000014000cc90 daemon_cl.obj + 0002:00000cb0 ??_C@_0CD@KNEPODGG@Unable?5to?5register?5Ctrl?9C?5handle@ 000000014000ccb0 daemon_cl.obj + 0002:00000ce8 ??_7HWTimestamper@@6B@ 000000014000cce8 daemon_cl.obj + 0002:00000d40 ??_7WindowsPCAPNetworkInterface@@6B@ 000000014000cd40 daemon_cl.obj + 0002:00000d70 ??_7WindowsPCAPNetworkInterfaceFactory@@6B@ 000000014000cd70 daemon_cl.obj + 0002:00000d80 ??_7WindowsLock@@6B@ 000000014000cd80 daemon_cl.obj + 0002:00000da0 ??_7WindowsLockFactory@@6B@ 000000014000cda0 daemon_cl.obj + 0002:00000db0 ??_7WindowsCondition@@6B@ 000000014000cdb0 daemon_cl.obj + 0002:00000dd0 ??_7WindowsConditionFactory@@6B@ 000000014000cdd0 daemon_cl.obj + 0002:00000de0 ??_7WindowsTimerQueue@@6B@ 000000014000cde0 daemon_cl.obj + 0002:00000df8 ??_7WindowsTimerQueueFactory@@6B@ 000000014000cdf8 daemon_cl.obj + 0002:00000e08 ??_7WindowsTimer@@6B@ 000000014000ce08 daemon_cl.obj + 0002:00000e18 ??_7WindowsTimerFactory@@6B@ 000000014000ce18 daemon_cl.obj + 0002:00000e28 ??_7WindowsThread@@6B@ 000000014000ce28 daemon_cl.obj + 0002:00000e40 ??_7WindowsThreadFactory@@6B@ 000000014000ce40 daemon_cl.obj + 0002:00000e58 ??_7WindowsTimestamper@@6B@ 000000014000ce58 daemon_cl.obj + 0002:00000eb0 ??_7WindowsNamedPipeIPC@@6B@ 000000014000ceb0 daemon_cl.obj + 0002:00000ec0 ??_C@_0BF@EIMMNHOL@rpcap?3?1?1?2Device?2NPF_?$AA@ 000000014000cec0 packet.obj + 0002:00000ed8 ??_C@_0N@IELFOPPD@Opening?3?5?$CFs?6?$AA@ 000000014000ced8 packet.obj + 0002:00000ee8 ??_C@_0P@CMACLOMI@ether?5proto?50x?$AA@ 000000014000cee8 packet.obj + 0002:00000ef8 ??_C@_03OIHFBGIJ@?$CFhx?$AA@ 000000014000cef8 packet.obj + 0002:00000f00 __real@43e0000000000000 000000014000cf00 CIL library: CIL module + 0002:00000f08 __real@41cdcd6500000000 000000014000cf08 CIL library: CIL module + 0002:00000f10 __real@43f0000000000000 000000014000cf10 CIL library: CIL module + 0002:00000f18 __real@3ff0e5604189374c 000000014000cf18 CIL library: CIL module + 0002:00000f20 __real@3ff0000000000000 000000014000cf20 CIL library: CIL module + 0002:00000fc8 __real@4008000000000000 000000014000cfc8 CIL library: CIL module + 0002:00000fd0 __real@4000000000000000 000000014000cfd0 CIL library: CIL module + 0002:00001050 __real@4024000000000000 000000014000d050 CIL library: CIL module + 0002:00001410 ??_R4type_info@@6B@ 000000014000d410 MSVCRT:ti_inst.obj + 0002:00001438 ??_R3type_info@@8 000000014000d438 MSVCRT:ti_inst.obj + 0002:00001450 ??_R2type_info@@8 000000014000d450 MSVCRT:ti_inst.obj + 0002:00001460 ??_R1A@?0A@EA@type_info@@8 000000014000d460 MSVCRT:ti_inst.obj + 0002:00001488 ??_R4bad_alloc@std@@6B@ 000000014000d488 avbts_osnet.obj + 0002:000014b0 ??_R3bad_alloc@std@@8 000000014000d4b0 avbts_osnet.obj + 0002:000014c8 ??_R2bad_alloc@std@@8 000000014000d4c8 avbts_osnet.obj + 0002:000014e0 ??_R1A@?0A@EA@exception@std@@8 000000014000d4e0 avbts_osnet.obj + 0002:00001508 ??_R3exception@std@@8 000000014000d508 avbts_osnet.obj + 0002:00001520 ??_R2exception@std@@8 000000014000d520 avbts_osnet.obj + 0002:00001530 ??_R1A@?0A@EA@bad_alloc@std@@8 000000014000d530 avbts_osnet.obj + 0002:00001558 ??_R4InterfaceLabel@@6B@ 000000014000d558 ieee1588port.obj + 0002:00001580 ??_R4LinkLayerAddress@@6B@ 000000014000d580 ieee1588port.obj + 0002:000015a8 ??_R3LinkLayerAddress@@8 000000014000d5a8 ieee1588port.obj + 0002:000015c0 ??_R2LinkLayerAddress@@8 000000014000d5c0 ieee1588port.obj + 0002:000015d8 ??_R1A@?0A@EA@InterfaceLabel@@8 000000014000d5d8 ieee1588port.obj + 0002:00001600 ??_R3InterfaceLabel@@8 000000014000d600 ieee1588port.obj + 0002:00001618 ??_R2InterfaceLabel@@8 000000014000d618 ieee1588port.obj + 0002:00001628 ??_R1A@?0A@EA@LinkLayerAddress@@8 000000014000d628 ieee1588port.obj + 0002:00001650 ??_R4PTPMessageFollowUp@@6B@ 000000014000d650 ptp_message.obj + 0002:00001678 ??_R3PTPMessageFollowUp@@8 000000014000d678 ptp_message.obj + 0002:00001690 ??_R2PTPMessageFollowUp@@8 000000014000d690 ptp_message.obj + 0002:000016a8 ??_R1A@?0A@EA@PTPMessageFollowUp@@8 000000014000d6a8 ptp_message.obj + 0002:000016d0 ??_R4PTPMessagePathDelayRespFollowUp@@6B@ 000000014000d6d0 ptp_message.obj + 0002:000016f8 ??_R3PTPMessagePathDelayRespFollowUp@@8 000000014000d6f8 ptp_message.obj + 0002:00001710 ??_R2PTPMessagePathDelayRespFollowUp@@8 000000014000d710 ptp_message.obj + 0002:00001728 ??_R1A@?0A@EA@PTPMessagePathDelayRespFollowUp@@8 000000014000d728 ptp_message.obj + 0002:00001750 ??_R4PTPMessagePathDelayResp@@6B@ 000000014000d750 ptp_message.obj + 0002:00001778 ??_R3PTPMessagePathDelayResp@@8 000000014000d778 ptp_message.obj + 0002:00001790 ??_R2PTPMessagePathDelayResp@@8 000000014000d790 ptp_message.obj + 0002:000017a8 ??_R1A@?0A@EA@PTPMessagePathDelayResp@@8 000000014000d7a8 ptp_message.obj + 0002:000017d0 ??_R4PTPMessagePathDelayReq@@6B@ 000000014000d7d0 ptp_message.obj + 0002:000017f8 ??_R3PTPMessagePathDelayReq@@8 000000014000d7f8 ptp_message.obj + 0002:00001810 ??_R2PTPMessagePathDelayReq@@8 000000014000d810 ptp_message.obj + 0002:00001828 ??_R1A@?0A@EA@PTPMessagePathDelayReq@@8 000000014000d828 ptp_message.obj + 0002:00001850 ??_R4PTPMessageAnnounce@@6B@ 000000014000d850 ptp_message.obj + 0002:00001878 ??_R3PTPMessageAnnounce@@8 000000014000d878 ptp_message.obj + 0002:00001890 ??_R2PTPMessageAnnounce@@8 000000014000d890 ptp_message.obj + 0002:000018a8 ??_R1A@?0A@EA@PTPMessageAnnounce@@8 000000014000d8a8 ptp_message.obj + 0002:000018d0 ??_R4PTPMessageSync@@6B@ 000000014000d8d0 ptp_message.obj + 0002:000018f8 ??_R3PTPMessageSync@@8 000000014000d8f8 ptp_message.obj + 0002:00001910 ??_R2PTPMessageSync@@8 000000014000d910 ptp_message.obj + 0002:00001928 ??_R1A@?0A@EA@PTPMessageSync@@8 000000014000d928 ptp_message.obj + 0002:00001950 ??_R4PTPMessageCommon@@6B@ 000000014000d950 ptp_message.obj + 0002:00001978 ??_R3PTPMessageCommon@@8 000000014000d978 ptp_message.obj + 0002:00001990 ??_R2PTPMessageCommon@@8 000000014000d990 ptp_message.obj + 0002:000019a0 ??_R1A@?0A@EA@PTPMessageCommon@@8 000000014000d9a0 ptp_message.obj + 0002:000019c8 ??_R4WindowsNamedPipeIPC@@6B@ 000000014000d9c8 daemon_cl.obj + 0002:000019f0 ??_R3WindowsNamedPipeIPC@@8 000000014000d9f0 daemon_cl.obj + 0002:00001a08 ??_R2WindowsNamedPipeIPC@@8 000000014000da08 daemon_cl.obj + 0002:00001a20 ??_R1A@?0A@EA@WindowsNamedPipeIPC@@8 000000014000da20 daemon_cl.obj + 0002:00001a48 ??_R4WindowsTimestamper@@6B@ 000000014000da48 daemon_cl.obj + 0002:00001a70 ??_R3WindowsTimestamper@@8 000000014000da70 daemon_cl.obj + 0002:00001a88 ??_R2WindowsTimestamper@@8 000000014000da88 daemon_cl.obj + 0002:00001aa0 ??_R1A@?0A@EA@WindowsTimestamper@@8 000000014000daa0 daemon_cl.obj + 0002:00001ac8 ??_R4WindowsThreadFactory@@6B@ 000000014000dac8 daemon_cl.obj + 0002:00001af0 ??_R3WindowsThreadFactory@@8 000000014000daf0 daemon_cl.obj + 0002:00001b08 ??_R2WindowsThreadFactory@@8 000000014000db08 daemon_cl.obj + 0002:00001b20 ??_R1A@?0A@EA@WindowsThreadFactory@@8 000000014000db20 daemon_cl.obj + 0002:00001b48 ??_R4WindowsThread@@6B@ 000000014000db48 daemon_cl.obj + 0002:00001b70 ??_R3WindowsThread@@8 000000014000db70 daemon_cl.obj + 0002:00001b88 ??_R2WindowsThread@@8 000000014000db88 daemon_cl.obj + 0002:00001ba0 ??_R1A@?0A@EA@WindowsThread@@8 000000014000dba0 daemon_cl.obj + 0002:00001bc8 ??_R4WindowsTimerFactory@@6B@ 000000014000dbc8 daemon_cl.obj + 0002:00001bf0 ??_R3WindowsTimerFactory@@8 000000014000dbf0 daemon_cl.obj + 0002:00001c08 ??_R2WindowsTimerFactory@@8 000000014000dc08 daemon_cl.obj + 0002:00001c20 ??_R1A@?0A@EA@WindowsTimerFactory@@8 000000014000dc20 daemon_cl.obj + 0002:00001c48 ??_R4WindowsTimer@@6B@ 000000014000dc48 daemon_cl.obj + 0002:00001c70 ??_R3WindowsTimer@@8 000000014000dc70 daemon_cl.obj + 0002:00001c88 ??_R2WindowsTimer@@8 000000014000dc88 daemon_cl.obj + 0002:00001ca0 ??_R1A@?0A@EA@WindowsTimer@@8 000000014000dca0 daemon_cl.obj + 0002:00001cc8 ??_R4WindowsTimerQueueFactory@@6B@ 000000014000dcc8 daemon_cl.obj + 0002:00001cf0 ??_R3WindowsTimerQueueFactory@@8 000000014000dcf0 daemon_cl.obj + 0002:00001d08 ??_R2WindowsTimerQueueFactory@@8 000000014000dd08 daemon_cl.obj + 0002:00001d20 ??_R1A@?0A@EA@WindowsTimerQueueFactory@@8 000000014000dd20 daemon_cl.obj + 0002:00001d48 ??_R4WindowsTimerQueue@@6B@ 000000014000dd48 daemon_cl.obj + 0002:00001d70 ??_R3WindowsTimerQueue@@8 000000014000dd70 daemon_cl.obj + 0002:00001d88 ??_R2WindowsTimerQueue@@8 000000014000dd88 daemon_cl.obj + 0002:00001da0 ??_R1A@?0A@EA@WindowsTimerQueue@@8 000000014000dda0 daemon_cl.obj + 0002:00001dc8 ??_R4WindowsConditionFactory@@6B@ 000000014000ddc8 daemon_cl.obj + 0002:00001df0 ??_R3WindowsConditionFactory@@8 000000014000ddf0 daemon_cl.obj + 0002:00001e08 ??_R2WindowsConditionFactory@@8 000000014000de08 daemon_cl.obj + 0002:00001e20 ??_R1A@?0A@EA@WindowsConditionFactory@@8 000000014000de20 daemon_cl.obj + 0002:00001e48 ??_R4WindowsCondition@@6B@ 000000014000de48 daemon_cl.obj + 0002:00001e70 ??_R3WindowsCondition@@8 000000014000de70 daemon_cl.obj + 0002:00001e88 ??_R2WindowsCondition@@8 000000014000de88 daemon_cl.obj + 0002:00001ea0 ??_R1A@?0A@EA@WindowsCondition@@8 000000014000dea0 daemon_cl.obj + 0002:00001ec8 ??_R4WindowsLockFactory@@6B@ 000000014000dec8 daemon_cl.obj + 0002:00001ef0 ??_R3WindowsLockFactory@@8 000000014000def0 daemon_cl.obj + 0002:00001f08 ??_R2WindowsLockFactory@@8 000000014000df08 daemon_cl.obj + 0002:00001f20 ??_R1A@?0A@EA@WindowsLockFactory@@8 000000014000df20 daemon_cl.obj + 0002:00001f48 ??_R4WindowsLock@@6B@ 000000014000df48 daemon_cl.obj + 0002:00001f70 ??_R3WindowsLock@@8 000000014000df70 daemon_cl.obj + 0002:00001f88 ??_R2WindowsLock@@8 000000014000df88 daemon_cl.obj + 0002:00001fa0 ??_R1A@?0A@EA@WindowsLock@@8 000000014000dfa0 daemon_cl.obj + 0002:00001fc8 ??_R4WindowsPCAPNetworkInterfaceFactory@@6B@ 000000014000dfc8 daemon_cl.obj + 0002:00001ff0 ??_R3WindowsPCAPNetworkInterfaceFactory@@8 000000014000dff0 daemon_cl.obj + 0002:00002008 ??_R2WindowsPCAPNetworkInterfaceFactory@@8 000000014000e008 daemon_cl.obj + 0002:00002020 ??_R1A@?0A@EA@WindowsPCAPNetworkInterfaceFactory@@8 000000014000e020 daemon_cl.obj + 0002:00002048 ??_R4WindowsPCAPNetworkInterface@@6B@ 000000014000e048 daemon_cl.obj + 0002:00002070 ??_R3WindowsPCAPNetworkInterface@@8 000000014000e070 daemon_cl.obj + 0002:00002088 ??_R2WindowsPCAPNetworkInterface@@8 000000014000e088 daemon_cl.obj + 0002:000020a0 ??_R1A@?0A@EA@WindowsPCAPNetworkInterface@@8 000000014000e0a0 daemon_cl.obj + 0002:000020c8 ??_R3OS_IPC@@8 000000014000e0c8 daemon_cl.obj + 0002:000020e0 ??_R2OS_IPC@@8 000000014000e0e0 daemon_cl.obj + 0002:000020f0 ??_R1A@?0A@EA@OS_IPC@@8 000000014000e0f0 daemon_cl.obj + 0002:00002118 ??_R3OSTimerQueueFactory@@8 000000014000e118 daemon_cl.obj + 0002:00002130 ??_R2OSTimerQueueFactory@@8 000000014000e130 daemon_cl.obj + 0002:00002140 ??_R1A@?0A@EA@OSTimerQueueFactory@@8 000000014000e140 daemon_cl.obj + 0002:00002168 ??_R3OSTimerQueue@@8 000000014000e168 daemon_cl.obj + 0002:00002180 ??_R2OSTimerQueue@@8 000000014000e180 daemon_cl.obj + 0002:00002190 ??_R1A@?0A@EA@OSTimerQueue@@8 000000014000e190 daemon_cl.obj + 0002:000021b8 ??_R3OSConditionFactory@@8 000000014000e1b8 daemon_cl.obj + 0002:000021d0 ??_R2OSConditionFactory@@8 000000014000e1d0 daemon_cl.obj + 0002:000021e0 ??_R1A@?0A@EA@OSConditionFactory@@8 000000014000e1e0 daemon_cl.obj + 0002:00002208 ??_R3OSCondition@@8 000000014000e208 daemon_cl.obj + 0002:00002220 ??_R2OSCondition@@8 000000014000e220 daemon_cl.obj + 0002:00002230 ??_R1A@?0A@EA@OSCondition@@8 000000014000e230 daemon_cl.obj + 0002:00002258 ??_R3OSThreadFactory@@8 000000014000e258 daemon_cl.obj + 0002:00002270 ??_R2OSThreadFactory@@8 000000014000e270 daemon_cl.obj + 0002:00002280 ??_R1A@?0A@EA@OSThreadFactory@@8 000000014000e280 daemon_cl.obj + 0002:000022a8 ??_R3OSThread@@8 000000014000e2a8 daemon_cl.obj + 0002:000022c0 ??_R2OSThread@@8 000000014000e2c0 daemon_cl.obj + 0002:000022d0 ??_R1A@?0A@EA@OSThread@@8 000000014000e2d0 daemon_cl.obj + 0002:000022f8 ??_R3OSLockFactory@@8 000000014000e2f8 daemon_cl.obj + 0002:00002310 ??_R2OSLockFactory@@8 000000014000e310 daemon_cl.obj + 0002:00002320 ??_R1A@?0A@EA@OSLockFactory@@8 000000014000e320 daemon_cl.obj + 0002:00002348 ??_R3OSLock@@8 000000014000e348 daemon_cl.obj + 0002:00002360 ??_R2OSLock@@8 000000014000e360 daemon_cl.obj + 0002:00002370 ??_R1A@?0A@EA@OSLock@@8 000000014000e370 daemon_cl.obj + 0002:00002398 ??_R3OSTimerFactory@@8 000000014000e398 daemon_cl.obj + 0002:000023b0 ??_R2OSTimerFactory@@8 000000014000e3b0 daemon_cl.obj + 0002:000023c0 ??_R1A@?0A@EA@OSTimerFactory@@8 000000014000e3c0 daemon_cl.obj + 0002:000023e8 ??_R3OSTimer@@8 000000014000e3e8 daemon_cl.obj + 0002:00002400 ??_R2OSTimer@@8 000000014000e400 daemon_cl.obj + 0002:00002410 ??_R1A@?0A@EA@OSTimer@@8 000000014000e410 daemon_cl.obj + 0002:00002438 ??_R3OSNetworkInterfaceFactory@@8 000000014000e438 daemon_cl.obj + 0002:00002450 ??_R2OSNetworkInterfaceFactory@@8 000000014000e450 daemon_cl.obj + 0002:00002460 ??_R1A@?0A@EA@OSNetworkInterfaceFactory@@8 000000014000e460 daemon_cl.obj + 0002:00002488 ??_R3OSNetworkInterface@@8 000000014000e488 daemon_cl.obj + 0002:000024a0 ??_R2OSNetworkInterface@@8 000000014000e4a0 daemon_cl.obj + 0002:000024b0 ??_R1A@?0A@EA@OSNetworkInterface@@8 000000014000e4b0 daemon_cl.obj + 0002:000024d8 ??_R4HWTimestamper@@6B@ 000000014000e4d8 daemon_cl.obj + 0002:00002500 ??_R3HWTimestamper@@8 000000014000e500 daemon_cl.obj + 0002:00002518 ??_R2HWTimestamper@@8 000000014000e518 daemon_cl.obj + 0002:00002528 ??_R1A@?0A@EA@HWTimestamper@@8 000000014000e528 daemon_cl.obj + 0002:00002550 __rtc_iaa 000000014000e550 MSVCRT:_initsect_.obj + 0002:00002558 __rtc_izz 000000014000e558 MSVCRT:_initsect_.obj + 0002:00002560 __rtc_taa 000000014000e560 MSVCRT:_initsect_.obj + 0002:00002568 __rtc_tzz 000000014000e568 MSVCRT:_initsect_.obj + 0002:00003750 _CT??_R0?AVbad_alloc@std@@@8??0bad_alloc@std@@QEAA@AEBV01@@Z24 000000014000f750 avbts_osnet.obj + 0002:00003778 _CT??_R0?AVexception@std@@@8??0exception@std@@QEAA@AEBV01@@Z24 000000014000f778 avbts_osnet.obj + 0002:000037a0 _CTA2?AVbad_alloc@std@@ 000000014000f7a0 avbts_osnet.obj + 0002:000037b8 _TI2?AVbad_alloc@std@@ 000000014000f7b8 avbts_osnet.obj + 0002:000037d4 __IMPORT_DESCRIPTOR_wpcap 000000014000f7d4 wpcap:wpcap.dll + 0002:000037e8 __IMPORT_DESCRIPTOR_IPHLPAPI 000000014000f7e8 Iphlpapi:IPHLPAPI.DLL + 0002:000037fc __IMPORT_DESCRIPTOR_WS2_32 000000014000f7fc ws2_32:WS2_32.dll + 0002:00003810 __IMPORT_DESCRIPTOR_KERNEL32 000000014000f810 kernel32:KERNEL32.dll + 0002:00003824 __IMPORT_DESCRIPTOR_MSVCP100 000000014000f824 msvcprt:MSVCP100.dll + 0002:00003838 __IMPORT_DESCRIPTOR_MSVCR100 000000014000f838 MSVCRT:MSVCR100.dll + 0002:0000384c __NULL_IMPORT_DESCRIPTOR 000000014000f84c wpcap:wpcap.dll + 0003:00000000 ??_R0?AVtype_info@@@8 0000000140011000 MSVCRT:ti_inst.obj + 0003:00000020 __security_cookie 0000000140011020 MSVCRT:gs_cookie.obj + 0003:00000028 __security_cookie_complement 0000000140011028 MSVCRT:gs_cookie.obj + 0003:00000030 __native_dllmain_reason 0000000140011030 MSVCRT:natstart.obj + 0003:00000034 __native_vcclrit_reason 0000000140011034 MSVCRT:natstart.obj + 0003:00000038 __globallocalestatus 0000000140011038 MSVCRT:xthdloc.obj + 0003:0000003c __defaultmatherr 000000014001103c MSVCRT:merr.obj + 0003:00000040 ??_R0?AVbad_alloc@std@@@8 0000000140011040 avbts_osnet.obj + 0003:00000068 ??_R0?AVexception@std@@@8 0000000140011068 avbts_osnet.obj + 0003:00000090 ??_R0?AVInterfaceLabel@@@8 0000000140011090 ieee1588port.obj + 0003:000000b8 ??_R0?AVLinkLayerAddress@@@8 00000001400110b8 ieee1588port.obj + 0003:000000e0 ??_R0?AVPTPMessageFollowUp@@@8 00000001400110e0 ptp_message.obj + 0003:00000110 ??_R0?AVPTPMessagePathDelayRespFollowUp@@@8 0000000140011110 ptp_message.obj + 0003:00000148 ??_R0?AVPTPMessagePathDelayResp@@@8 0000000140011148 ptp_message.obj + 0003:00000178 ??_R0?AVPTPMessagePathDelayReq@@@8 0000000140011178 ptp_message.obj + 0003:000001a8 ??_R0?AVPTPMessageAnnounce@@@8 00000001400111a8 ptp_message.obj + 0003:000001d8 ??_R0?AVPTPMessageSync@@@8 00000001400111d8 ptp_message.obj + 0003:00000200 ??_R0?AVPTPMessageCommon@@@8 0000000140011200 ptp_message.obj + 0003:00000228 ??_R0?AVWindowsNamedPipeIPC@@@8 0000000140011228 daemon_cl.obj + 0003:00000258 ??_R0?AVWindowsTimestamper@@@8 0000000140011258 daemon_cl.obj + 0003:00000288 ??_R0?AVWindowsThreadFactory@@@8 0000000140011288 daemon_cl.obj + 0003:000002b8 ??_R0?AVWindowsThread@@@8 00000001400112b8 daemon_cl.obj + 0003:000002e0 ??_R0?AVWindowsTimerFactory@@@8 00000001400112e0 daemon_cl.obj + 0003:00000310 ??_R0?AVWindowsTimer@@@8 0000000140011310 daemon_cl.obj + 0003:00000338 ??_R0?AVWindowsTimerQueueFactory@@@8 0000000140011338 daemon_cl.obj + 0003:00000368 ??_R0?AVWindowsTimerQueue@@@8 0000000140011368 daemon_cl.obj + 0003:00000390 ??_R0?AVWindowsConditionFactory@@@8 0000000140011390 daemon_cl.obj + 0003:000003c0 ??_R0?AVWindowsCondition@@@8 00000001400113c0 daemon_cl.obj + 0003:000003e8 ??_R0?AVWindowsLockFactory@@@8 00000001400113e8 daemon_cl.obj + 0003:00000418 ??_R0?AVWindowsLock@@@8 0000000140011418 daemon_cl.obj + 0003:00000440 ??_R0?AVWindowsPCAPNetworkInterfaceFactory@@@8 0000000140011440 daemon_cl.obj + 0003:00000480 ??_R0?AVWindowsPCAPNetworkInterface@@@8 0000000140011480 daemon_cl.obj + 0003:000004b8 ??_R0?AVOS_IPC@@@8 00000001400114b8 daemon_cl.obj + 0003:000004d8 ??_R0?AVOSTimerQueueFactory@@@8 00000001400114d8 daemon_cl.obj + 0003:00000508 ??_R0?AVOSTimerQueue@@@8 0000000140011508 daemon_cl.obj + 0003:00000530 ??_R0?AVOSConditionFactory@@@8 0000000140011530 daemon_cl.obj + 0003:00000560 ??_R0?AVOSCondition@@@8 0000000140011560 daemon_cl.obj + 0003:00000588 ??_R0?AVOSThreadFactory@@@8 0000000140011588 daemon_cl.obj + 0003:000005b0 ??_R0?AVOSThread@@@8 00000001400115b0 daemon_cl.obj + 0003:000005d0 ??_R0?AVOSLockFactory@@@8 00000001400115d0 daemon_cl.obj + 0003:000005f8 ??_R0?AVOSLock@@@8 00000001400115f8 daemon_cl.obj + 0003:00000618 ??_R0?AVOSTimerFactory@@@8 0000000140011618 daemon_cl.obj + 0003:00000640 ??_R0?AVOSTimer@@@8 0000000140011640 daemon_cl.obj + 0003:00000660 ??_R0?AVOSNetworkInterfaceFactory@@@8 0000000140011660 daemon_cl.obj + 0003:00000690 ??_R0?AVOSNetworkInterface@@@8 0000000140011690 daemon_cl.obj + 0003:000006c0 ??_R0?AVHWTimestamper@@@8 00000001400116c0 daemon_cl.obj + 0003:000006e8 ?other_multicast@IEEE1588Port@@0VLinkLayerAddress@@A 00000001400116e8 ieee1588port.obj + 0003:000006f8 ?pdelay_multicast@IEEE1588Port@@0VLinkLayerAddress@@A 00000001400116f8 ieee1588port.obj + 0003:00000710 _fltused 0000000140011710 MSVCRT:dllsupp.obj + 0003:00000cc0 _dowildcard 0000000140011cc0 MSVCRT:wildcard.obj + 0003:00000cc4 _newmode 0000000140011cc4 MSVCRT:_newmode.obj + 0003:00000cc8 _commode 0000000140011cc8 MSVCRT:xncommod.obj + 0003:00000ccc _fmode 0000000140011ccc MSVCRT:xtxtmode.obj + 0003:00000cd8 ?factoryMap@OSNetworkInterfaceFactory@@0V?$map@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@@std@@A 0000000140011cd8 avbts_osnet.obj + 0003:00000cf8 __native_startup_state 0000000140011cf8 + 0003:00000d00 __native_startup_lock 0000000140011d00 + 0003:00000d08 __dyn_tls_init_callback 0000000140011d08 + 0003:00000d10 __onexitend 0000000140011d10 + 0003:00000d18 __onexitbegin 0000000140011d18 + + entry point at 0001:0000991c + + Static symbols + + 0001:00009664 pre_cpp_init 000000014000a664 f MSVCRT:crtexe.obj + 0001:000096cc __tmainCRTStartup 000000014000a6cc f MSVCRT:crtexe.obj + 0001:0000984c pre_c_init 000000014000a84c f MSVCRT:crtexe.obj + 0001:00009f60 _onexit$fin$0 000000014000af60 f MSVCRT:atonexit.obj + 0001:00009f7b __tmainCRTStartup$filt$0 000000014000af7b f MSVCRT:crtexe.obj + 0001:00009f99 ?filt$0@?0??__ArrayUnwind@@YAXPEAX_KHP6AX0@Z@Z@4HA 000000014000af99 f MSVCRT:ehvecdtr.obj + 0001:00009fda ?fin$0@?0???_M@YAXPEAX_KHP6AX0@Z@Z@4HA 000000014000afda f MSVCRT:ehvecdtr.obj + 0001:0000a010 _IsNonwritableInCurrentImage$filt$0 000000014000b010 f MSVCRT:pesect.obj + 0001:0000a040 ?catch$0@?0???$_Insert@V?$_List_const_iterator@V?$_List_val@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@@std@@@?$list@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@QEAAXV?$_List_const_iterator@V?$_List_val@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@@1@00Uforward_iterator_tag@1@@Z@4HA 000000014000b040 f CIL library: CIL module + 0001:0000a04f __catch$??$_Insert@V?$_List_const_iterator@V?$_List_val@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@@std@@@?$list@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@QEAAXV?$_List_const_iterator@V?$_List_val@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@@1@00Uforward_iterator_tag@1@@Z$0 000000014000b04f f CIL library: CIL module + 0001:0000a0b0 ?dtor$0@?0???0?$list@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@QEAA@AEBV01@@Z@4HA 000000014000b0b0 f CIL library: CIL module + 0001:0000a0c0 ?catch$0@?0???0?$list@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@QEAA@AEBV01@@Z@4HA 000000014000b0c0 f CIL library: CIL module + 0001:0000a0cd __catch$??0?$list@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@QEAA@AEBV01@@Z$0 000000014000b0cd f CIL library: CIL module + 0001:0000a0e0 ?catch$0@?0??_Buynode@?$_List_val@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@QEAAPEAU_Node@?$_List_nod@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@2@PEAU342@0AEBQEAUWindowsTimerQueueHandlerArg@@@Z@4HA 000000014000b0e0 f CIL library: CIL module + 0001:0000a0e0 ?catch$0@?0??_Buynode@?$_List_val@PEAVPTPMessageAnnounce@@V?$allocator@PEAVPTPMessageAnnounce@@@std@@@std@@QEAAPEAU_Node@?$_List_nod@PEAVPTPMessageAnnounce@@V?$allocator@PEAVPTPMessageAnnounce@@@std@@@2@PEAU342@0AEBQEAVPTPMessageAnnounce@@@Z@4HA 000000014000b0e0 f CIL library: CIL module + 0001:0000a0ed __catch$?_Buynode@?$_List_val@PEAVPTPMessageAnnounce@@V?$allocator@PEAVPTPMessageAnnounce@@@std@@@std@@QEAAPEAU_Node@?$_List_nod@PEAVPTPMessageAnnounce@@V?$allocator@PEAVPTPMessageAnnounce@@@std@@@2@PEAU342@0AEBQEAVPTPMessageAnnounce@@@Z$0 000000014000b0ed f CIL library: CIL module + 0001:0000a0ed __catch$?_Buynode@?$_List_val@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@std@@QEAAPEAU_Node@?$_List_nod@PEAUWindowsTimerQueueHandlerArg@@V?$allocator@PEAUWindowsTimerQueueHandlerArg@@@std@@@2@PEAU342@0AEBQEAUWindowsTimerQueueHandlerArg@@@Z$0 000000014000b0ed f CIL library: CIL module + 0001:0000a110 ?dtor$0@?0???0PTPMessagePathDelayResp@@QEAA@PEAVIEEE1588Port@@@Z@4HA 000000014000b110 f CIL library: CIL module + 0001:0000a110 ?dtor$0@?0???0PTPMessageAnnounce@@QEAA@PEAVIEEE1588Port@@@Z@4HA 000000014000b110 f CIL library: CIL module + 0001:0000a110 ?dtor$0@?0???0PTPMessagePathDelayRespFollowUp@@QEAA@PEAVIEEE1588Port@@@Z@4HA 000000014000b110 f CIL library: CIL module + 0001:0000a120 ?catch$0@?0???$_Buynode@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@?$_Tree_val@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@QEAAPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@1@$$QEAU?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@1@@Z@4HA 000000014000b120 f CIL library: CIL module + 0001:0000a12d __catch$??$_Buynode@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@?$_Tree_val@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@std@@QEAAPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@$0A@@std@@@1@$$QEAU?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@1@@Z$0 000000014000b12d f CIL library: CIL module + 0001:0000a150 ?catch$0@?0???$_Buynode@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@?$_Tree_val@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@QEAAPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@1@$$QEAU?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@1@@Z@4HA 000000014000b150 f CIL library: CIL module + 0001:0000a15d __catch$??$_Buynode@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@?$_Tree_val@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@std@@QEAAPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@$0A@@std@@@1@$$QEAU?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@1@@Z$0 000000014000b15d f CIL library: CIL module + 0001:0000a180 ?dtor$0@?0???$_Buynode@U?$pair@$$CBHUTimerQueue_t@@@std@@@?$_Tree_val@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@QEAAPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@1@$$QEAU?$pair@$$CBHUTimerQueue_t@@@1@@Z@4HA 000000014000b180 f CIL library: CIL module + 0001:0000a1a0 ?catch$0@?0???$_Buynode@U?$pair@$$CBHUTimerQueue_t@@@std@@@?$_Tree_val@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@QEAAPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@1@$$QEAU?$pair@$$CBHUTimerQueue_t@@@1@@Z@4HA 000000014000b1a0 f CIL library: CIL module + 0001:0000a1ad __catch$??$_Buynode@U?$pair@$$CBHUTimerQueue_t@@@std@@@?$_Tree_val@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@std@@QEAAPEAU_Node@?$_Tree_nod@V?$_Tmap_traits@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@$0A@@std@@@1@$$QEAU?$pair@$$CBHUTimerQueue_t@@@1@@Z$0 000000014000b1ad f CIL library: CIL module + 0001:0000a1d0 ?dtor$1@?0???1?$map@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@@std@@QEAA@XZ@4HA 000000014000b1d0 f CIL library: CIL module + 0001:0000a1d0 ?dtor$1@?0???1?$map@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@@std@@QEAA@XZ@4HA 000000014000b1d0 f CIL library: CIL module + 0001:0000a1e0 ?dtor$0@?0???0WindowsTimerQueue@@IEAA@XZ@4HA 000000014000b1e0 f CIL library: CIL module + 0001:0000a1f0 ?dtor$0@?0???A?$map@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@@std@@QEAAAEAVLinkLayerAddress@@AEBVPortIdentity@@@Z@4HA 000000014000b1f0 f CIL library: CIL module + 0001:0000a200 ?dtor$1@?0???A?$map@VPortIdentity@@VLinkLayerAddress@@U?$less@VPortIdentity@@@std@@V?$allocator@U?$pair@$$CBVPortIdentity@@VLinkLayerAddress@@@std@@@4@@std@@QEAAAEAVLinkLayerAddress@@AEBVPortIdentity@@@Z@4HA 000000014000b200 f CIL library: CIL module + 0001:0000a210 ?dtor$0@?0???0IEEE1588Port@@QEAA@PEAVIEEE1588Clock@@G_NPEAVHWTimestamper@@1HPEAVInterfaceLabel@@PEAVOSConditionFactory@@PEAVOSThreadFactory@@PEAVOSTimerFactory@@PEAVOSLockFactory@@@Z@4HA 000000014000b210 f CIL library: CIL module + 0001:0000a220 ?dtor$1@?0???0IEEE1588Port@@QEAA@PEAVIEEE1588Clock@@G_NPEAVHWTimestamper@@1HPEAVInterfaceLabel@@PEAVOSConditionFactory@@PEAVOSThreadFactory@@PEAVOSTimerFactory@@PEAVOSLockFactory@@@Z@4HA 000000014000b220 f CIL library: CIL module + 0001:0000a240 ?dtor$2@?0???0IEEE1588Port@@QEAA@PEAVIEEE1588Clock@@G_NPEAVHWTimestamper@@1HPEAVInterfaceLabel@@PEAVOSConditionFactory@@PEAVOSThreadFactory@@PEAVOSTimerFactory@@PEAVOSLockFactory@@@Z@4HA 000000014000b240 f CIL library: CIL module + 0001:0000a260 ?dtor$0@?0???A?$map@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@@std@@QEAAAEAUTimerQueue_t@@AEBH@Z@4HA 000000014000b260 f CIL library: CIL module + 0001:0000a270 ?dtor$1@?0???A?$map@HUTimerQueue_t@@U?$less@H@std@@V?$allocator@U?$pair@$$CBHUTimerQueue_t@@@std@@@3@@std@@QEAAAEAUTimerQueue_t@@AEBH@Z@4HA 000000014000b270 f CIL library: CIL module + 0001:0000a280 ?dtor$0@?0??createOSTimerQueue@WindowsTimerQueueFactory@@UEAAPEAVOSTimerQueue@@XZ@4HA 000000014000b280 f CIL library: CIL module + 0001:0000a290 ?dtor$8@?0??buildPTPMessage@@YAPEAVPTPMessageCommon@@PEADHPEAVLinkLayerAddress@@PEAVIEEE1588Port@@@Z@4HA 000000014000b290 f CIL library: CIL module + 0001:0000a2a0 ?dtor$10@?0??buildPTPMessage@@YAPEAVPTPMessageCommon@@PEADHPEAVLinkLayerAddress@@PEAVIEEE1588Port@@@Z@4HA 000000014000b2a0 f CIL library: CIL module + 0001:0000a2b0 ?dtor$0@?0??openPort@IEEE1588Port@@QEAAPEAXXZ@4HA 000000014000b2b0 f CIL library: CIL module + 0001:0000a2c0 ?dtor$0@?0??processMessage@PTPMessagePathDelayReq@@UEAAXPEAVIEEE1588Port@@@Z@4HA 000000014000b2c0 f CIL library: CIL module + 0001:0000a2d0 ?dtor$1@?0??processMessage@PTPMessagePathDelayReq@@UEAAXPEAVIEEE1588Port@@@Z@4HA 000000014000b2d0 f CIL library: CIL module + 0001:0000a2e0 ?dtor$0@?0??sendPort@PTPMessagePathDelayReq@@QEAAXPEAVIEEE1588Port@@PEAVPortIdentity@@@Z@4HA 000000014000b2e0 f CIL library: CIL module + 0001:0000a2e0 ?dtor$0@?0??sendPort@PTPMessagePathDelayResp@@QEAAXPEAVIEEE1588Port@@PEAVPortIdentity@@@Z@4HA 000000014000b2e0 f CIL library: CIL module + 0001:0000a2e0 ?dtor$0@?0??sendPort@PTPMessagePathDelayRespFollowUp@@QEAAXPEAVIEEE1588Port@@PEAVPortIdentity@@@Z@4HA 000000014000b2e0 f CIL library: CIL module + 0001:0000a2e0 ?dtor$0@?0??sendPort@PTPMessageAnnounce@@QEAAXPEAVIEEE1588Port@@PEAVPortIdentity@@@Z@4HA 000000014000b2e0 f CIL library: CIL module + 0001:0000a2e0 ?dtor$0@?0??sendPort@PTPMessageFollowUp@@QEAAXPEAVIEEE1588Port@@PEAVPortIdentity@@@Z@4HA 000000014000b2e0 f CIL library: CIL module + 0001:0000a2e0 ?dtor$0@?0??sendPort@PTPMessageSync@@QEAAXPEAVIEEE1588Port@@PEAVPortIdentity@@@Z@4HA 000000014000b2e0 f CIL library: CIL module + 0001:0000a2f0 ?dtor$0@?0??processEvent@IEEE1588Port@@QEAAXW4Event@@@Z@4HA 000000014000b2f0 f CIL library: CIL module + 0001:0000a300 ?dtor$1@?0??processEvent@IEEE1588Port@@QEAAXW4Event@@@Z@4HA 000000014000b300 f CIL library: CIL module + 0001:0000a310 ?dtor$2@?0??processEvent@IEEE1588Port@@QEAAXW4Event@@@Z@4HA 000000014000b310 f CIL library: CIL module + 0001:0000a320 ?dtor$3@?0??processEvent@IEEE1588Port@@QEAAXW4Event@@@Z@4HA 000000014000b320 f CIL library: CIL module + 0001:0000a330 main$dtor$1 000000014000b330 f CIL library: CIL module + 0001:0000a340 main$dtor$2 000000014000b340 f CIL library: CIL module + 0001:0000a350 main$dtor$3 000000014000b350 f CIL library: CIL module + 0001:0000a360 ?dtor$1@?0???__F?factoryMap@OSNetworkInterfaceFactory@@0V?$map@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@@std@@A@@YAXXZ@4HA 000000014000b360 f CIL library: CIL module + 0001:0000a370 ??__E?factoryMap@OSNetworkInterfaceFactory@@0V?$map@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@@std@@A@@YAXXZ 000000014000b370 f CIL library: CIL module + 0001:0000a390 ??__E?other_multicast@IEEE1588Port@@0VLinkLayerAddress@@A@@YAXXZ 000000014000b390 f CIL library: CIL module + 0001:0000a3e0 ??__E?pdelay_multicast@IEEE1588Port@@0VLinkLayerAddress@@A@@YAXXZ 000000014000b3e0 f CIL library: CIL module + 0001:0000a430 ??__F?factoryMap@OSNetworkInterfaceFactory@@0V?$map@Vfactory_name_t@@PEAVOSNetworkInterfaceFactory@@U?$less@Vfactory_name_t@@@std@@V?$allocator@U?$pair@$$CBVfactory_name_t@@PEAVOSNetworkInterfaceFactory@@@std@@@4@@std@@A@@YAXXZ 000000014000b430 f CIL library: CIL module + 0001:0000a470 ??__F?other_multicast@IEEE1588Port@@0VLinkLayerAddress@@A@@YAXXZ 000000014000b470 f CIL library: CIL module + 0001:0000a480 ??__F?pdelay_multicast@IEEE1588Port@@0VLinkLayerAddress@@A@@YAXXZ 000000014000b480 f CIL library: CIL module diff --git a/daemons/mrpd/README b/daemons/mrpd/README new file mode 100644 index 0000000..e68d938 --- /dev/null +++ b/daemons/mrpd/README @@ -0,0 +1,11 @@ +INTRODUCTION + +The MRP daemon is required to establish stream reservations with compatible AVB +infrastructure devices. The command line selectively enables MMRP (via the +-m option), MVRP (via the -v option), and MSRP (via the -s option). You must +also specify the interface on which you want to bind the daemon to +(e.g. -i eth2). The full command line typically appears as follows: + sudo ./mrpd -mvs -i eth2 + +Sample client applications - mrpctl, mrpq, mrpl - illustrate how to connect, +query and add attributes to the MRP daemon. diff --git a/examples/Makefile b/examples/Makefile deleted file mode 100644 index a25b198..0000000 --- a/examples/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -OPT=-O2 -#CFLAGS=$(OPT) -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -ggdb -CFLAGS=$(OPT) -Wall -W -Wno-parentheses -ggdb - -CC=gcc -INCFLAGS=-I../lib -LDLIBS=-ligb -lpci -lz -pthread -LDFLAGS=-L../lib - -all: test latency_test simple_talker mrpq mrpl - -test: test.o - -simple_talker: simple_talker.o - -mrpl: mrpl.o - -mrpq: mrpq.o - -latency_test: latency_test.o - -latency_test.o: latency_test.c - gcc -c $(INCFLAGS) $(CFLAGS) latency_test.c - -test.o: test.c - gcc -c $(INCFLAGS) $(CFLAGS) test.c - -simple_talker.o: simple_talker.c - gcc -c $(INCFLAGS) -I../daemons/mrpd $(CFLAGS) simple_talker.c - -mrpl.o: mrpl.c - gcc -c $(INCFLAGS) -I../daemons/mrpd $(CFLAGS) mrpl.c - -mrpq.o: mrpq.c - gcc -c $(INCFLAGS) -I../daemons/mrpd $(CFLAGS) mrpq.c - -%: %.o - $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ - -clean: - rm -f `find . -name "*~" -o -name "*.[oa]" -o -name "\#*\#" -o -name TAGS -o -name core -o -name "*.orig"` - diff --git a/examples/latency_test.c b/examples/latency_test.c deleted file mode 100644 index 70d0787..0000000 --- a/examples/latency_test.c +++ /dev/null @@ -1,305 +0,0 @@ -/****************************************************************************** - - Copyright (c) 2001-2012, Intel Corporation - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the name of the Intel Corporation nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - -******************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include - -#include "igb.h" - -#define IGB_BIND_NAMESZ 24 - -device_t igb_dev; -volatile int halt_tx; - -void -sigint_handler (int signum) -{ - printf("halting ...\n"); - halt_tx = signum; -} - -int -connect() -{ - struct pci_access *pacc; - struct pci_dev *dev; - int err; - char devpath[IGB_BIND_NAMESZ]; - - memset(&igb_dev, 0, sizeof(device_t)); - - pacc = pci_alloc(); - pci_init(pacc); - pci_scan_bus(pacc); - for (dev=pacc->devices; dev; dev=dev->next) - { - pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); - - igb_dev.pci_vendor_id = dev->vendor_id; - igb_dev.pci_device_id = dev->device_id; - igb_dev.domain = dev->domain; - igb_dev.bus = dev->bus; - igb_dev.dev = dev->dev; - igb_dev.func = dev->func; - - snprintf(devpath, IGB_BIND_NAMESZ, "%04x:%02x:%02x.%d", \ - dev->domain, dev->bus, dev->dev, dev->func ); - printf("probing %s\n", devpath); - - err = igb_probe(&igb_dev); - - if (err) { - continue; - } - - printf ("attaching to %s\n", devpath); - err = igb_attach(devpath, &igb_dev); - - if (err) { - printf ("attach failed! (%s)\n", strerror(errno)); - continue; - } - printf("attach successful to %s\n", devpath); - goto out; - } - - pci_cleanup(pacc); - return ENXIO; - -out: - pci_cleanup(pacc); - return 0; - -} - -#define VERSION_STR "1.0" - -static const char *version_str = -"latency test v" VERSION_STR "\n" -"Copyright (c) 2012, Intel Corporation\n"; - - -static void -usage( void ) { - fprintf(stderr, - "\n" - "usage: latency_test [-hr] [-d ]" - "\n" - "options:\n" - " -h show this message\n" - " -d observation window (seconds)\n" - " -p delay from sw schedule to hw transmit in usec (defaults to 20 msec)\n" - "\n" - "%s" - "\n", version_str); - exit(1); -} - -#define PACKET_IPG (20000000) /* 20 msec */ - -int -main(int argc, char *argv[]) { - int c; - unsigned i; - unsigned tqavctl; - int err; - struct igb_dma_alloc a_page; - struct igb_packet a_packet; - struct igb_packet *tmp_packet; - struct igb_packet *cleaned_packets; - struct igb_packet *free_packets; - u_int64_t last_time; - u_int64_t rdtsc0; - unsigned int captured_latency; - int obs_window = 1; - int obs_window_count; - int ipg = PACKET_IPG / 1000; - - unsigned int min = 0xFFFFFFFF; - unsigned int max = 0; - float avg = 0.0; - - for (;;) { - c = getopt(argc, argv, "hrp:d:"); - - if (c < 0) - break; - - switch (c) { - default: - case 'h': - usage(); - break; - case 'p': - sscanf(optarg, "%d", &ipg); - break; - case 'd': - sscanf(optarg, "%d", &obs_window); - break; - } - } - if (optind < argc) - usage(); - - ipg *= 1000; /* scale to nsec */ - - if (ipg > 400000000) { printf("specified delay is too large \n"); return(-1); } - - err = connect(); - - if (err) { printf("connect failed (%s)\n", strerror(errno)); return(errno); } - - err = igb_init(&igb_dev); - - if (err) { printf("init failed (%s)\n", strerror(errno)); return(errno); } - - err = igb_dma_malloc_page(&igb_dev, &a_page); - - if (err) { printf("malloc failed (%s)\n", strerror(errno)); return(errno); } - -#define PKT_SZ 200 - memset(&a_packet, 0, sizeof(a_packet)); - - a_packet.dmatime = a_packet.attime = a_packet.flags = 0; - a_packet.map.paddr = a_page.dma_paddr; - a_packet.map.mmap_size = a_page.mmap_size; - - a_packet.offset = 0; - a_packet.vaddr = a_page.dma_vaddr + a_packet.offset; - a_packet.len = PKT_SZ; - - free_packets = NULL; - - /* divide the dma page into buffers for packets */ - for (i = 1; i < ((a_page.mmap_size) / PKT_SZ); i++) { - tmp_packet = malloc(sizeof(struct igb_packet)); - if (NULL == tmp_packet) { printf("malloc failed (%s)\n", strerror(errno)); return(errno); } - *tmp_packet = a_packet; - tmp_packet->offset = (i * PKT_SZ); - tmp_packet->vaddr += tmp_packet->offset; - tmp_packet->next = free_packets; - memset(tmp_packet->vaddr, 0xffffffff, PKT_SZ); /* MAC header at least */ - free_packets = tmp_packet; - } - - igb_set_class_bandwidth(&igb_dev, 0, 0, 0); /* disable Qav */ - igb_readreg(&igb_dev, 0x3570, &tqavctl); - tqavctl &= 0xFFFF; /* zero-out the prefetch delay */ - igb_writereg(&igb_dev, 0x3570, tqavctl); - - halt_tx = 0; - signal(SIGINT, sigint_handler); - - - igb_get_wallclock(&igb_dev, &last_time, &rdtsc0); - - obs_window_count = obs_window; - - while (!halt_tx) { - tmp_packet = free_packets; - - if (NULL == tmp_packet) goto cleanup; - - free_packets = tmp_packet->next; - - igb_get_wallclock(&igb_dev, &last_time, &rdtsc0); - - tmp_packet->attime = last_time + ipg; - *(u_int64_t *)(tmp_packet->vaddr + 32) = tmp_packet->attime; - - err = igb_xmit(&igb_dev, 0, tmp_packet); - - if (ENOSPC == err) { - /* put back for now */ - tmp_packet->next = free_packets; - free_packets = tmp_packet; - } - - sleep(1); /* sleep 1 sec */ - -cleanup: - igb_clean(&igb_dev, &cleaned_packets); - i = 0; - while (cleaned_packets) { - i++; - tmp_packet = cleaned_packets; - cleaned_packets = cleaned_packets->next; - /* remap attime to compare to dma time */ - while (tmp_packet->attime > 999999999) tmp_packet->attime -= 1000000000; - if ((tmp_packet->attime > 999000000) && (tmp_packet->dmatime < 1000000)) - captured_latency = (unsigned int)(1000000000 - tmp_packet->attime + tmp_packet->dmatime); - else - captured_latency = (unsigned int)(tmp_packet->dmatime - tmp_packet->attime); - - if (captured_latency < min) - min = captured_latency; - - if (captured_latency > max) - max = captured_latency; - - avg += ((float)captured_latency / (float)obs_window); - - tmp_packet->next = free_packets; - free_packets = tmp_packet; - } - obs_window_count--; - if (0 == obs_window_count) { - obs_window_count = obs_window; - printf("min = %d max = %d avg = %f\n", min, max, avg); - min = 0xFFFFFFFF; - max = 0; - avg = 0.0; - } - - } - - igb_dma_free_page(&igb_dev, &a_page); - err = igb_detach(&igb_dev); - return(0); -} - diff --git a/examples/mrp_client/Makefile b/examples/mrp_client/Makefile new file mode 100644 index 0000000..a9d5414 --- /dev/null +++ b/examples/mrp_client/Makefile @@ -0,0 +1,26 @@ +OPT=-O2 +CFLAGS=$(OPT) -Wall -W -Wno-parentheses -ggdb + +CC=gcc +INCFLAGS=-I../../lib/igb +LDLIBS=-ligb -lpci -lz -pthread +LDFLAGS=-L../../lib/igb + +all: mrpq mrpl + +mrpl: mrpl.o + +mrpq: mrpq.o + +mrpl.o: mrpl.c + gcc -c $(INCFLAGS) -I../../daemons/mrpd $(CFLAGS) mrpl.c + +mrpq.o: mrpq.c + gcc -c $(INCFLAGS) -I../../daemons/mrpd $(CFLAGS) mrpq.c + +%: %.o + $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ + +clean: + rm -f `find . -name "*~" -o -name "*.[oa]" -o -name "\#*\#" -o -name TAGS -o -name core -o -name "*.orig"` mrpl mrpq + diff --git a/examples/mrpl.c b/examples/mrp_client/mrpl.c similarity index 100% rename from examples/mrpl.c rename to examples/mrp_client/mrpl.c diff --git a/examples/mrpq.c b/examples/mrp_client/mrpq.c similarity index 100% rename from examples/mrpq.c rename to examples/mrp_client/mrpq.c diff --git a/examples/simple_talker.c b/examples/simple_talker.c deleted file mode 100755 index 118fd0f..0000000 --- a/examples/simple_talker.c +++ /dev/null @@ -1,970 +0,0 @@ -/****************************************************************************** - - Copyright (c) 2012, Intel Corporation - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the name of the Intel Corporation nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - -******************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "igb.h" -#include "mrpd.h" - -#define IGB_BIND_NAMESZ 24 - -/* global variables */ -int control_socket = -1; -device_t igb_dev; -volatile int halt_tx = 0; -volatile int listeners = 0; -volatile int mrp_okay; -volatile int mrp_error = 0;; -volatile int domain_a_valid = 0; -int domain_class_a_id; -int domain_class_a_priority; -int domain_class_a_vid; -volatile int domain_b_valid = 0; -int domain_class_b_id; -int domain_class_b_priority; -int domain_class_b_vid; - -#define VERSION_STR "1.0" - -static const char *version_str = -"simple_talker v" VERSION_STR "\n" -"Copyright (c) 2012, Intel Corporation\n"; - -#define MRPD_PORT_DEFAULT 7500 - -int mrp_join_listener(uint8_t *streamid); - -int -send_mrp_msg( char *notify_data, int notify_len) { - struct sockaddr_in addr; - socklen_t addr_len; - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(MRPD_PORT_DEFAULT); - inet_aton("127.0.0.1", &addr.sin_addr); - addr_len = sizeof(addr); - - if (control_socket != -1) - return(sendto(control_socket, notify_data, notify_len, 0, (struct sockaddr *)&addr, addr_len)); - else - return(0); -} - -int -mrp_connect() { - struct sockaddr_in addr; - int sock_fd = -1; - - sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sock_fd < 0) - goto out; - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(MRPD_PORT_DEFAULT); - inet_aton("127.0.0.1", &addr.sin_addr); - - memset(&addr, 0, sizeof(addr)); - - control_socket = sock_fd; - - return(0); -out: - if (sock_fd != -1) close(sock_fd); - sock_fd = -1; - return(-1); -} - -int -mrp_disconnect() { - char *msgbuf; - int rc; - - msgbuf = malloc(64); - if (NULL == msgbuf) - return -1; - - memset(msgbuf,0,64); - sprintf(msgbuf,"BYE"); - mrp_okay = 0; - rc = send_mrp_msg(msgbuf, 1500); - /* rc = recv_mrp_okay(); */ - free(msgbuf); - return rc; -} - - -int -recv_mrp_okay() { - while ((mrp_okay == 0) && (mrp_error == 0)) - usleep(20000); - return 0; -} - -int mrp_register_domain(int *class_id, int *priority, u_int16_t *vid) { - char *msgbuf; - int rc; - msgbuf = malloc(64); - if (NULL == msgbuf) - return -1; - - memset(msgbuf,0,64); - sprintf(msgbuf,"S+D:C:%d:P:%d:V:%04x", - *class_id, - *priority, - *vid); - - mrp_okay = 0; - rc = send_mrp_msg(msgbuf, 1500); - /* rc = recv_mrp_okay(); */ - free(msgbuf); - return rc; - -} - -int mrp_get_domain(int *class_a_id, int *a_priority, u_int16_t *a_vid, \ - int *class_b_id, int *b_priority, u_int16_t *b_vid ) { - char *msgbuf; - - /* we may not get a notification if we are joining late, - * so query for what is already there ... - */ - msgbuf = malloc(64); - if (NULL == msgbuf) - return -1; - - memset(msgbuf,0,64); - sprintf(msgbuf,"S??"); - send_mrp_msg(msgbuf, 64); - free(msgbuf); - - while (!halt_tx && (domain_a_valid == 0) && (domain_b_valid == 0)) - usleep(20000); - - *class_a_id = 0; - *a_priority = 0; - *a_vid = 0; - *class_b_id = 0; - *b_priority = 0; - *b_vid = 0; - - if (domain_a_valid) { - *class_a_id = domain_class_a_id; - *a_priority = domain_class_a_priority; - *a_vid = domain_class_a_vid; - } - if (domain_b_valid) { - *class_b_id = domain_class_b_id; - *b_priority = domain_class_b_priority; - *b_vid = domain_class_b_vid; - } - - return(0); -} - -unsigned char monitor_stream_id[] = {0,0,0,0,0,0,0,0}; - -int -mrp_await_listener(unsigned char *streamid) { - char *msgbuf; - - memcpy(monitor_stream_id, streamid, sizeof(monitor_stream_id)); - - msgbuf = malloc(64); - if (NULL == msgbuf) - return -1; - - memset(msgbuf,0,64); - sprintf(msgbuf,"S??"); - send_mrp_msg(msgbuf, 64); - free(msgbuf); - - /* either already there ... or need to wait ... */ - while (!halt_tx && (listeners == 0)) - usleep(20000); - - return(0); -} - -int -process_mrp_msg(char *buf, int buflen) { - - /* - * 1st character indicates application - * [MVS] - MAC, VLAN or STREAM - */ - unsigned int id; - unsigned int priority; - unsigned int vid; - int i,j,k; - unsigned int substate; - unsigned char recovered_streamid[8]; - k = 0; - -next_line: - if (k >= buflen) - return(0); - - switch (buf[k]) { - case 'E': - printf("%s from mrpd\n", buf); - fflush(stdout); - mrp_error = 1; - break; - case 'O': - mrp_okay = 1; - break; - case 'M': - case 'V': - printf("%s unhandled from mrpd\n", buf); - fflush(stdout); - /* unhandled for now */ - break; - case 'L': - /* parse a listener attribute - see if it matches our monitor_stream_id */ - i = k; - while (buf[i] != 'D') - i++; - i+=2; /* skip the ':' */ - sscanf(&(buf[i]),"%d",&substate); - - while (buf[i] != 'S') - i++; - i+=2; /* skip the ':' */ - - for (j = 0; j < 8; j++) { - sscanf(&(buf[i+2*j]),"%02x",&id); - recovered_streamid[j] = (unsigned char)id; - } - - printf("FOUND STREAM ID=%02x%02x%02x%02x%02x%02x%02x%02x ", - recovered_streamid[0], - recovered_streamid[1], - recovered_streamid[2], - recovered_streamid[3], - recovered_streamid[4], - recovered_streamid[5], - recovered_streamid[6], - recovered_streamid[7]); - - switch (substate) { - case 0: - printf("with state ignore\n"); - break; - case 1: - printf("with state askfailed\n"); - break; - case 2: - printf("with state ready\n"); - break; - case 3: - printf("with state readyfail\n"); - break; - default: - printf("with state UNKNOWN (%d)\n", substate); - break; - } - - if (substate > MSRP_LISTENER_ASKFAILED) { - if (memcmp(recovered_streamid, monitor_stream_id, sizeof(recovered_streamid)) == 0) { - mrp_join_listener(recovered_streamid); - listeners = 1; - printf("added listener\n"); - } - } - fflush(stdout); - - /* try to find a newline ... */ - while ((i < buflen) && (buf[i] != '\n') && (buf[i] != '\0')) - i++; - - if (i == buflen) - return(0); - if (buf[i] == '\0') - return(0); - - i++; - k = i; - goto next_line; - break; - case 'D': - i = k+4; - /* save the domain attribute */ - sscanf(&(buf[i]),"%d",&id); - while (buf[i] != 'P') - i++; - i+=2; /* skip the ':' */ - sscanf(&(buf[i]),"%d",&priority); - while (buf[i] != 'V') - i++; - i+=2; /* skip the ':' */ - sscanf(&(buf[i]),"%x",&vid); - if (id == 6) { - domain_class_a_id = id; - domain_class_a_priority = priority; - domain_class_a_vid = vid; - domain_a_valid = 1; - } else { - domain_class_b_id = id; - domain_class_b_priority = priority; - domain_class_b_vid = vid; - domain_b_valid = 1; - } - while ((i < buflen) && (buf[i] != '\n') && (buf[i] != '\0')) - i++; - - if ((i == buflen) || (buf[i] == '\0')) - return(0); - i++; - k = i; - goto next_line; - break; - case 'T': - /* as simple_talker we don't care about other talkers */ - i = k; - while ((i < buflen) && (buf[i] != '\n') && (buf[i] != '\0')) - i++; - if (i == buflen) - return(0); - if (buf[i] == '\0') - return(0); - - i++; - k = i; - goto next_line; - break; - case 'S': - /* handle the leave/join events */ - switch (buf[k+4]) { - case 'L': - i = k+5; - while (buf[i] != 'D') - i++; - i+=2; /* skip the ':' */ - sscanf(&(buf[i]),"%d",&substate); - - while (buf[i] != 'S') - i++; - i+=2; /* skip the ':' */ - - for (j = 0; j < 8; j++) { - sscanf(&(buf[i+2*j]),"%02x",&id); - recovered_streamid[j] = (unsigned char)id; - } - - printf("EVENT on STREAM ID=%02x%02x%02x%02x%02x%02x%02x%02x ", - recovered_streamid[0], - recovered_streamid[1], - recovered_streamid[2], - recovered_streamid[3], - recovered_streamid[4], - recovered_streamid[5], - recovered_streamid[6], - recovered_streamid[7]); - - switch (substate) { - case 0: - printf("with state ignore\n"); - break; - case 1: - printf("with state askfailed\n"); - break; - case 2: - printf("with state ready\n"); - break; - case 3: - printf("with state readyfail\n"); - break; - default: - printf("with state UNKNOWN (%d)\n", substate); - break; - } - - - switch (buf[k+1]) { - case 'L': - printf("got a leave indication\n"); - if (memcmp(recovered_streamid, monitor_stream_id, sizeof(recovered_streamid)) == 0) { - listeners = 0; - printf("listener left\n"); - } - break; - case 'J': - case 'N': - printf("got a new/join indication\n"); - if (substate > MSRP_LISTENER_ASKFAILED) { - if (memcmp(recovered_streamid, monitor_stream_id, sizeof(recovered_streamid)) == 0) - mrp_join_listener(recovered_streamid); - listeners = 1; - } - break; - } - /* only care about listeners ... */ - default: - return(0); - break; - } - break; - case '\0': - break; - } - return(0); -} - - -void * -mrp_monitor_thread(void *arg) { - char *msgbuf; - struct sockaddr_in client_addr; - struct msghdr msg; - struct iovec iov; - int bytes = 0; - struct pollfd fds; - int rc; - - if (NULL == arg) - rc = 0; - else - rc = 1; - - msgbuf = (char *)malloc (MAX_MRPD_CMDSZ); - if (NULL == msgbuf) - return NULL; - while (!halt_tx) { - fds.fd = control_socket; - fds.events = POLLIN; - fds.revents = 0; - rc = poll(&fds, 1, 100); - if (rc < 0) { - free (msgbuf); - pthread_exit(NULL); - } - if (rc == 0) - continue; - if ((fds.revents & POLLIN) == 0) { - free (msgbuf); - pthread_exit(NULL); - } - - memset(&msg, 0, sizeof(msg)); - memset(&client_addr, 0, sizeof(client_addr)); - memset(msgbuf, 0, MAX_MRPD_CMDSZ); - - iov.iov_len = MAX_MRPD_CMDSZ; - iov.iov_base = msgbuf; - msg.msg_name = &client_addr; - msg.msg_namelen = sizeof(client_addr); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - bytes = recvmsg(control_socket, &msg, 0); - if (bytes < 0) - continue; - - process_mrp_msg(msgbuf, bytes); - } - free (msgbuf); - - pthread_exit(NULL); -} - -pthread_t monitor_thread; -pthread_attr_t monitor_attr; - -int -mrp_monitor() { - pthread_attr_init(&monitor_attr); - pthread_create(&monitor_thread, NULL, mrp_monitor_thread, NULL); - - return(0); -} - -int -mrp_join_listener(uint8_t *streamid) { - char *msgbuf; - int rc; - - msgbuf = malloc(1500); - if (NULL == msgbuf) - return -1; - - memset(msgbuf,0,1500); - sprintf(msgbuf,"S+L:%02X%02X%02X%02X%02X%02X%02X%02X" - ":D:2", - streamid[0], - streamid[1], - streamid[2], - streamid[3], - streamid[4], - streamid[5], - streamid[6], - streamid[7]); - - mrp_okay = 0; - rc = send_mrp_msg(msgbuf, 1500); - /* rc = recv_mrp_okay(); */ - free(msgbuf); - return rc; -} - -int -mrp_advertise_stream( - uint8_t *streamid, - uint8_t *destaddr, - u_int16_t vlan, - int pktsz, - int interval, - int priority, - int latency) { - char *msgbuf; - int rc; - - msgbuf = malloc(1500); - if (NULL == msgbuf) - return -1; - - memset(msgbuf,0,1500); - sprintf(msgbuf,"S++S:%02X%02X%02X%02X%02X%02X%02X%02X" - ":A:%02X%02X%02X%02X%02X%02X" - ":V:%04X" - ":Z:%d" - ":I:%d" - ":P:%d" - ":L:%d", - streamid[0], - streamid[1], - streamid[2], - streamid[3], - streamid[4], - streamid[5], - streamid[6], - streamid[7], - destaddr[0], - destaddr[1], - destaddr[2], - destaddr[3], - destaddr[4], - destaddr[5], - vlan, - pktsz, - interval, - priority << 5, - latency); - - mrp_okay = 0; - rc = send_mrp_msg(msgbuf, 1500); - /* rc = recv_mrp_okay(); */ - free(msgbuf); - return rc; -} - -int -mrp_unadvertise_stream( - uint8_t *streamid, - uint8_t *destaddr, - u_int16_t vlan, - int pktsz, - int interval, - int priority, - int latency) { - char *msgbuf; - int rc; - - msgbuf = malloc(1500); - if (NULL == msgbuf) - return -1; - - memset(msgbuf,0,1500); - sprintf(msgbuf,"S--S:%02X%02X%02X%02X%02X%02X%02X%02X" - ":A:%02X%02X%02X%02X%02X%02X" - ":V:%04X" - ":Z:%d" - ":I:%d" - ":P:%d" - ":L:%d", - streamid[0], - streamid[1], - streamid[2], - streamid[3], - streamid[4], - streamid[5], - streamid[6], - streamid[7], - destaddr[0], - destaddr[1], - destaddr[2], - destaddr[3], - destaddr[4], - destaddr[5], - vlan, - pktsz, - interval, - priority << 5, - latency); - - mrp_okay = 0; - rc = send_mrp_msg(msgbuf, 1500); - /* rc = recv_mrp_okay(); */ - free(msgbuf); - return rc; -} - -void -sigint_handler (int signum) -{ - printf("got SIGINT\n"); - halt_tx = signum; -} - -int -pci_connect() -{ - struct pci_access *pacc; - struct pci_dev *dev; - int err; - char devpath[IGB_BIND_NAMESZ]; - - memset(&igb_dev, 0, sizeof(device_t)); - - pacc = pci_alloc(); - pci_init(pacc); - pci_scan_bus(pacc); - for (dev=pacc->devices; dev; dev=dev->next) - { - pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); - - igb_dev.pci_vendor_id = dev->vendor_id; - igb_dev.pci_device_id = dev->device_id; - igb_dev.domain = dev->domain; - igb_dev.bus = dev->bus; - igb_dev.dev = dev->dev; - igb_dev.func = dev->func; - - snprintf(devpath, IGB_BIND_NAMESZ, "%04x:%02x:%02x.%d", \ - dev->domain, dev->bus, dev->dev, dev->func ); - - err = igb_probe(&igb_dev); - - if (err) { - continue; - } - - printf ("attaching to %s\n", devpath); - err = igb_attach(devpath, &igb_dev); - - if (err) { - printf ("attach failed! (%s)\n", strerror(errno)); - continue; - } - goto out; - } - - pci_cleanup(pacc); - return ENXIO; - -out: - pci_cleanup(pacc); - return 0; - -} - -unsigned char STATION_ADDR[] = {0,0,0,0,0,0}; -unsigned char STREAM_ID[] = {0,0,0,0,0,0,0,0}; -unsigned char DEST_ADDR[] = {0x91, 0xE0, 0xF0, 0x00, 0x00, 0x00}; /* IEEE 1722 reserved address */ - -int -get_mac_address(char *interface) { - struct ifreq if_request; - int lsock; - int rc; - - lsock = socket(PF_PACKET, SOCK_RAW, htons(0x800)); - if (lsock < 0) - return -1; - - memset(&if_request, 0, sizeof(if_request)); - - strncpy(if_request.ifr_name, interface, sizeof(if_request.ifr_name)); - - rc = ioctl(lsock, SIOCGIFHWADDR, &if_request); - if (rc < 0) { - close(lsock); - return -1; - } - - memcpy(STATION_ADDR, if_request.ifr_hwaddr.sa_data, sizeof(STATION_ADDR)); - close(lsock); - return 0; -} - -static void -usage( void ) { - fprintf(stderr, - "\n" - "usage: simple_talker [-h] -i interface-name" - "\n" - "options:\n" - " -h show this message\n" - " -i specify interface for AVB connection\n" - "\n" - "%s" - "\n", version_str); - exit(1); -} - -#define PACKET_IPG (125000) /* (1) packet every 125 msec */ - -int -main(int argc, char *argv[]) { - unsigned i; - int err; - struct igb_dma_alloc a_page; - struct igb_packet a_packet; - struct igb_packet *tmp_packet; - struct igb_packet *cleaned_packets; - struct igb_packet *free_packets; - int c; - u_int64_t last_time; - u_int64_t rdtsc0; - int rc = 0; - char *interface = NULL; - int class_a_id = 0; - int a_priority = 0; - u_int16_t a_vid = 0; - int class_b_id = 0; - int b_priority = 0; - u_int16_t b_vid = 0; - - for (;;) { - c = getopt(argc, argv, "hi:"); - - if (c < 0) - break; - - switch (c) { - case 'h': - usage(); - break; - case 'i': - if (interface) { - printf("only one interface per daemon is supported\n"); - usage(); - } - interface = strdup(optarg); - break; - } - } - if (optind < argc) - usage(); - - if (NULL == interface) { - usage(); - } - - rc = mrp_connect(); - if (rc) { - printf("socket creation failed\n"); - return(errno); - } - - err = pci_connect(); - - if (err) { - printf("connect failed (%s) - are you running as root?\n", strerror(errno)); - return(errno); - } - - err = igb_init(&igb_dev); - - if (err) { - printf("init failed (%s) - is the driver really loaded?\n", strerror(errno)); - return(errno); - } - - err = igb_dma_malloc_page(&igb_dev, &a_page); - - if (err) { - printf("malloc failed (%s) - out of memory?\n", strerror(errno)); - return(errno); - } - - signal(SIGINT, sigint_handler); - - rc = get_mac_address(interface); - if (rc) { - printf("failed to open interface\n"); - usage(); - } - - mrp_monitor(); - mrp_get_domain(&class_a_id, &a_priority, &a_vid, &class_b_id, &b_priority, &b_vid); - - printf("detected domain Class A PRIO=%d VID=%04x...\n", a_priority, a_vid); - - mrp_register_domain(&class_a_id, &a_priority, &a_vid); - igb_set_class_bandwidth(&igb_dev, 0, 0, 0); /* xxx Qav */ - - memset(STREAM_ID, 0, sizeof(STREAM_ID)); - - memcpy(STREAM_ID, STATION_ADDR, sizeof(STATION_ADDR)); - -#define PKT_SZ 200 - a_packet.dmatime = a_packet.attime = a_packet.flags = 0; - a_packet.map.paddr = a_page.dma_paddr; - a_packet.map.mmap_size = a_page.mmap_size; - - a_packet.offset = 0; - a_packet.vaddr = a_page.dma_vaddr + a_packet.offset; - a_packet.len = PKT_SZ; - - free_packets = NULL; - - /* divide the dma page into buffers for packets */ - for (i = 1; i < ((a_page.mmap_size) / PKT_SZ); i++) { - tmp_packet = malloc(sizeof(struct igb_packet)); - if (NULL == tmp_packet) { printf("failed to allocate igb_packet memory!\n"); return(errno); } - - *tmp_packet = a_packet; - tmp_packet->offset = (i * PKT_SZ); - tmp_packet->vaddr += tmp_packet->offset; - tmp_packet->next = free_packets; - memset(tmp_packet->vaddr, 0, PKT_SZ); /* MAC header at least */ - memcpy(tmp_packet->vaddr, DEST_ADDR, sizeof(DEST_ADDR)); - memcpy(tmp_packet->vaddr+6, STATION_ADDR, sizeof(STATION_ADDR)); - /* Q-tag */ - ((char *)tmp_packet->vaddr)[12] = 0x81; - ((char *)tmp_packet->vaddr)[13] = 0x00; - ((char *)tmp_packet->vaddr)[14] = ((a_priority << 13 | a_vid) ) >> 8; - ((char *)tmp_packet->vaddr)[15] = ((a_priority << 13 | a_vid) ) & 0xFF; - ((char *)tmp_packet->vaddr)[16] = 0x88; /* experimental etype */ - ((char *)tmp_packet->vaddr)[17] = 0xB5; - free_packets = tmp_packet; - } - - - /* - * subtract 16 bytes for the MAC header/Q-tag - pktsz is limited to the - * data payload of the ethernet frame . - * - * IPG is scaled to the Class (A) observation interval of packets per 125 usec - */ - printf("advertising stream ...\n"); - mrp_advertise_stream( STREAM_ID, DEST_ADDR, a_vid, PKT_SZ - 16, PACKET_IPG / 125000, a_priority, 3900); - - printf("awaiting a listener ...\n"); - - mrp_await_listener(STREAM_ID); - - printf("got a listener ...\n"); - - halt_tx = 0; - - rc = nice(-20); - - igb_get_wallclock(&igb_dev, &last_time, &rdtsc0); - - while (listeners && !halt_tx) { - tmp_packet = free_packets; - - if (NULL == tmp_packet) - goto cleanup; - - free_packets = tmp_packet->next; - - /* unfortuntely unless this thread is at rtprio - * you get pre-empted between fetching the time - * and programming the packet and get a late packet - */ - tmp_packet->attime = last_time + PACKET_IPG; - *(u_int64_t *)(tmp_packet->vaddr + 32) = tmp_packet->attime; - err = igb_xmit(&igb_dev, 0, tmp_packet); - - if (!err) { - continue; - } - - if (ENOSPC == err) { - /* put back for now */ - tmp_packet->next = free_packets; - free_packets = tmp_packet; - } -cleanup: - igb_clean(&igb_dev, &cleaned_packets); - i = 0; - while (cleaned_packets) { - i++; - tmp_packet = cleaned_packets; - cleaned_packets = cleaned_packets->next; - tmp_packet->next = free_packets; - free_packets = tmp_packet; - } - } - - rc = nice(0); - - if (halt_tx == 0) - printf("listener left ...\n"); - - halt_tx = 1; - mrp_unadvertise_stream( STREAM_ID, DEST_ADDR, a_vid, PKT_SZ - 16, PACKET_IPG / 125000, a_priority, 3900); - - igb_set_class_bandwidth(&igb_dev, 0, 0, 0); /* disable Qav */ - - rc = mrp_disconnect(); - igb_dma_free_page(&igb_dev, &a_page); - err = igb_detach(&igb_dev); - pthread_exit(NULL); - return(0); -} - diff --git a/examples/simple_talker/Makefile b/examples/simple_talker/Makefile new file mode 100644 index 0000000..6e3984c --- /dev/null +++ b/examples/simple_talker/Makefile @@ -0,0 +1,21 @@ +OPT=-O2 +CFLAGS=$(OPT) -Wall -W -Wno-parentheses + +CC=gcc +INCFLAGS=-I../../lib/igb +LDLIBS=-ligb -lpci -lz -lrt -lm -pthread +LDFLAGS=-L../../lib/igb + +all: simple_talker + +simple_talker: simple_talker.o + +simple_talker.o: simple_talker.c + gcc -c $(INCFLAGS) -I../../daemons/mrpd $(CFLAGS) simple_talker.c + +%: %.o + $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ + +clean: + rm -f `find . -name "*~" -o -name "*.[oa]" -o -name "\#*\#" -o -name TAGS -o -name core -o -name "*.orig"` simple_talker + diff --git a/examples/simple_talker/README b/examples/simple_talker/README new file mode 100644 index 0000000..79e0db1 --- /dev/null +++ b/examples/simple_talker/README @@ -0,0 +1,22 @@ +EXAMPLE APPLICATIONS + +The 'simple_talker' application illustrates the various steps to publish a stream +and streaming 1722/61883 audio frames after a listener connects. The audio +itself is a simple sine wave. It has been tested with other vendors listeners +at AVNu plug-fests. + +The simple talker application requires root permissions to execute and +attach to the driver. + sudo ./simple_talker + +To exit the app, hit Ctrl-C. The application gracefully tears down +the connection to the driver. If the application unexpectedly aborts the +kernel-mode driver also reclaims the various buffers and attempts to clean up. +The application should be able to re-initialize and use the transmit queues +without restarting the driver. + +Note this application requires using the provided gptp timesync daemon to +provide the 802.1AS presentation times included in the 1722 frames. This +application also requires the mrpd daemon to be running to detect and +establish various stream reservation parameters. + diff --git a/examples/simple_talker/simple_talker.c b/examples/simple_talker/simple_talker.c new file mode 100755 index 0000000..f7178c4 --- /dev/null +++ b/examples/simple_talker/simple_talker.c @@ -0,0 +1,1155 @@ +/****************************************************************************** + + Copyright (c) 2012, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "igb.h" +#include "mrpd.h" +#include +#include +#include + +typedef struct { + int64_t ml_phoffset; + int64_t ls_phoffset; + int32_t ml_freqoffset; + int32_t ls_freqoffset; + int64_t local_time; +} gPtpTimeData; + + +#define SHM_SIZE 4*8 + sizeof(pthread_mutex_t) /* 3 - 64 bit and 2 - 32 bits */ +#define SHM_NAME "/ptp" + +#define MAX_SAMPLE_VALUE ((1U << ((sizeof(int32_t)*8)-1))-1) + +#define SRC_CHANNELS (2) +#define SAMPLES_PER_SECOND (48000) +#define FREQUENCY (480) +#define SAMPLES_PER_CYCLE (SAMPLES_PER_SECOND/FREQUENCY) +#define GAIN (.5) + +#define SYSTEM_FREQ_FILENAME "/sys/module/igb_avb/parameters/TEN_USEC_COUNT" + +//1722 header +#define ETH_TYPE 0x22F0 + +#define CD_SUBTYPE 0x02 /* for simple audio format */ +#define SV_VER_MR_RS_GV_TV 0x81 +#define RS_TU 0x00 +#define SAMPLE_FORMAT_NON_INTR_FLOAT 0x02 +#define NOMINAL_SAMPLE_RATE 0x09 +#define LINEAR_SAMPLE_MSB 0x20 +unsigned char GATEWAY_INFO[] = + { SAMPLE_FORMAT_NON_INTR_FLOAT, 0, NOMINAL_SAMPLE_RATE, LINEAR_SAMPLE_MSB }; + +#define SAMPLE_SIZE 4 /* 4 bytes */ +#define SAMPLES_PER_FRAME 6 +#define CHANNELS 2 +#define PAYLOAD_SIZE SAMPLE_SIZE*SAMPLES_PER_FRAME*CHANNELS /* 6*4 * 2 channels = 48 bytes */ + +#define IGB_BIND_NAMESZ 24 + +#define XMIT_DELAY (200000000) /* us */ +#define RENDER_DELAY (XMIT_DELAY+2000000) /* us */ +typedef enum { false = 0, true = 1 } bool; +typedef struct __attribute__ ((packed)) { + uint64_t subtype:7; + uint64_t cd_indicator:1; + uint64_t timestamp_valid:1; + uint64_t gateway_valid:1; + uint64_t reserved0:1; + uint64_t reset:1; + uint64_t version:3; + uint64_t sid_valid:1; + uint64_t seq_number:8; + uint64_t timestamp_uncertain:1; + uint64_t reserved1:7; + uint64_t stream_id; + uint64_t timestamp:32; + uint64_t gateway_info:32; + uint64_t length:16; +} seventeen22_header; + +/* 61883 CIP with SYT Field */ +typedef struct { + uint16_t packet_channel:6; + uint16_t format_tag:2; + uint16_t app_control:4; + uint16_t packet_tcode:4; + uint16_t source_id:6; + uint16_t reserved0:2; + uint16_t data_block_size:8; + uint16_t reserved1:2; + uint16_t source_packet_header:1; + uint16_t quadlet_padding_count:3; + uint16_t fraction_number:2; + uint16_t data_block_continuity:8; + uint16_t format_id:6; + uint16_t eoh:2; + uint16_t format_dependent_field:8; + uint16_t syt; +} six1883_header; + +typedef struct { + uint8_t label; + uint8_t value[3]; +} six1883_sample; + +/* global variables */ +int control_socket = -1; +device_t igb_dev; +volatile int halt_tx = 0; +volatile int listeners = 0; +volatile int mrp_okay; +volatile int mrp_error = 0;; +volatile int domain_a_valid = 0; +int domain_class_a_id; +int domain_class_a_priority; +int domain_class_a_vid; +volatile int domain_b_valid = 0; +int domain_class_b_id; +int domain_class_b_priority; +int domain_class_b_vid; + +#define VERSION_STR "1.0" +static const char *version_str = "simple_talker v" VERSION_STR "\n" + "Copyright (c) 2012, Intel Corporation\n"; + +#define MRPD_PORT_DEFAULT 7500 +static inline uint64_t ST_rdtsc(void) +{ + uint64_t ret; + unsigned c, d; + asm volatile ("rdtsc":"=a" (c), "=d"(d)); + ret = d; + ret <<= 32; + ret |= c; + return ret; +} + +static int shm_fd = -1; +static char *memory_offset_buffer = NULL; +int gptpinit(void) +{ + shm_fd = shm_open(SHM_NAME, O_RDWR, 0); + if (shm_fd == -1) { + perror("shm_open()"); + return false; + } + memory_offset_buffer = + (char *)mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, + shm_fd, 0); + if (memory_offset_buffer == (char *)-1) { + perror("mmap()"); + memory_offset_buffer = NULL; + shm_unlink(SHM_NAME); + return false; + } + return true; +} + +void gptpdeinit(void) +{ + if (memory_offset_buffer != NULL) { + munmap(memory_offset_buffer, SHM_SIZE); + } + if (shm_fd != -1) { + close(shm_fd); + } +} + +int gptpscaling(gPtpTimeData * td) +{ + pthread_mutex_lock((pthread_mutex_t *) memory_offset_buffer); + memcpy(td, memory_offset_buffer + sizeof(pthread_mutex_t), sizeof(*td)); + pthread_mutex_unlock((pthread_mutex_t *) memory_offset_buffer); + + fprintf(stderr, "ml_phoffset = %lld, ls_phoffset = %lld\n", + td->ml_phoffset, td->ls_phoffset); + fprintf(stderr, "ml_freqffset = %d, ls_freqoffset = %d\n", + td->ml_freqoffset, td->ls_freqoffset); + + return true; +} + +void gensine32(int32_t * buf, unsigned count) +{ + long double interval = (2 * ((long double)M_PI)) / count; + unsigned i; + for (i = 0; i < count; ++i) { + buf[i] = + (int32_t) (MAX_SAMPLE_VALUE * sinl(i * interval) * GAIN); + } +} + +int get_samples(unsigned count, int32_t * buffer) +{ + static int init = 0; + static int32_t samples_onechannel[100]; + static unsigned index = 0; + + if (init == 0) { + gensine32(samples_onechannel, 100); + init = 1; + } + + while (count > 0) { + int i; + for (i = 0; i < SRC_CHANNELS; ++i) { + *(buffer++) = samples_onechannel[index]; + } + index = (index + 1) % 100; + --count; + } + + return 0; +} + +int mrp_join_listener(uint8_t * streamid); +int send_mrp_msg(char *notify_data, int notify_len) +{ + struct sockaddr_in addr; + socklen_t addr_len; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(MRPD_PORT_DEFAULT); + inet_aton("127.0.0.1", &addr.sin_addr); + addr_len = sizeof(addr); + if (control_socket != -1) + return (sendto + (control_socket, notify_data, notify_len, 0, + (struct sockaddr *)&addr, addr_len)); + + else + return (0); +} + +int mrp_connect() +{ + struct sockaddr_in addr; + int sock_fd = -1; + sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock_fd < 0) + goto out; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(MRPD_PORT_DEFAULT); + inet_aton("127.0.0.1", &addr.sin_addr); + memset(&addr, 0, sizeof(addr)); + control_socket = sock_fd; + return (0); + out: if (sock_fd != -1) + close(sock_fd); + sock_fd = -1; + return (-1); +} + +int mrp_disconnect() +{ + char *msgbuf; + int rc; + msgbuf = malloc(64); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 64); + sprintf(msgbuf, "BYE"); + mrp_okay = 0; + rc = send_mrp_msg(msgbuf, 1500); + + /* rc = recv_mrp_okay(); */ + free(msgbuf); + return rc; +} + +int recv_mrp_okay() +{ + while ((mrp_okay == 0) && (mrp_error == 0)) + usleep(20000); + return 0; +} + +int mrp_register_domain(int *class_id, int *priority, u_int16_t * vid) +{ + char *msgbuf; + int rc; + msgbuf = malloc(64); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 64); + sprintf(msgbuf, "S+D:C:%d:P:%d:V:%04x", *class_id, *priority, *vid); + mrp_okay = 0; + rc = send_mrp_msg(msgbuf, 1500); + + /* rc = recv_mrp_okay(); */ + free(msgbuf); + return rc; +} + +int mrp_get_domain(int *class_a_id, int *a_priority, u_int16_t * a_vid, + int *class_b_id, int *b_priority, u_int16_t * b_vid) +{ + char *msgbuf; + + /* we may not get a notification if we are joining late, + * so query for what is already there ... + */ + msgbuf = malloc(64); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 64); + sprintf(msgbuf, "S??"); + send_mrp_msg(msgbuf, 64); + free(msgbuf); + while (!halt_tx && (domain_a_valid == 0) && (domain_b_valid == 0)) + usleep(20000); + *class_a_id = 0; + *a_priority = 0; + *a_vid = 0; + *class_b_id = 0; + *b_priority = 0; + *b_vid = 0; + if (domain_a_valid) { + *class_a_id = domain_class_a_id; + *a_priority = domain_class_a_priority; + *a_vid = domain_class_a_vid; + } + if (domain_b_valid) { + *class_b_id = domain_class_b_id; + *b_priority = domain_class_b_priority; + *b_vid = domain_class_b_vid; + } + return (0); +} +unsigned char monitor_stream_id[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +int mrp_await_listener(unsigned char *streamid) +{ + char *msgbuf; + memcpy(monitor_stream_id, streamid, sizeof(monitor_stream_id)); + msgbuf = malloc(64); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 64); + sprintf(msgbuf, "S??"); + send_mrp_msg(msgbuf, 64); + free(msgbuf); + + /* either already there ... or need to wait ... */ + while (!halt_tx && (listeners == 0)) + usleep(20000); + return (0); +} + +int process_mrp_msg(char *buf, int buflen) +{ + + /* + * 1st character indicates application + * [MVS] - MAC, VLAN or STREAM + */ + unsigned int id; + unsigned int priority; + unsigned int vid; + int i, j, k; + unsigned int substate; + unsigned char recovered_streamid[8]; + k = 0; + next_line:if (k >= buflen) + return (0); + switch (buf[k]) { + case 'E': + printf("%s from mrpd\n", buf); + fflush(stdout); + mrp_error = 1; + break; + case 'O': + mrp_okay = 1; + break; + case 'M': + case 'V': + printf("%s unhandled from mrpd\n", buf); + fflush(stdout); + + /* unhandled for now */ + break; + case 'L': + + /* parse a listener attribute - see if it matches our monitor_stream_id */ + i = k; + while (buf[i] != 'D') + i++; + i += 2; /* skip the ':' */ + sscanf(&(buf[i]), "%d", &substate); + while (buf[i] != 'S') + i++; + i += 2; /* skip the ':' */ + for (j = 0; j < 8; j++) { + sscanf(&(buf[i + 2 * j]), "%02x", &id); + recovered_streamid[j] = (unsigned char)id; + } printf + ("FOUND STREAM ID=%02x%02x%02x%02x%02x%02x%02x%02x ", + recovered_streamid[0], recovered_streamid[1], + recovered_streamid[2], recovered_streamid[3], + recovered_streamid[4], recovered_streamid[5], + recovered_streamid[6], recovered_streamid[7]); + switch (substate) { + case 0: + printf("with state ignore\n"); + break; + case 1: + printf("with state askfailed\n"); + break; + case 2: + printf("with state ready\n"); + break; + case 3: + printf("with state readyfail\n"); + break; + default: + printf("with state UNKNOWN (%d)\n", substate); + break; + } + if (substate > MSRP_LISTENER_ASKFAILED) { + if (memcmp + (recovered_streamid, monitor_stream_id, + sizeof(recovered_streamid)) == 0) { + mrp_join_listener(recovered_streamid); + listeners = 1; + printf("added listener\n"); + } + } + fflush(stdout); + + /* try to find a newline ... */ + while ((i < buflen) && (buf[i] != '\n') && (buf[i] != '\0')) + i++; + if (i == buflen) + return (0); + if (buf[i] == '\0') + return (0); + i++; + k = i; + goto next_line; + break; + case 'D': + i = k + 4; + + /* save the domain attribute */ + sscanf(&(buf[i]), "%d", &id); + while (buf[i] != 'P') + i++; + i += 2; /* skip the ':' */ + sscanf(&(buf[i]), "%d", &priority); + while (buf[i] != 'V') + i++; + i += 2; /* skip the ':' */ + sscanf(&(buf[i]), "%x", &vid); + if (id == 6) { + domain_class_a_id = id; + domain_class_a_priority = priority; + domain_class_a_vid = vid; + domain_a_valid = 1; + } else { + domain_class_b_id = id; + domain_class_b_priority = priority; + domain_class_b_vid = vid; + domain_b_valid = 1; + } + while ((i < buflen) && (buf[i] != '\n') && (buf[i] != '\0')) + i++; + if ((i == buflen) || (buf[i] == '\0')) + return (0); + i++; + k = i; + goto next_line; + break; + case 'T': + + /* as simple_talker we don't care about other talkers */ + i = k; + while ((i < buflen) && (buf[i] != '\n') && (buf[i] != '\0')) + i++; + if (i == buflen) + return (0); + if (buf[i] == '\0') + return (0); + i++; + k = i; + goto next_line; + break; + case 'S': + + /* handle the leave/join events */ + switch (buf[k + 4]) { + case 'L': + i = k + 5; + while (buf[i] != 'D') + i++; + i += 2; /* skip the ':' */ + sscanf(&(buf[i]), "%d", &substate); + while (buf[i] != 'S') + i++; + i += 2; /* skip the ':' */ + for (j = 0; j < 8; j++) { + sscanf(&(buf[i + 2 * j]), "%02x", &id); + recovered_streamid[j] = (unsigned char)id; + } printf + ("EVENT on STREAM ID=%02x%02x%02x%02x%02x%02x%02x%02x ", + recovered_streamid[0], recovered_streamid[1], + recovered_streamid[2], recovered_streamid[3], + recovered_streamid[4], recovered_streamid[5], + recovered_streamid[6], recovered_streamid[7]); + switch (substate) { + case 0: + printf("with state ignore\n"); + break; + case 1: + printf("with state askfailed\n"); + break; + case 2: + printf("with state ready\n"); + break; + case 3: + printf("with state readyfail\n"); + break; + default: + printf("with state UNKNOWN (%d)\n", substate); + break; + } + switch (buf[k + 1]) { + case 'L': + printf("got a leave indication\n"); + if (memcmp + (recovered_streamid, monitor_stream_id, + sizeof(recovered_streamid)) == 0) { + listeners = 0; + printf("listener left\n"); + } + break; + case 'J': + case 'N': + printf("got a new/join indication\n"); + if (substate > MSRP_LISTENER_ASKFAILED) { + if (memcmp + (recovered_streamid, + monitor_stream_id, + sizeof(recovered_streamid)) == 0) + mrp_join_listener + (recovered_streamid); + listeners = 1; + } + break; + } + + /* only care about listeners ... */ + default: + return (0); + break; + } + break; + case '\0': + break; + } + return (0); +} + +void *mrp_monitor_thread(void *arg) +{ + char *msgbuf; + struct sockaddr_in client_addr; + struct msghdr msg; + struct iovec iov; + int bytes = 0; + struct pollfd fds; + int rc; + if (NULL == arg) + rc = 0; + + else + rc = 1; + msgbuf = (char *)malloc(MAX_MRPD_CMDSZ); + if (NULL == msgbuf) + return NULL; + while (!halt_tx) { + fds.fd = control_socket; + fds.events = POLLIN; + fds.revents = 0; + rc = poll(&fds, 1, 100); + if (rc < 0) { + free(msgbuf); + pthread_exit(NULL); + } + if (rc == 0) + continue; + if ((fds.revents & POLLIN) == 0) { + free(msgbuf); + pthread_exit(NULL); + } + memset(&msg, 0, sizeof(msg)); + memset(&client_addr, 0, sizeof(client_addr)); + memset(msgbuf, 0, MAX_MRPD_CMDSZ); + iov.iov_len = MAX_MRPD_CMDSZ; + iov.iov_base = msgbuf; + msg.msg_name = &client_addr; + msg.msg_namelen = sizeof(client_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + bytes = recvmsg(control_socket, &msg, 0); + if (bytes < 0) + continue; + process_mrp_msg(msgbuf, bytes); + } + free(msgbuf); + pthread_exit(NULL); +} + +pthread_t monitor_thread; +pthread_attr_t monitor_attr; +int mrp_monitor() +{ + pthread_attr_init(&monitor_attr); + pthread_create(&monitor_thread, NULL, mrp_monitor_thread, NULL); + return (0); +} + +int mrp_join_listener(uint8_t * streamid) +{ + char *msgbuf; + int rc; + msgbuf = malloc(1500); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 1500); + sprintf(msgbuf, "S+L:%02X%02X%02X%02X%02X%02X%02X%02X" + ":D:2", streamid[0], streamid[1], streamid[2], streamid[3], + streamid[4], streamid[5], streamid[6], streamid[7]); + mrp_okay = 0; + rc = send_mrp_msg(msgbuf, 1500); + + /* rc = recv_mrp_okay(); */ + free(msgbuf); + return rc; +} + +int +mrp_advertise_stream(uint8_t * streamid, + uint8_t * destaddr, + u_int16_t vlan, + int pktsz, int interval, int priority, int latency) +{ + char *msgbuf; + int rc; + msgbuf = malloc(1500); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 1500); + sprintf(msgbuf, "S++S:%02X%02X%02X%02X%02X%02X%02X%02X" + ":A:%02X%02X%02X%02X%02X%02X" + ":V:%04X" + ":Z:%d" + ":I:%d" + ":P:%d" + ":L:%d", streamid[0], streamid[1], streamid[2], + streamid[3], streamid[4], streamid[5], streamid[6], + streamid[7], destaddr[0], destaddr[1], destaddr[2], + destaddr[3], destaddr[4], destaddr[5], vlan, pktsz, + interval, priority << 5, latency); + mrp_okay = 0; + rc = send_mrp_msg(msgbuf, 1500); + + /* rc = recv_mrp_okay(); */ + free(msgbuf); + return rc; +} + +int +mrp_unadvertise_stream(uint8_t * streamid, + uint8_t * destaddr, + u_int16_t vlan, + int pktsz, int interval, int priority, int latency) +{ + char *msgbuf; + int rc; + msgbuf = malloc(1500); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 1500); + sprintf(msgbuf, "S--S:%02X%02X%02X%02X%02X%02X%02X%02X" + ":A:%02X%02X%02X%02X%02X%02X" + ":V:%04X" + ":Z:%d" + ":I:%d" + ":P:%d" + ":L:%d", streamid[0], streamid[1], streamid[2], + streamid[3], streamid[4], streamid[5], streamid[6], + streamid[7], destaddr[0], destaddr[1], destaddr[2], + destaddr[3], destaddr[4], destaddr[5], vlan, pktsz, + interval, priority << 5, latency); + mrp_okay = 0; + rc = send_mrp_msg(msgbuf, 1500); + + /* rc = recv_mrp_okay(); */ + free(msgbuf); + return rc; +} + +void sigint_handler(int signum) +{ + printf("got SIGINT\n"); + halt_tx = signum; +} + +int pci_connect() +{ + struct pci_access *pacc; + struct pci_dev *dev; + int err; + char devpath[IGB_BIND_NAMESZ]; + memset(&igb_dev, 0, sizeof(device_t)); + pacc = pci_alloc(); + pci_init(pacc); + pci_scan_bus(pacc); + for (dev = pacc->devices; dev; dev = dev->next) { + pci_fill_info(dev, + PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); + igb_dev.pci_vendor_id = dev->vendor_id; + igb_dev.pci_device_id = dev->device_id; + igb_dev.domain = dev->domain; + igb_dev.bus = dev->bus; + igb_dev.dev = dev->dev; + igb_dev.func = dev->func; + snprintf(devpath, IGB_BIND_NAMESZ, "%04x:%02x:%02x.%d", + dev->domain, dev->bus, dev->dev, dev->func); + err = igb_probe(&igb_dev); + if (err) { + continue; + } + printf("attaching to %s\n", devpath); + err = igb_attach(devpath, &igb_dev); + if (err) { + printf("attach failed! (%s)\n", strerror(errno)); + continue; + } + goto out; + } + pci_cleanup(pacc); + return ENXIO; + out: pci_cleanup(pacc); + return 0; +} + +unsigned char STATION_ADDR[] = { 0, 0, 0, 0, 0, 0 }; +unsigned char STREAM_ID[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* IEEE 1722 reserved address */ +unsigned char DEST_ADDR[] = { 0x91, 0xE0, 0xF0, 0x00, 0x0e, 0x80 }; + +int get_mac_address(char *interface) +{ + struct ifreq if_request; + int lsock; + int rc; + lsock = socket(PF_PACKET, SOCK_RAW, htons(0x800)); + if (lsock < 0) + return -1; + memset(&if_request, 0, sizeof(if_request)); + strncpy(if_request.ifr_name, interface, sizeof(if_request.ifr_name)); + rc = ioctl(lsock, SIOCGIFHWADDR, &if_request); + if (rc < 0) { + close(lsock); + return -1; + } + memcpy(STATION_ADDR, if_request.ifr_hwaddr.sa_data, + sizeof(STATION_ADDR)); + close(lsock); + return 0; +} + +static void usage(void) +{ + fprintf(stderr, "\n" + "usage: simple_talker [-h] -i interface-name" + "\n" + "options:\n" + " -h show this message\n" + " -i specify interface for AVB connection\n" + "\n" "%s" "\n", version_str); + exit(1); +} + +#define PACKET_IPG (125000) /* (1) packet every 125 usec */ + +int main(int argc, char *argv[]) +{ + unsigned i; + int err; + struct igb_dma_alloc a_page; + struct igb_packet a_packet; + struct igb_packet *tmp_packet; + struct igb_packet *cleaned_packets; + struct igb_packet *free_packets; + int c; + u_int64_t last_time; + int rc = 0; + char *interface = NULL; + int class_a_id = 0; + int a_priority = 0; + u_int16_t a_vid = 0; + int class_b_id = 0; + int b_priority = 0; + u_int16_t b_vid = 0; + int seqnum; + int time_stamp; + int16_t data_length; + unsigned total_samples = 0; + int TEN_USEC_COUNT; + gPtpTimeData td; + int32_t sample_buffer[SAMPLES_PER_FRAME * SRC_CHANNELS]; + seventeen22_header *header0; + six1883_header *header1; + six1883_sample *sample; + FILE *freqfile; + char freqstring[8]; + uint64_t now_tscns, now_8021as; + uint64_t update_tscns, update_8021as; + unsigned delta_tscns, delta_8021as, delta_local; + long double ml_ratio, ls_ratio; + + for (;;) { + c = getopt(argc, argv, "hi:"); + if (c < 0) + break; + switch (c) { + case 'h': + usage(); + break; + case 'i': + if (interface) { + printf + ("only one interface per daemon is supported\n"); + usage(); + } + interface = strdup(optarg); + break; + } + } + if (optind < argc) + usage(); + if (NULL == interface) { + usage(); + } + rc = mrp_connect(); + if (rc) { + printf("socket creation failed\n"); + return (errno); + } + err = pci_connect(); + if (err) { + printf("connect failed (%s) - are you running as root?\n", + strerror(errno)); + return (errno); + } + err = igb_init(&igb_dev); + if (err) { + printf("init failed (%s) - is the driver really loaded?\n", + strerror(errno)); + return (errno); + } + err = igb_dma_malloc_page(&igb_dev, &a_page); + if (err) { + printf("malloc failed (%s) - out of memory?\n", + strerror(errno)); + return (errno); + } + signal(SIGINT, sigint_handler); + rc = get_mac_address(interface); + if (rc) { + printf("failed to open interface\n"); + usage(); + } + + freqfile = fopen(SYSTEM_FREQ_FILENAME, "r"); + if (freqfile == NULL) { + printf("Failed to open kernel module parameter: %s\n", + strerror(errno)); + return errno; + } + + if (fscanf(freqfile, "%7s", freqstring) != 1) { + printf("Failed to read kernel module parameter: %s\n", + strerror(errno)); + return errno; + } + + TEN_USEC_COUNT = atoi(freqstring); + /* fprintf(stderr, "TEN_USEC_COUNT = %d\n", TEN_USEC_COUNT); */ + + mrp_monitor(); + + /* + * should use mrp_get_domain() above but this is a simplification + */ + domain_a_valid = 1; + class_a_id = MSRP_SR_CLASS_A; + a_priority = MSRP_SR_CLASS_A_PRIO; + a_vid = 2; + printf("detected domain Class A PRIO=%d VID=%04x...\n", a_priority, + a_vid); + +#define PKT_SZ 100 + + mrp_register_domain(&class_a_id, &a_priority, &a_vid); + igb_set_class_bandwidth(&igb_dev, PACKET_IPG / 125000, 0, PKT_SZ - 22, + 0); + + memset(STREAM_ID, 0, sizeof(STREAM_ID)); + memcpy(STREAM_ID, STATION_ADDR, sizeof(STATION_ADDR)); + + a_packet.dmatime = a_packet.attime = a_packet.flags = 0; + a_packet.map.paddr = a_page.dma_paddr; + a_packet.map.mmap_size = a_page.mmap_size; + a_packet.offset = 0; + a_packet.vaddr = a_page.dma_vaddr + a_packet.offset; + a_packet.len = PKT_SZ; + free_packets = NULL; + seqnum = 0; + + /* divide the dma page into buffers for packets */ + for (i = 1; i < ((a_page.mmap_size) / PKT_SZ); i++) { + tmp_packet = malloc(sizeof(struct igb_packet)); + if (NULL == tmp_packet) { + printf("failed to allocate igb_packet memory!\n"); + return (errno); + } + *tmp_packet = a_packet; + tmp_packet->offset = (i * PKT_SZ); + tmp_packet->vaddr += tmp_packet->offset; + tmp_packet->next = free_packets; + memset(tmp_packet->vaddr, 0, PKT_SZ); /* MAC header at least */ + memcpy(tmp_packet->vaddr, DEST_ADDR, sizeof(DEST_ADDR)); + memcpy(tmp_packet->vaddr + 6, STATION_ADDR, + sizeof(STATION_ADDR)); + + /* Q-tag */ + ((char *)tmp_packet->vaddr)[12] = 0x81; + ((char *)tmp_packet->vaddr)[13] = 0x00; + ((char *)tmp_packet->vaddr)[14] = + ((a_priority << 13 | a_vid)) >> 8; + ((char *)tmp_packet->vaddr)[15] = + ((a_priority << 13 | a_vid)) & 0xFF; + ((char *)tmp_packet->vaddr)[16] = 0x22; /* 1722 eth type */ + ((char *)tmp_packet->vaddr)[17] = 0xF0; + + /* 1722 header update + payload */ + header0 = + (seventeen22_header *) (((char *)tmp_packet->vaddr) + 18); + header0->cd_indicator = 0; + header0->subtype = 0; + header0->sid_valid = 1; + header0->version = 0; + header0->reset = 0; + header0->reserved0 = 0; + header0->gateway_valid = 0; + header0->reserved1 = 0; + header0->timestamp_uncertain = 0; + memset(&(header0->stream_id), 0, sizeof(header0->stream_id)); + memcpy(&(header0->stream_id), STATION_ADDR, + sizeof(STATION_ADDR)); + header0->length = htons(32); + header1 = (six1883_header *) (header0 + 1); + header1->format_tag = 1; + header1->packet_channel = 0x1F; + header1->packet_tcode = 0xA; + header1->app_control = 0x0; + header1->reserved0 = 0; + header1->source_id = 0x3F; + header1->data_block_size = CHANNELS; + header1->fraction_number = 0; + header1->quadlet_padding_count = 0; + header1->source_packet_header = 0; + header1->reserved1 = 0; + header1->eoh = 0x2; + header1->format_id = 0x10; + header1->format_dependent_field = 0x02; + header1->syt = 0xFFFF; + tmp_packet->len = + 18 + sizeof(seventeen22_header) + sizeof(six1883_header) + + (SAMPLES_PER_FRAME * CHANNELS * sizeof(six1883_sample)); + free_packets = tmp_packet; + } + + /* + * subtract 16 bytes for the MAC header/Q-tag - pktsz is limited to the + * data payload of the ethernet frame . + * + * IPG is scaled to the Class (A) observation interval of packets per 125 usec + */ + fprintf(stderr, "advertising stream ...\n"); + mrp_advertise_stream(STREAM_ID, DEST_ADDR, a_vid, PKT_SZ - 16, + PACKET_IPG / 125000, a_priority, 3900); + fprintf(stderr, "awaiting a listener ...\n"); + mrp_await_listener(STREAM_ID); + printf("got a listener ...\n"); + halt_tx = 0; + + gptpinit(); + gptpscaling(&td); + + now_tscns = ST_rdtsc() * 10000 / TEN_USEC_COUNT; + update_tscns = td.local_time + td.ls_phoffset; + update_8021as = td.local_time - td.ml_phoffset; + delta_tscns = (unsigned)(now_tscns - update_tscns); + ml_ratio = -1 * (((long double)td.ml_freqoffset) / 1000000000000) + 1; + ls_ratio = -1 * (((long double)td.ls_freqoffset) / 1000000000000) + 1; + delta_local = (unsigned)(ls_ratio * delta_tscns); + delta_8021as = (unsigned)(ml_ratio * ls_ratio * delta_tscns); + now_8021as = update_8021as + delta_8021as; + last_time = td.local_time + delta_local + XMIT_DELAY; + + time_stamp = now_8021as + RENDER_DELAY; + + rc = nice(-20); + + while (listeners && !halt_tx) { + tmp_packet = free_packets; + if (NULL == tmp_packet) + goto cleanup; + header0 = + (seventeen22_header *) (((char *)tmp_packet->vaddr) + 18); + header1 = (six1883_header *) (header0 + 1); + free_packets = tmp_packet->next; + + /* unfortuntely unless this thread is at rtprio + * you get pre-empted between fetching the time + * and programming the packet and get a late packet + */ + tmp_packet->attime = last_time + PACKET_IPG; + last_time += PACKET_IPG; + + get_samples(SAMPLES_PER_FRAME, sample_buffer); + header0->seq_number = seqnum++; + if (seqnum % 4 == 0) + header0->timestamp_valid = 0; + + else + header0->timestamp_valid = 1; + + time_stamp = htonl(time_stamp); + header0->timestamp = time_stamp; + time_stamp = ntohl(time_stamp); + time_stamp += PACKET_IPG; + header1->data_block_continuity = total_samples; + total_samples += SAMPLES_PER_FRAME; + sample = + (six1883_sample *) (((char *)tmp_packet->vaddr) + + (18 + sizeof(seventeen22_header) + + sizeof(six1883_header))); + + for (i = 0; i < SAMPLES_PER_FRAME * CHANNELS; ++i) { + uint32_t tmp = htonl(sample_buffer[i]); + sample[i].label = 0x40; + memcpy(&(sample[i].value), &(tmp), + sizeof(sample[i].value)); + } + + err = igb_xmit(&igb_dev, 0, tmp_packet); + + if (!err) { + continue; + } + + if (ENOSPC == err) { + + /* put back for now */ + tmp_packet->next = free_packets; + free_packets = tmp_packet; + } + + cleanup: igb_clean(&igb_dev, &cleaned_packets); + i = 0; + while (cleaned_packets) { + i++; + tmp_packet = cleaned_packets; + cleaned_packets = cleaned_packets->next; + tmp_packet->next = free_packets; + free_packets = tmp_packet; + } + } + rc = nice(0); + + if (halt_tx == 0) + printf("listener left ...\n"); + halt_tx = 1; + + mrp_unadvertise_stream(STREAM_ID, DEST_ADDR, a_vid, PKT_SZ - 16, + PACKET_IPG / 125000, a_priority, 3900); + + igb_set_class_bandwidth(&igb_dev, 0, 0, 0, 0); /* disable Qav */ + + rc = mrp_disconnect(); + + igb_dma_free_page(&igb_dev, &a_page); + + err = igb_detach(&igb_dev); + + pthread_exit(NULL); + + return (0); +} diff --git a/examples/test.c b/examples/test.c deleted file mode 100644 index 524aa24..0000000 --- a/examples/test.c +++ /dev/null @@ -1,219 +0,0 @@ -/****************************************************************************** - - Copyright (c) 2001-2012, Intel Corporation - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the name of the Intel Corporation nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - -******************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include - -#include "igb.h" - -#define IGB_BIND_NAMESZ 24 - -device_t igb_dev; -volatile int halt_tx; - -void -sigint_handler (int signum) -{ - printf("got SIGINT\n"); - halt_tx = signum; -} - -int -connect() -{ - struct pci_access *pacc; - struct pci_dev *dev; - int err; - char devpath[IGB_BIND_NAMESZ]; - - memset(&igb_dev, 0, sizeof(device_t)); - - pacc = pci_alloc(); - pci_init(pacc); - pci_scan_bus(pacc); - for (dev=pacc->devices; dev; dev=dev->next) - { - pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); - - igb_dev.pci_vendor_id = dev->vendor_id; - igb_dev.pci_device_id = dev->device_id; - igb_dev.domain = dev->domain; - igb_dev.bus = dev->bus; - igb_dev.dev = dev->dev; - igb_dev.func = dev->func; - - snprintf(devpath, IGB_BIND_NAMESZ, "%04x:%02x:%02x.%d", \ - dev->domain, dev->bus, dev->dev, dev->func ); - printf("probing %s\n", devpath); - - err = igb_probe(&igb_dev); - - if (err) { - continue; - } - - printf ("attaching to %s\n", devpath); - err = igb_attach(devpath, &igb_dev); - - if (err) { - printf ("attach failed! (%s)\n", strerror(errno)); - continue; - } - printf("attach successful to %s\n", devpath); - goto out; - } - - pci_cleanup(pacc); - return ENXIO; - -out: - pci_cleanup(pacc); - return 0; - -} - -#define PACKET_IPG (20000000) /* 20 msec */ - -int -main() -{ - unsigned i; - int err; - struct igb_dma_alloc a_page; - struct igb_packet a_packet; - struct igb_packet *tmp_packet; - struct igb_packet *cleaned_packets; - struct igb_packet *free_packets; - u_int64_t last_time; - u_int64_t rdtsc0; - - err = connect(); - - if (err) { printf("connect failed (%s)\n", strerror(errno)); return(errno); } - - err = igb_init(&igb_dev); - - if (err) { printf("init failed (%s)\n", strerror(errno)); return(errno); } - - err = igb_dma_malloc_page(&igb_dev, &a_page); - - if (err) { printf("malloc failed (%s)\n", strerror(errno)); return(errno); } - -#define PKT_SZ 200 - a_packet.dmatime = a_packet.attime = a_packet.flags = 0; - a_packet.map.paddr = a_page.dma_paddr; - a_packet.map.mmap_size = a_page.mmap_size; - - a_packet.offset = 0; - a_packet.vaddr = a_page.dma_vaddr + a_packet.offset; - a_packet.len = PKT_SZ; - - free_packets = NULL; - - /* divide the dma page into buffers for packets */ - for (i = 1; i < ((a_page.mmap_size) / PKT_SZ); i++) { - tmp_packet = malloc(sizeof(struct igb_packet)); - if (NULL == tmp_packet) { printf("failed to allocate igb_packet memory!\n"); return(errno); } - - *tmp_packet = a_packet; - tmp_packet->offset = (i * PKT_SZ); - tmp_packet->vaddr += tmp_packet->offset; - tmp_packet->next = free_packets; - memset(tmp_packet->vaddr, 0xffffffff, PKT_SZ); /* MAC header at least */ - free_packets = tmp_packet; - } - - igb_set_class_bandwidth(&igb_dev, 0, 0, 0); /* disable Qav */ - - halt_tx = 0; - signal(SIGINT, sigint_handler); - - - igb_get_wallclock(&igb_dev, &last_time, &rdtsc0); - - while (!halt_tx) { - tmp_packet = free_packets; - - if (NULL == tmp_packet) goto cleanup; - - free_packets = tmp_packet->next; - - tmp_packet->attime = last_time + PACKET_IPG; - *(u_int64_t *)(tmp_packet->vaddr + 32) = tmp_packet->attime; - - err = igb_xmit(&igb_dev, 0, tmp_packet); - - if (!err) { last_time += PACKET_IPG; continue; } - - if (ENOSPC == err) { - /* put back for now */ - tmp_packet->next = free_packets; - free_packets = tmp_packet; - } -cleanup: - igb_clean(&igb_dev, &cleaned_packets); - i = 0; - while (cleaned_packets) { - i++; - tmp_packet = cleaned_packets; - cleaned_packets = cleaned_packets->next; - /* remap attime to compare to dma time */ - while (tmp_packet->attime > 999999999) tmp_packet->attime -= 1000000000; - /* doesn't handle wrap-around ! */ - printf("delta(attime-dmatime)=%d\n", (unsigned int)(tmp_packet->attime - tmp_packet->dmatime)); - tmp_packet->next = free_packets; - free_packets = tmp_packet; - } - - } - - igb_dma_free_page(&igb_dev, &a_page); - err = igb_detach(&igb_dev); - return(0); -} - diff --git a/kmod/LICENSE b/kmod/igb/LICENSE similarity index 100% rename from kmod/LICENSE rename to kmod/igb/LICENSE diff --git a/kmod/Makefile b/kmod/igb/Makefile similarity index 100% rename from kmod/Makefile rename to kmod/igb/Makefile diff --git a/kmod/igb/README b/kmod/igb/README new file mode 100644 index 0000000..08f9a5c --- /dev/null +++ b/kmod/igb/README @@ -0,0 +1,31 @@ +INTRODUCTION + +This component demonstrates various features of the Intel I210 Ethernet +controller. These features can be used for developing Audio/Video Bridging +applications, Industrial Ethernet applications which require precise timing +control over frame transmission, or test harnesses for measuring system +latencies and sampling events. + +This component - igb_avb - is limited to the Intel I210 Ethernet controller. +The kernel module can be loaded in parallel to existing in-kernel igb modules +which may be used on other supported Intel LAN controllers. Modifications are +required to the in-kernel drivers if the existing in-kernel igb driver has +support for the Intel I210. + +BUILDING + +The kernel igb module can be built which supports the latest Linux kernel +3.x PTP clock support - to enable, modify kmod/Makefile and enable -DCONFIG_PTP +as a build option (e.g. EXTRA_CFLAGS += -DCONFIG_PTP). + +RUNNING + +To install the kernel mode driver, you must have root permissions. Typically, +the driver is loaded by: + sudo modprobe dca + sudo modprobe ptp + sudo insmod ./igb_avb.ko + +As 3.4 and later kernels include support for the I210, you may need to 'rmmod +igb' before loading the igb_avb module. + diff --git a/kmod/e1000_82575.c b/kmod/igb/e1000_82575.c similarity index 100% rename from kmod/e1000_82575.c rename to kmod/igb/e1000_82575.c diff --git a/kmod/e1000_82575.h b/kmod/igb/e1000_82575.h similarity index 100% rename from kmod/e1000_82575.h rename to kmod/igb/e1000_82575.h diff --git a/kmod/e1000_api.c b/kmod/igb/e1000_api.c similarity index 100% rename from kmod/e1000_api.c rename to kmod/igb/e1000_api.c diff --git a/kmod/e1000_api.h b/kmod/igb/e1000_api.h similarity index 100% rename from kmod/e1000_api.h rename to kmod/igb/e1000_api.h diff --git a/kmod/e1000_defines.h b/kmod/igb/e1000_defines.h similarity index 100% rename from kmod/e1000_defines.h rename to kmod/igb/e1000_defines.h diff --git a/kmod/e1000_hw.h b/kmod/igb/e1000_hw.h similarity index 100% rename from kmod/e1000_hw.h rename to kmod/igb/e1000_hw.h diff --git a/kmod/e1000_i210.c b/kmod/igb/e1000_i210.c similarity index 100% rename from kmod/e1000_i210.c rename to kmod/igb/e1000_i210.c diff --git a/kmod/e1000_i210.h b/kmod/igb/e1000_i210.h similarity index 100% rename from kmod/e1000_i210.h rename to kmod/igb/e1000_i210.h diff --git a/kmod/e1000_mac.c b/kmod/igb/e1000_mac.c similarity index 100% rename from kmod/e1000_mac.c rename to kmod/igb/e1000_mac.c diff --git a/kmod/e1000_mac.h b/kmod/igb/e1000_mac.h similarity index 100% rename from kmod/e1000_mac.h rename to kmod/igb/e1000_mac.h diff --git a/kmod/e1000_manage.c b/kmod/igb/e1000_manage.c similarity index 100% rename from kmod/e1000_manage.c rename to kmod/igb/e1000_manage.c diff --git a/kmod/e1000_manage.h b/kmod/igb/e1000_manage.h similarity index 100% rename from kmod/e1000_manage.h rename to kmod/igb/e1000_manage.h diff --git a/kmod/e1000_mbx.c b/kmod/igb/e1000_mbx.c similarity index 100% rename from kmod/e1000_mbx.c rename to kmod/igb/e1000_mbx.c diff --git a/kmod/e1000_mbx.h b/kmod/igb/e1000_mbx.h similarity index 100% rename from kmod/e1000_mbx.h rename to kmod/igb/e1000_mbx.h diff --git a/kmod/e1000_nvm.c b/kmod/igb/e1000_nvm.c similarity index 100% rename from kmod/e1000_nvm.c rename to kmod/igb/e1000_nvm.c diff --git a/kmod/e1000_nvm.h b/kmod/igb/e1000_nvm.h similarity index 100% rename from kmod/e1000_nvm.h rename to kmod/igb/e1000_nvm.h diff --git a/kmod/e1000_osdep.h b/kmod/igb/e1000_osdep.h similarity index 100% rename from kmod/e1000_osdep.h rename to kmod/igb/e1000_osdep.h diff --git a/kmod/e1000_phy.c b/kmod/igb/e1000_phy.c similarity index 100% rename from kmod/e1000_phy.c rename to kmod/igb/e1000_phy.c diff --git a/kmod/e1000_phy.h b/kmod/igb/e1000_phy.h similarity index 100% rename from kmod/e1000_phy.h rename to kmod/igb/e1000_phy.h diff --git a/kmod/e1000_regs.h b/kmod/igb/e1000_regs.h similarity index 100% rename from kmod/e1000_regs.h rename to kmod/igb/e1000_regs.h diff --git a/kmod/igb.h b/kmod/igb/igb.h similarity index 100% rename from kmod/igb.h rename to kmod/igb/igb.h diff --git a/kmod/igb_ethtool.c b/kmod/igb/igb_ethtool.c similarity index 100% rename from kmod/igb_ethtool.c rename to kmod/igb/igb_ethtool.c diff --git a/kmod/igb_main.c b/kmod/igb/igb_main.c similarity index 100% rename from kmod/igb_main.c rename to kmod/igb/igb_main.c diff --git a/kmod/igb_param.c b/kmod/igb/igb_param.c similarity index 100% rename from kmod/igb_param.c rename to kmod/igb/igb_param.c diff --git a/kmod/igb_procfs.c b/kmod/igb/igb_procfs.c similarity index 100% rename from kmod/igb_procfs.c rename to kmod/igb/igb_procfs.c diff --git a/kmod/igb_ptp.c b/kmod/igb/igb_ptp.c similarity index 100% rename from kmod/igb_ptp.c rename to kmod/igb/igb_ptp.c diff --git a/kmod/igb_regtest.h b/kmod/igb/igb_regtest.h similarity index 100% rename from kmod/igb_regtest.h rename to kmod/igb/igb_regtest.h diff --git a/kmod/igb_sysfs.c b/kmod/igb/igb_sysfs.c similarity index 100% rename from kmod/igb_sysfs.c rename to kmod/igb/igb_sysfs.c diff --git a/kmod/igb_vmdq.c b/kmod/igb/igb_vmdq.c similarity index 100% rename from kmod/igb_vmdq.c rename to kmod/igb/igb_vmdq.c diff --git a/kmod/igb_vmdq.h b/kmod/igb/igb_vmdq.h similarity index 100% rename from kmod/igb_vmdq.h rename to kmod/igb/igb_vmdq.h diff --git a/kmod/kcompat.c b/kmod/igb/kcompat.c similarity index 100% rename from kmod/kcompat.c rename to kmod/igb/kcompat.c diff --git a/kmod/kcompat.h b/kmod/igb/kcompat.h similarity index 100% rename from kmod/kcompat.h rename to kmod/igb/kcompat.h diff --git a/kmod/kcompat_ethtool.c b/kmod/igb/kcompat_ethtool.c similarity index 100% rename from kmod/kcompat_ethtool.c rename to kmod/igb/kcompat_ethtool.c diff --git a/lib/LICENSE b/lib/igb/LICENSE similarity index 100% rename from lib/LICENSE rename to lib/igb/LICENSE diff --git a/lib/Makefile b/lib/igb/Makefile similarity index 100% rename from lib/Makefile rename to lib/igb/Makefile diff --git a/lib/e1000_82575.h b/lib/igb/e1000_82575.h similarity index 100% rename from lib/e1000_82575.h rename to lib/igb/e1000_82575.h diff --git a/lib/e1000_defines.h b/lib/igb/e1000_defines.h similarity index 100% rename from lib/e1000_defines.h rename to lib/igb/e1000_defines.h diff --git a/lib/e1000_hw.h b/lib/igb/e1000_hw.h similarity index 100% rename from lib/e1000_hw.h rename to lib/igb/e1000_hw.h diff --git a/lib/e1000_osdep.h b/lib/igb/e1000_osdep.h similarity index 100% rename from lib/e1000_osdep.h rename to lib/igb/e1000_osdep.h diff --git a/lib/e1000_regs.h b/lib/igb/e1000_regs.h similarity index 100% rename from lib/e1000_regs.h rename to lib/igb/e1000_regs.h diff --git a/lib/igb.c b/lib/igb/igb.c similarity index 91% rename from lib/igb.c rename to lib/igb/igb.c index c149e99..4c814fa 100644 --- a/lib/igb.c +++ b/lib/igb/igb.c @@ -981,7 +981,11 @@ igb_get_wallclock(device_t *dev, u_int64_t *curtime, u_int64_t *rdtsc) return(0); } int -igb_set_class_bandwidth(device_t *dev, u_int32_t class_a, u_int32_t class_b, u_int32_t tpktsz) +igb_set_class_bandwidth(device_t *dev, + u_int32_t class_a, + u_int32_t class_b, + u_int32_t tpktsz_a, + u_int32_t tpktsz_b) { u_int32_t tqavctrl; u_int32_t tqavcc0, tqavcc1; @@ -992,6 +996,7 @@ igb_set_class_bandwidth(device_t *dev, u_int32_t class_a, u_int32_t class_b, u_i struct e1000_hw *hw; struct igb_link_cmd link; int err; + float class_a_percent, class_b_percent; if (NULL == dev) return EINVAL; adapter = (struct adapter *)dev->private_data; @@ -1011,9 +1016,17 @@ igb_set_class_bandwidth(device_t *dev, u_int32_t class_a, u_int32_t class_b, u_i if (link.duplex != FULL_DUPLEX ) return EINVAL; - if ((class_a + class_b) > 75 ) return EINVAL; + if (tpktsz_a < 64) + tpktsz_a = 64; /* minimum ethernet frame size */ - if ((tpktsz < 64) || (tpktsz > 2000)) return EINVAL; + if (tpktsz_a > 1500) + return EINVAL; + + if (tpktsz_b < 64) + tpktsz_b = 64; /* minimum ethernet frame size */ + + if (tpktsz_b > 1500) + return EINVAL; tqavctrl = E1000_READ_REG(hw, E1000_TQAVCTRL); @@ -1032,24 +1045,59 @@ igb_set_class_bandwidth(device_t *dev, u_int32_t class_a, u_int32_t class_b, u_i else linkrate = E1000_TQAVCC_LINKRATE; - /* XXX convert to fixed point or floating point percents */ - class_a_idle = (class_a * 2 * linkrate / 100); /* 'class_a' is a percent */ - class_b_idle = (class_b * 2 * linkrate / 100); + /* + * class_a and class_b are the packets-per-(respective)observation + * interval (125 usec for class A, 250 usec for class B) + * these parameters are also used when establishing the MSRP + * talker advertise attribute (as well as the tpktsize) + * + * note that class_a and class_b are independent of the media + * rate. For our idle slope calculation, we need to scale the + * (tpktsz + (media overhead)) * rate -> percentage of media rate. + */ + + /* 12=Ethernet IPG, 8=Preamble+Start of Frame, 18=Mac Header with VLAN+Etype, 4=CRC */ + class_a_percent = (float)((tpktsz_a + (12 + 8 + 18 + 4)) * class_a) ; + class_b_percent = (float)((tpktsz_b + (12 + 8 + 18 + 4)) * class_b) ; + + class_a_percent /= 0.000125; /* class A observation window */ + class_b_percent /= 0.000250; /* class B observation window */ + + if (link.speed == 100) { + class_a_percent /= (100000000.0 / 8); /* bytes-per-sec @ 100Mbps */ + class_b_percent /= (100000000.0 / 8); + class_a_idle = (u_int32_t)(class_a_percent * 0.2 * (float)linkrate + 0.5); + class_b_idle = (u_int32_t)(class_b_percent * 0.2 * (float)linkrate + 0.5); + } else { + class_a_percent /= (1000000000.0 / 8); /* bytes-per-sec @ 1Gbps */ + class_b_percent /= (1000000000.0 / 8); + class_a_idle = (u_int32_t)(class_a_percent * 2.0 * (float)linkrate + 0.5); + class_b_idle = (u_int32_t)(class_b_percent * 2.0 * (float)linkrate + 0.5); + } + + if ((class_a_percent + class_b_percent) > 0.75) + return EINVAL; tqavcc0 |= class_a_idle; tqavcc1 |= class_b_idle; /* - * The datasheet lists a formula for configuring the high credit threshold, - * however it is only relevant in the conditions the high priority SR queues - * are internally pre-empted by manageability traffic or low power proxy modes - - * and if the SR queues are pre-empted, they would burst more packets than expected. - * So - if you enable manageability or proxy modes while running AVB traffic, you - * should program the high credit thresholds to prevent non-compliant packet bursts. - * But be aware the stream didn't stream as much bandwidth as it reserved, - * and you may have had an underrun on the listener. + * hiCredit is the number of idleslope credits accumulated due to delay T + * + * we assume the maxInterferenceSize is 18 + 4 + 1500 (1522). + * Note: if EEE is enabled, we should use for maxInterferenceSize + * the overhead of link recovery (a media-specific quantity). + */ + tqavhc0 = 0x80000000 + (class_a_idle * 1522 / linkrate ); /* L.10 */ + + /* + * Class B high credit is is the same, except the delay + * is the MaxBurstSize of Class A + maxInterferenceSize of non-SR traffic + * + * L.41 + * max Class B delay = (1522 + tpktsz_a) / (linkrate - class_a_idle) */ - tqavhc0 = 0xFFFFFFFF; - tqavhc1 = 0xFFFFFFFF; + + tqavhc1 = 0x80000000 + (class_b_idle * ((1522 + tpktsz_a)/ (linkrate - class_a_idle))); /* implicitly enable the Qav shaper */ tqavctrl |= E1000_TQAVCTRL_TX_ARB; diff --git a/lib/igb.h b/lib/igb/igb.h similarity index 98% rename from lib/igb.h rename to lib/igb/igb.h index 4e8239c..5eba112 100644 --- a/lib/igb.h +++ b/lib/igb/igb.h @@ -86,7 +86,7 @@ void igb_dma_free_page(device_t *dev, struct igb_dma_alloc *page); int igb_xmit(device_t *dev, unsigned int queue_index, struct igb_packet *packet); void igb_clean(device_t *dev, struct igb_packet **cleaned_packets); int igb_get_wallclock(device_t *dev, u_int64_t *curtime, u_int64_t *rdtsc); -int igb_set_class_bandwidth(device_t *dev, u_int32_t class_a, u_int32_t class_b, u_int32_t tpktsz); +int igb_set_class_bandwidth(device_t *dev, u_int32_t class_a, u_int32_t class_b, u_int32_t tpktsz_a, u_int32_t tpktsz_b); void igb_trigger(device_t *dev, u_int32_t data); void igb_readreg(device_t *dev, u_int32_t reg, u_int32_t *data); diff --git a/lib/igb_internal.h b/lib/igb/igb_internal.h similarity index 100% rename from lib/igb_internal.h rename to lib/igb/igb_internal.h -- 2.7.4