From 1c87ad9804f6bd33e395b81a4bd6dd99971b200f Mon Sep 17 00:00:00 2001 From: Mounesh Sutar Date: Thu, 2 Nov 2017 16:09:31 +0530 Subject: daemons: segmentation fault while executing maap_daemon on aarch64 platform. For aarch64 platforms PTHREAD_STACK_MIN is 128k, according to it setting THREAD_STACK_SIZE to value of PTHREAD_STACK_MIN instead of 64k. --- daemons/maap/linux/src/maap_log_linux.c | 17 ++++++++++++++--- daemons/shaper/src/shaper_log_linux.c | 16 +++++++++++++--- lib/avtp_pipeline/platform/Linux/openavb_tasks.h | 10 +++++++++- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/daemons/maap/linux/src/maap_log_linux.c b/daemons/maap/linux/src/maap_log_linux.c index 2d6f02b3..35dcfcbc 100644 --- a/daemons/maap/linux/src/maap_log_linux.c +++ b/daemons/maap/linux/src/maap_log_linux.c @@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include "platform.h" #include "maap_log_queue.h" @@ -74,7 +75,14 @@ extern void *loggingThreadFn(void *pv); THREAD_TYPE(loggingThread); THREAD_DEFINITON(loggingThread); -#define THREAD_STACK_SIZE 65536 +#if !defined(PTHREAD_STACK_MIN) +#error "PTHREAD_STACK_MIN variable not defined" +#elif (PTHREAD_STACK_MIN > 65536) +#define THREAD_STACK_SIZE PTHREAD_STACK_MIN +#else +#define THREAD_STACK_SIZE 65536 +#endif + #define loggingThread_THREAD_STK_SIZE THREAD_STACK_SIZE static MUTEX_HANDLE_ALT(gLogMutex); @@ -211,13 +219,16 @@ void maapLogInit(void) loggingThreadRunning = TRUE; THREAD_CREATE(loggingThread, loggingThread, NULL, loggingThreadFn, NULL); THREAD_CHECK_ERROR(loggingThread, "Thread / task creation failed", errResult); - if (errResult); // Already reported + if (errResult) { + loggingThreadRunning = FALSE; + MAAP_LOG_ERROR("Could not log data: loggingThread create failure"); + } } } void maapLogExit() { - if (MAAP_LOG_FROM_THREAD) { + if (MAAP_LOG_FROM_THREAD && loggingThreadRunning ) { loggingThreadRunning = FALSE; THREAD_JOIN(loggingThread, NULL); } diff --git a/daemons/shaper/src/shaper_log_linux.c b/daemons/shaper/src/shaper_log_linux.c index d68a79a2..38017c38 100644 --- a/daemons/shaper/src/shaper_log_linux.c +++ b/daemons/shaper/src/shaper_log_linux.c @@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include "platform.h" #include "shaper_log_queue.h" @@ -74,7 +75,13 @@ extern void *loggingThreadFn(void *pv); THREAD_TYPE(loggingThread); THREAD_DEFINITON(loggingThread); -#define THREAD_STACK_SIZE 65536 +#if !defined(PTHREAD_STACK_MIN) +#error "PTHREAD_STACK_MIN variable not defined" +#elif (PTHREAD_STACK_MIN > 65536) +#define THREAD_STACK_SIZE PTHREAD_STACK_MIN +#else +#define THREAD_STACK_SIZE 65536 +#endif #define loggingThread_THREAD_STK_SIZE THREAD_STACK_SIZE static MUTEX_HANDLE_ALT(gLogMutex); @@ -217,13 +224,16 @@ void shaperLogInit(void) loggingThreadRunning = TRUE; THREAD_CREATE(loggingThread, loggingThread, NULL, loggingThreadFn, NULL); THREAD_CHECK_ERROR(loggingThread, "Thread / task creation failed", errResult); - if (errResult) {} // Already reported + if (errResult) { + loggingThreadRunning = FALSE; + SHAPER_LOG_ERROR("Could not log data: loggingThread create failure"); + } } } void shaperLogExit() { - if (SHAPER_LOG_FROM_THREAD) { + if (SHAPER_LOG_FROM_THREAD && loggingThreadRunning) { loggingThreadRunning = FALSE; THREAD_JOIN(loggingThread, NULL); } diff --git a/lib/avtp_pipeline/platform/Linux/openavb_tasks.h b/lib/avtp_pipeline/platform/Linux/openavb_tasks.h index 01969f09..387fd985 100644 --- a/lib/avtp_pipeline/platform/Linux/openavb_tasks.h +++ b/lib/avtp_pipeline/platform/Linux/openavb_tasks.h @@ -32,7 +32,15 @@ https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. #ifndef _EAVBTASKS_H #define _EAVBTASKS_H -#define THREAD_STACK_SIZE 65536 +#include + +#if !defined(PTHREAD_STACK_MIN) +#error "PTHREAD_STACK_MIN variable not defined" +#elif (PTHREAD_STACK_MIN > 65536) +#define THREAD_STACK_SIZE PTHREAD_STACK_MIN +#else +#define THREAD_STACK_SIZE 65536 +#endif /////////////////////////// // Platform code Tasks values -- cgit v1.2.1 From 5f2fea5cfe48bfd2f52a7f5163209aed3914af86 Mon Sep 17 00:00:00 2001 From: Fredrik Hallenberg Date: Thu, 16 Nov 2017 14:44:29 +0100 Subject: Fix mmrp parsing --- examples/mrp_client/mrpdhelper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/mrp_client/mrpdhelper.c b/examples/mrp_client/mrpdhelper.c index ddd5a958..a4026903 100644 --- a/examples/mrp_client/mrpdhelper.c +++ b/examples/mrp_client/mrpdhelper.c @@ -301,8 +301,8 @@ static int parse_mmrp(char *sz, size_t len, struct mrpdhelper_notify *n) if (parse_state(&sz[5], n) < 0) return -1; - n->attrib = mrpdhelper_attribtype_mvrp; - if (sscanf(&sz[8], "M=%" SCNx64, &n->u.m.mac) != 1) + n->attrib = mrpdhelper_attribtype_mmrp; + if (sscanf(&sz[4], "M=%" SCNx64, &n->u.m.mac) != 1) return -1; return parse_registrar(sz, n, NULL); } -- cgit v1.2.1 From adc779488a6a5305ea9159df11aed958145bfd79 Mon Sep 17 00:00:00 2001 From: Fredrik Hallenberg Date: Thu, 16 Nov 2017 14:47:36 +0100 Subject: Fix crash when enabling LOG_MVRP --- daemons/mrpd/mvrp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemons/mrpd/mvrp.c b/daemons/mrpd/mvrp.c index b36e3ace..fa14ac37 100644 --- a/daemons/mrpd/mvrp.c +++ b/daemons/mrpd/mvrp.c @@ -331,7 +331,8 @@ int mvrp_event(int event, struct mvrp_attribute *rattrib) } attrib = mvrp_conditional_reclaim(attrib); #if LOG_MVRP - mvrp_print_debug_info(event, attrib); + if (attrib != NULL) + mvrp_print_debug_info(event, attrib); #endif break; default: -- cgit v1.2.1 From fa0163d131885fab1c1f2c0954e453d0da93f93f Mon Sep 17 00:00:00 2001 From: Michal Galka Date: Mon, 27 Nov 2017 11:40:55 +0100 Subject: Fixing stale Tx timestamp fetch. --- daemons/gptp/linux/src/linux_hal_generic.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/daemons/gptp/linux/src/linux_hal_generic.cpp b/daemons/gptp/linux/src/linux_hal_generic.cpp index 5364fa6a..395e344f 100644 --- a/daemons/gptp/linux/src/linux_hal_generic.cpp +++ b/daemons/gptp/linux/src/linux_hal_generic.cpp @@ -33,6 +33,8 @@ #include #include +#include +#include #include #include #include @@ -285,6 +287,10 @@ int LinuxTimestamperGeneric::HWTimestamper_txtimestamp struct cmsghdr *cmsg; struct sockaddr_ll remote; struct iovec sgentry; + PTPMessageId reflectedMessageId; + uint8_t reflected_bytes[46]; + uint8_t *gptpCommonHeader; + uint16_t sequenceId; struct { struct cmsghdr cm; char control[256]; @@ -296,8 +302,10 @@ int LinuxTimestamperGeneric::HWTimestamper_txtimestamp msg.msg_iov = &sgentry; msg.msg_iovlen = 1; - sgentry.iov_base = NULL; - sgentry.iov_len = 0; + sgentry.iov_base = reflected_bytes; + sgentry.iov_len = sizeof(reflected_bytes); + + gptpCommonHeader = reflected_bytes + 14; memset( &remote, 0, sizeof(remote)); msg.msg_name = (caddr_t) &remote; @@ -316,6 +324,14 @@ int LinuxTimestamperGeneric::HWTimestamper_txtimestamp goto done; } } + sequenceId = PLAT_ntohs(*((uint16_t*)(PTP_COMMON_HDR_SEQUENCE_ID(gptpCommonHeader)))); + reflectedMessageId.setSequenceId(sequenceId); + reflectedMessageId.setMessageType((MessageType)(*gptpCommonHeader & 0xF)); + if (messageId != reflectedMessageId) { + GPTP_LOG_WARNING("Timestamp discarded due to wrong message id"); + ret = GPTP_EC_EAGAIN; + goto done; + } // Retrieve the timestamp cmsg = CMSG_FIRSTHDR(&msg); -- cgit v1.2.1 From 3fde53e712e70b48a5405fd286aef637df751d33 Mon Sep 17 00:00:00 2001 From: Michal Galka Date: Fri, 1 Dec 2017 10:22:57 +0100 Subject: Got rid of magic numbers. More self-explanatory code. --- daemons/gptp/common/ptptypes.hpp | 1 + daemons/gptp/linux/src/linux_hal_generic.cpp | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/daemons/gptp/common/ptptypes.hpp b/daemons/gptp/common/ptptypes.hpp index 03cd57be..f73c8f92 100644 --- a/daemons/gptp/common/ptptypes.hpp +++ b/daemons/gptp/common/ptptypes.hpp @@ -43,6 +43,7 @@ typedef double FrequencyRatio; /*!< Frequency Ratio */ typedef long double FrequencyRatio; /*!< Frequency Ratio */ #endif +#define ETHER_HDR_LEN (14) #define ETHER_ADDR_OCTETS 6 /*!< Number of octets in a link layer address*/ #define IP_ADDR_OCTETS 4 /*!< Number of octets in a ip address*/ #define PTP_ETHERTYPE 0x88F7 /*!< PTP ethertype */ diff --git a/daemons/gptp/linux/src/linux_hal_generic.cpp b/daemons/gptp/linux/src/linux_hal_generic.cpp index 395e344f..9eeba46f 100644 --- a/daemons/gptp/linux/src/linux_hal_generic.cpp +++ b/daemons/gptp/linux/src/linux_hal_generic.cpp @@ -30,7 +30,6 @@ POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ - #include #include #include @@ -288,7 +287,7 @@ int LinuxTimestamperGeneric::HWTimestamper_txtimestamp struct sockaddr_ll remote; struct iovec sgentry; PTPMessageId reflectedMessageId; - uint8_t reflected_bytes[46]; + uint8_t reflected_bytes[ETHER_HDR_LEN + PTP_COMMON_HDR_LENGTH]; uint8_t *gptpCommonHeader; uint16_t sequenceId; struct { @@ -305,7 +304,7 @@ int LinuxTimestamperGeneric::HWTimestamper_txtimestamp sgentry.iov_base = reflected_bytes; sgentry.iov_len = sizeof(reflected_bytes); - gptpCommonHeader = reflected_bytes + 14; + gptpCommonHeader = reflected_bytes + ETHER_HDR_LEN; memset( &remote, 0, sizeof(remote)); msg.msg_name = (caddr_t) &remote; @@ -326,7 +325,7 @@ int LinuxTimestamperGeneric::HWTimestamper_txtimestamp } sequenceId = PLAT_ntohs(*((uint16_t*)(PTP_COMMON_HDR_SEQUENCE_ID(gptpCommonHeader)))); reflectedMessageId.setSequenceId(sequenceId); - reflectedMessageId.setMessageType((MessageType)(*gptpCommonHeader & 0xF)); + reflectedMessageId.setMessageType((MessageType)(*PTP_COMMON_HDR_TRANSSPEC_MSGTYPE(gptpCommonHeader) & 0xF)); if (messageId != reflectedMessageId) { GPTP_LOG_WARNING("Timestamp discarded due to wrong message id"); ret = GPTP_EC_EAGAIN; -- cgit v1.2.1 From b02a3663431cdf3f5be0ceb0b190ecf6a337f94f Mon Sep 17 00:00:00 2001 From: Brant Thomsen Date: Tue, 5 Dec 2017 09:29:38 -0700 Subject: AVTP Pipeline Build Cleanup Switched from defaulting from i210-specific to generic adapter support. (Intel i210 will still work if the PCAP interface is used.) GStreamer support defaults to being disabled, as the feature is not needed. AVDECC binaries copied to the build/bin folder, so that .INI files can be shared more easily with the AVTP Pipeline binaries. Explicitly sets CMAKE_TOOLCHAIN_FILE, which makes it easier to change the build type. README file recommends using run_daemons.sh to start necessary daemons. --- lib/avtp_pipeline/README.md | 27 +++++++++++++-------------- lib/avtp_pipeline/avtp_avdecc.mk | 9 ++++++--- lib/avtp_pipeline/avtp_pipeline.mk | 7 ++++--- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/lib/avtp_pipeline/README.md b/lib/avtp_pipeline/README.md index 23e6e525..cbf89a1a 100644 --- a/lib/avtp_pipeline/README.md +++ b/lib/avtp_pipeline/README.md @@ -6,8 +6,7 @@ - gPTP - MAAP - MSRP - - igb direct for packet TX. - - igb credit based shaper + - credit based shaper - build system - Credit-based shaper algorithm is not used for individual streams. - Testing of various mappings and benchmarking has been performed. Look at the end of this file for benchmark results. @@ -25,15 +24,17 @@ - $ sudo apt-get install linux-headers-generic - linux-headers (same version as the kernel you're building for) -- Install dependencies for AVTP pipeline +- Additional install dependencies for AVTP pipeline - $ sudo apt-get install libglib2.0-dev + - $ sudo apt-get install libasound2-dev + +- Additional install dependencies for AVTP pipeline if GStreamer support is enabled - $ sudo apt-get install libgstreamer0.10-dev - $ sudo apt-get install libgstreamer-plugins-base0.10-dev - - $ sudo apt-get install libasound2-dev ### Building everything - Building from the repo root -- $ ARCH=I210 make all +- $ make all ### Building just AVTP pipeline - $ make avtp_pipeline @@ -51,7 +52,8 @@ Make sure to call `make avtp_pipeline_clean` before. ### Building just AVTP AVDECC support - $ make avtp_avdecc -Binaries will be installed in lib/avtp_pipeline/build_avdecc/bin. +Binaries will be installed in lib/avtp_pipeline/build/bin. +Build files will be in the lib/avtp_pipeline/build_avdecc directory, to avoid interfering with the AVTP Pipeline build files. The openavb_avdecc binary needs to be run in addition to the AVTP pipeline binary (openavb_harness or openavb_host) for AVDECC to be supported. @@ -61,13 +63,10 @@ The openavb_avdecc binary needs to be run in addition to the AVTP pipeline binar ## Running OpenAvnu daemons - Helper scripts in the repo root. - `$ sudo ./run_igb.sh eth1` - - Load the igb driver. Supply the interface name (ethx) as parameter. -- `$ sudo ./run_gptp.sh eth1` - - Start gptp daemon. Supply the interface name (ethx) as parameter. -- `$ sudo ./run_srp.sh eth1` - - Start msrp daemon. Supply the interface name (ethx) as parameter. -- `$ sudo ./run_maap.sh eth1` - - Start maap daemon. Supply the interface name (ethx) as parameter. + - Load the igb driver. Supply the interface name (ethx) as parameter. Only needed if using IGB support for the Intel i210 adapter. +- `$ sudo ./run_daemons.sh eth1` + - Start gptp, msrp, maap, and shaper daemons. Supply the interface name (ethx) as parameter. + - Daemons can also be started individually using the run_gptp.sh, run_srp.sh, run_maap.sh, and run_shaper.sh scripts. ## Running OpenAvnu simple talker example - `$ sudo ./run_simple_talker.sh eth1` @@ -84,7 +83,7 @@ The openavb_avdecc binary needs to be run in addition to the AVTP pipeline binar ## Benchmark results -All test done on DELL Optiplex 755 with Intel Core 2 Duo CPU E8400 @ 3.00GHz +All test done on DELL Optiplex 755 with Intel Core 2 Duo CPU E8400 @ 3.00GHz | Type | Comment | Class | Streams | CPU Talker | CPU Listener | |:-------:| -------------|:-----:| -------:| ----------:| ------------:| diff --git a/lib/avtp_pipeline/avtp_avdecc.mk b/lib/avtp_pipeline/avtp_avdecc.mk index 9a140c03..1331daf0 100644 --- a/lib/avtp_pipeline/avtp_avdecc.mk +++ b/lib/avtp_pipeline/avtp_avdecc.mk @@ -1,10 +1,12 @@ AVB_FEATURE_AVDECC ?= 1 -PLATFORM_TOOLCHAIN ?= x86_i210_linux +PLATFORM_TOOLCHAIN ?= generic .PHONY: all clean all: build_avdecc/Makefile $(MAKE) -s -C build_avdecc install + mkdir -p build/bin + cp build_avdecc/bin/* build/bin/. doc: build_avdecc/Makefile $(MAKE) -s -C build_avdecc doc @@ -17,7 +19,8 @@ clean: build_avdecc/Makefile: mkdir -p build_avdecc && \ cd build_avdecc && \ - cmake -DCMAKE_TOOLCHAIN_FILE=../platform/Linux/$(PLATFORM_TOOLCHAIN).cmake \ + cmake -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_TOOLCHAIN_FILE=../platform/Linux/$(PLATFORM_TOOLCHAIN).cmake \ -DAVB_FEATURE_AVDECC=$(AVB_FEATURE_AVDECC) \ - .. + .. diff --git a/lib/avtp_pipeline/avtp_pipeline.mk b/lib/avtp_pipeline/avtp_pipeline.mk index a3ed80e8..e08ac9b9 100644 --- a/lib/avtp_pipeline/avtp_pipeline.mk +++ b/lib/avtp_pipeline/avtp_pipeline.mk @@ -1,7 +1,7 @@ AVB_FEATURE_ENDPOINT ?= 1 IGB_LAUNCHTIME_ENABLED ?= 0 -AVB_FEATURE_GSTREAMER ?= 1 -PLATFORM_TOOLCHAIN ?= x86_i210_linux +AVB_FEATURE_GSTREAMER ?= 0 +PLATFORM_TOOLCHAIN ?= generic .PHONY: all clean @@ -19,7 +19,8 @@ clean: build/Makefile: mkdir -p build && \ cd build && \ - cmake -DCMAKE_TOOLCHAIN_FILE=../platform/Linux/$(PLATFORM_TOOLCHAIN).cmake \ + cmake -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_TOOLCHAIN_FILE=../platform/Linux/$(PLATFORM_TOOLCHAIN).cmake \ -DAVB_FEATURE_ENDPOINT=$(AVB_FEATURE_ENDPOINT) \ -DIGB_LAUNCHTIME_ENABLED=$(IGB_LAUNCHTIME_ENABLED) \ -DAVB_FEATURE_GSTREAMER=$(AVB_FEATURE_GSTREAMER) \ -- cgit v1.2.1 From e19b04885a2807e24a1efe169bcc65fc5e8ad38e Mon Sep 17 00:00:00 2001 From: Brant Thomsen Date: Tue, 5 Dec 2017 12:26:27 -0700 Subject: Added AVTP Pipeline Example Added example Talker and Listener .ini files. Added scripts to start and stop the AVTP Pipeline as a Talker & Listener. README updates describing how to use the updated scripts and .ini files. --- lib/avtp_pipeline/README.md | 41 +++-- .../platform/Linux/intf_alsa/example_listener.ini | 135 +++++++++++++++ .../platform/Linux/intf_alsa/example_talker.ini | 181 +++++++++++++++++++++ run_avtp_pipeline.sh | 24 +++ stop_avtp_pipeline.sh | 19 +++ 5 files changed, 386 insertions(+), 14 deletions(-) create mode 100644 lib/avtp_pipeline/platform/Linux/intf_alsa/example_listener.ini create mode 100644 lib/avtp_pipeline/platform/Linux/intf_alsa/example_talker.ini create mode 100755 run_avtp_pipeline.sh create mode 100755 stop_avtp_pipeline.sh diff --git a/lib/avtp_pipeline/README.md b/lib/avtp_pipeline/README.md index cbf89a1a..ac03a856 100644 --- a/lib/avtp_pipeline/README.md +++ b/lib/avtp_pipeline/README.md @@ -63,22 +63,35 @@ The openavb_avdecc binary needs to be run in addition to the AVTP pipeline binar ## Running OpenAvnu daemons - Helper scripts in the repo root. - `$ sudo ./run_igb.sh eth1` - - Load the igb driver. Supply the interface name (ethx) as parameter. Only needed if using IGB support for the Intel i210 adapter. + - Load the igb driver. Supply the interface name (ethx) as parameter. Only needed if using IGB support for the Intel i210 adapter. - `$ sudo ./run_daemons.sh eth1` - - Start gptp, msrp, maap, and shaper daemons. Supply the interface name (ethx) as parameter. + - Start the gptp, msrp, maap, and shaper daemons. Supply the interface name (ethx) as parameter. - Daemons can also be started individually using the run_gptp.sh, run_srp.sh, run_maap.sh, and run_shaper.sh scripts. - -## Running OpenAvnu simple talker example -- `$ sudo ./run_simple_talker.sh eth1` - - Run the current OpenAvnu simple talker example. Supply the interface name (ethx) as parameter. - -## Running OpenAvnu Echo Talker -- `$ sudo ./run_echo_talker.sh eth1` - - Run the AVTP Echo talker test stream. Supply the interface name (ethx) as parameter. - -## Running OpenAvnu Echo Listener -- `$ sudo ./run_echo_listener.sh eth1` - - Run the AVTP Echo talker test stream. Supply the interface name (ethx) as parameter. +- `$ sudo ./stop_daemons.sh` + - Stop the gptp, msrp, maap, and shaper daemons. Don't use this command while AVTP Pipeline is running. + +## Running OpenAvnu AVTP Pipeline example +- `$ sudo ./run_avtp_pipeline.sh eth1` + - Run the current OpenAvnu AVTP Pipeline example. Supply the interface name (ethx) as parameter. +- `$ sudo ./stop_avtp_pipeline.sh eth1` + - Stop the current OpenAvnu AVTP Pipeline example. The script will also attempt to cleanly recover if the AVTP pipeline binaries crashed. + +The AVTP Pipeline example is expected to be run simultaneously on two or more different Linux computers, +with the network interfaces connected using AVB-capable switches. +(The daemons do not currently support using two different network interfaces on the same computer, so different computers must be used.) +You can refer to the list of [Avnu Certified Products](http://avnu.org/certified-products/) for switches with AVB/TSN support. + +To connect the Talker and Listener with the example implementation, you need to use an AVDECC controller. +(These are also referred to as 1722.1 or ATDECC controllers.) +This will tell the Listener(s) which stream to listen to, +and allow the Talker and Listener(s) to coordinate when they should start streaming. +There are several AVDECC controllers available, including one in the OpenAvnu avdecc-lib/controller folder. + +The AVTP Pipeline example Talker and Listener should also be compatible with other AVB/TSN products that support 8-channel, +48K/24-bit [IEC 61883-6](https://webstore.iec.ch/preview/info_iec61883-6%7Bed2.0%7Den.pdf) audio and AVDECC management. +The list of [Avnu Certified Products](http://avnu.org/certified-products/) includes some of them. +The example Talker and Listener has also been used successfully to stream audio to and from Apple Macbooks running macOS version 10.12 (Sierra) and later, +and controlled with the Apple Macbook built-in AVDECC controller (avbutil). ## Benchmark results diff --git a/lib/avtp_pipeline/platform/Linux/intf_alsa/example_listener.ini b/lib/avtp_pipeline/platform/Linux/intf_alsa/example_listener.ini new file mode 100644 index 00000000..7b15b48c --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_alsa/example_listener.ini @@ -0,0 +1,135 @@ +##################################################################### +# General Talker Configuration for ALSA and +# uncompressed 61883-6 audio mapping +##################################################################### + +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = listener + +# initial_state: Specify whether the talker or listener should be +# running or stopped on startup. Valid values are running or stopped. +# If not specified, the default will depend on how the talker or +# listener is launched. +initial_state = stopped + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 01:23:45:67:89:ab + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +#stream_uid = 0 + +# dest_addr: When SRP is being used the destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set in both side the talker and listener. +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# At this time they need to be locally administered and must be in the range +# of 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically :00 for the first stream, :01 for the second, etc. +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +#max_interval_frames = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 50000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +raw_rx_buffers = 200 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +#report_seconds = 1 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +ifname = pcap:eth0 + +# Bit mask used for CPU pinning. Defaults to all cpus can be used (0xffffffff). +#thread_affinity = 12 + +# Enable real time scheduling with this priority. Defaults to not use RT sched (0). +thread_rt_priority = 10 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_uncmp_audio.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapUncmpAudioInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 400 + +# map_nv_tx_rate: Transmit rate. +# This must be set for the uncompressed audio mapping module. +map_nv_tx_rate = 8000 + +# map_nv_packing_factor: Multiple of how many packets of audio frames to place in a media queue item. +# Note: Typically when decreasing the map_nv_tx_rate the packing factor will also be decreased since +# the number of frames per packet will be increasing. +map_nv_packing_factor = 256 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +# intf_fn: The name of the initialize function in the interface. +intf_lib = ./libopenavb_intf_alsa.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfAlsaInitialize + +# intf_nv_ignore_timestamp: If set the listener will ignore the timestamp on media queue items. +# intf_nv_ignore_timestamp = 1 + +# intf_nv_device_name: ALSA device name. Commonly "default" or "plug:dmix" +intf_nv_device_name = default + +# intf_nv_audio_rate: Sampling rate of the audio (samples/second) +intf_nv_audio_rate = 48000 + +# intf_nv_audio_bit_depth: Number of bits per audio sample. Typical values are 16, 24, and 32. +intf_nv_audio_bit_depth = 24 + +# intf_nv_audio_channels: Number of channels of audio. +intf_nv_audio_channels = 8 + +# intf_nv_allow_resampling: 0 = disable software resampling. 1 = allow software resampling. Default is disable. +intf_nv_allow_resampling = 1 + +# intf_nv_start_threshold_periods: The number of period to wait before starting playback. The larger the value to great +# the latency. The small the number the great chance for a buffer underrun. A good range is 1 - 5. +intf_nv_start_threshold_periods = 3 + +# intf_nv_period_time: the number of microseconds which should be set to unify latency between different platforms. +# This influence ALSA's period_time and period_size parameters and the result value should be the nearest possible. +# Initial playback latency is equal intf_nv_start_threshold_periods * intf_nv_period_time. If not set internal defaults are used. +# intf_nv_period_time = 0 + +# Default PC audio is little-endian +intf_nv_audio_endian = little + diff --git a/lib/avtp_pipeline/platform/Linux/intf_alsa/example_talker.ini b/lib/avtp_pipeline/platform/Linux/intf_alsa/example_talker.ini new file mode 100644 index 00000000..d301ea79 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_alsa/example_talker.ini @@ -0,0 +1,181 @@ +##################################################################### +# General Talker Configuration for ALSA and +# uncompressed 61883-6 audio mapping +##################################################################### + +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# initial_state: Specify whether the talker or listener should be +# running or stopped on startup. Valid values are running or stopped. +# If not specified, the default will depend on how the talker or +# listener is launched. +initial_state = stopped + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 01:23:45:67:89:ab + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 0 + +# dest_addr: destination multicast address for the stream. +# +# If using SRP and MAAP, dynamic destination addresses are generated +# automatically by the talker and passed to the listner, and don't +# need to be configured. +# +# Without MAAP, locally administered (static) addresses must be +# configured. Thouse addresses are in the range of: +# 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically use :00 for the first stream, :01 for the second, etc. +# +# When SRP is being used the static destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set (to the same value) in both the talker and listener. +# +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +sr_class = A + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 4000 + +# max_transmit_deficit_usec: Allows setting the maximum packet transmit rate deficit that will +# be recovered when a talker falls behind. This is only used on a talker side. When a talker +# can not keep up with the specified transmit rate it builds up a deficit and will attempt to +# make up for this deficit by sending more packets. There is normally some variability in the +# transmit rate because of other demands on the system so this is expected. However, without this +# bounding value the deficit could grew too large in cases such where more streams are started +# than the system can support and when the number of streams is reduced the remaining streams +# will attempt to recover this deficit by sending packets at a higher rate. This can cause a problem +# at the listener side and significantly delay the recovery time before media playback will return +# to normal. Typically this value can be set to the expected buffer size (in usec) that listeners are +# expected to be buffering. For low latency solutions this is normally a small value. For non-live +# media playback such as video playback the listener side buffers can often be large enough to held many +# seconds of data. +max_transmit_deficit_usec = 50000 + +# internal_latency: Allows manually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 4 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +#report_seconds = 1 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +ifname = pcap:eth0 + +# vlan_id: VLAN Identifier (1-4094). Used in "no endpoint" builds. Defaults to 2. +# vlan_id = 2 + +# Enable fixed timestamping in interface. Defaults to disable (0). +fixed_timestamp = 1 + +# Tx packets to process per wake; for values > 1, traffic shaping must be enabled to evenly space the packets. +#batch_factor = 1 + +# Bit mask used for CPU pinning. Defaults to all cpus can be used (0xffffffff). +#thread_affinity = 12 + +# Enable real time scheduling with this priority. Defaults to not use RT sched (0). +thread_rt_priority = 20 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_uncmp_audio.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapUncmpAudioInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +# map_nv_tx_rate: Transmit rate. +# This must be set for the uncompressed audio mapping module. +map_nv_tx_rate = 8000 + +# map_nv_packing_factor: Multiple of how many packets of audio frames to place in a media queue item. +# Note: Typically when decreasing the map_nv_tx_rate the packing factor will also be decreased since +# the number of frames per packet will be increasing. +map_nv_packing_factor = 1 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +# intf_fn: The name of the initialize function in the interface. +intf_lib = ./libopenavb_intf_alsa.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfAlsaInitialize + +# intf_nv_ignore_timestamp: If set the listener will ignore the timestamp on media queue items. +# intf_nv_ignore_timestamp = 1 + +# intf_nv_device_name: ALSA device name. Commonly "default" or "plug:dmix" +intf_nv_device_name = default + +# intf_nv_audio_rate: Sampling rate of the audio (samples/second) +intf_nv_audio_rate = 48000 + +# intf_nv_audio_bit_depth: Number of bits per audio sample. Typical values are 16, 24, and 32. +intf_nv_audio_bit_depth = 24 + +# intf_nv_audio_channels: Number of channels of audio. +intf_nv_audio_channels = 8 + +# intf_nv_allow_resampling: 0 = disable software resampling. 1 = allow software resampling. Default is disable. +intf_nv_allow_resampling = 1 + +# Default PC audio is little-endian +intf_nv_audio_endian = little + +# Clock skew between media clock and PTP clock in nanoseconds-per-second - adjust this to correct Fixed/Real TS Delta drift +intf_nv_clock_skew_ppb = 0 + diff --git a/run_avtp_pipeline.sh b/run_avtp_pipeline.sh new file mode 100755 index 00000000..8b80f8df --- /dev/null +++ b/run_avtp_pipeline.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Script to start the AVTP Pipeline talker/listener with 8-channel, 48K/24-bit IEC 61883-6 audio. +# For more details, refer to the lib/avtp_pipeline/README.md file. + +if [ "$#" -eq "0" ]; then + echo "Please enter network interface name as parameter. For example:" + echo "sudo $0 eth1" + echo "" + exit -1 +fi + +nic=$1 +echo "Starting AVTP Pipeline on "$nic + +currentdir="$PWD" +scriptdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +cd $scriptdir/lib/avtp_pipeline/build/bin +./openavb_avdecc -I pcap:$nic example_talker.ini example_listener.ini & +sleep 5 +./openavb_host -I pcap:$nic example_talker.ini example_listener.ini & +cd $currentdir + diff --git a/stop_avtp_pipeline.sh b/stop_avtp_pipeline.sh new file mode 100755 index 00000000..81f9ab3c --- /dev/null +++ b/stop_avtp_pipeline.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +killall -2 openavb_host +sleep 1 +killall -2 openavb_avdecc + +# Code below this point is to recover in case one of the applications crashes. + +sleep 5 + +killall -9 openavb_host +killall -9 openavb_avdecc +sleep 1 + +scriptdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +$scriptdir/lib/avtp_pipeline/build/bin/shutdown_openavb_endpoint.sh +$scriptdir/lib/avtp_pipeline/build/bin/shutdown_openavb_avdecc.sh + -- cgit v1.2.1 From 4d6d13bee2a6c9687376bf3cd74d817ea6da57c5 Mon Sep 17 00:00:00 2001 From: Brant Thomsen Date: Tue, 5 Dec 2017 16:02:36 -0700 Subject: Use pushd and popd rather than saving the current directory --- run_avtp_pipeline.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run_avtp_pipeline.sh b/run_avtp_pipeline.sh index 8b80f8df..0a632bb0 100755 --- a/run_avtp_pipeline.sh +++ b/run_avtp_pipeline.sh @@ -13,12 +13,12 @@ fi nic=$1 echo "Starting AVTP Pipeline on "$nic -currentdir="$PWD" scriptdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +pushd . cd $scriptdir/lib/avtp_pipeline/build/bin ./openavb_avdecc -I pcap:$nic example_talker.ini example_listener.ini & sleep 5 ./openavb_host -I pcap:$nic example_talker.ini example_listener.ini & -cd $currentdir +popd -- cgit v1.2.1 From f964f25f1e5932dd6474bf71be8a17744ec529f3 Mon Sep 17 00:00:00 2001 From: Brant Thomsen Date: Thu, 7 Dec 2017 08:16:54 -0700 Subject: Added note about p1722.1 renaming AVDECC to ATDECC --- lib/avtp_pipeline/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/avtp_pipeline/README.md b/lib/avtp_pipeline/README.md index ac03a856..62aba7da 100644 --- a/lib/avtp_pipeline/README.md +++ b/lib/avtp_pipeline/README.md @@ -82,7 +82,7 @@ with the network interfaces connected using AVB-capable switches. You can refer to the list of [Avnu Certified Products](http://avnu.org/certified-products/) for switches with AVB/TSN support. To connect the Talker and Listener with the example implementation, you need to use an AVDECC controller. -(These are also referred to as 1722.1 or ATDECC controllers.) +(These are also referred to as 1722.1 or ATDECC controllers. AVDECC was renamed to ATDECC by the IEEE P1722.1 work group in 2017.) This will tell the Listener(s) which stream to listen to, and allow the Talker and Listener(s) to coordinate when they should start streaming. There are several AVDECC controllers available, including one in the OpenAvnu avdecc-lib/controller folder. -- cgit v1.2.1 From d04ba86e2bb17aa3568df7be1caddd44387c0cbd Mon Sep 17 00:00:00 2001 From: Christopher Hall Date: Wed, 18 Oct 2017 13:23:08 -0700 Subject: Added wireless timestamper and port code buildPTPMessage friend declarations and function definition now use CommonPort object processMessage method in PTP message classes now use CommonPort argument redundant documentation for buildPTPMessage and processMessage removed leaving only that in the base class Add cast to addEventTimerLocked() call (CommonPort::processEvent) preventing Windows compiler warnings Moved all message interval maintenance to CommonPort, also simiplifying Signal message processing Divided FollowUp processing code into two functions for code received on an EtherPort or a WirelessPort (as a Vendor Extension) Modified Windows daemon code to accept a wireless flags Added Windows wireless adapter abstract class to implement a PROSet-like interface for Windows Implemented an Intel specific Windows wireless adapter class tested on 8260 hardware with driver version 19.50.1.6 --- daemons/gptp/README.rst | 29 + daemons/gptp/common/avbts_clock.hpp | 9 + daemons/gptp/common/avbts_message.hpp | 105 ++-- daemons/gptp/common/avbts_osipc.hpp | 1 - daemons/gptp/common/common_port.cpp | 9 +- daemons/gptp/common/common_port.hpp | 97 +++- daemons/gptp/common/ether_port.cpp | 97 ++-- daemons/gptp/common/ether_port.hpp | 38 -- daemons/gptp/common/ieee1588.hpp | 12 - daemons/gptp/common/ptp_message.cpp | 624 ++++++++++++--------- daemons/gptp/common/wireless_port.cpp | 242 ++++++++ daemons/gptp/common/wireless_port.hpp | 179 ++++++ daemons/gptp/common/wireless_tstamper.cpp | 127 +++++ daemons/gptp/common/wireless_tstamper.hpp | 221 ++++++++ daemons/gptp/linux/src/daemon_cl.cpp | 1 + daemons/gptp/windows/daemon_cl/daemon_cl.cpp | 129 +++-- daemons/gptp/windows/daemon_cl/intel_wireless.cpp | 424 ++++++++++++++ daemons/gptp/windows/daemon_cl/intel_wireless.hpp | 187 ++++++ daemons/gptp/windows/daemon_cl/tsc.hpp | 13 +- daemons/gptp/windows/daemon_cl/windows_hal.cpp | 180 +++++- daemons/gptp/windows/daemon_cl/windows_hal.hpp | 114 +++- daemons/gptp/windows/daemon_cl/work_queue.cpp | 106 ++++ daemons/gptp/windows/daemon_cl/work_queue.hpp | 74 +++ .../windows/named_pipe_test/named_pipe_test.cpp | 2 +- 24 files changed, 2564 insertions(+), 456 deletions(-) create mode 100644 daemons/gptp/common/wireless_port.cpp create mode 100644 daemons/gptp/common/wireless_port.hpp create mode 100644 daemons/gptp/common/wireless_tstamper.cpp create mode 100644 daemons/gptp/common/wireless_tstamper.hpp create mode 100644 daemons/gptp/windows/daemon_cl/intel_wireless.cpp create mode 100644 daemons/gptp/windows/daemon_cl/intel_wireless.hpp create mode 100644 daemons/gptp/windows/daemon_cl/work_queue.cpp create mode 100644 daemons/gptp/windows/daemon_cl/work_queue.hpp diff --git a/daemons/gptp/README.rst b/daemons/gptp/README.rst index f56fd719..1e26543d 100644 --- a/daemons/gptp/README.rst +++ b/daemons/gptp/README.rst @@ -94,6 +94,35 @@ To run from the command line: where xx-xx-xx-xx-xx-xx is the mac address of the local interface +Windows Wireless Specific ++++++++++++++++++++++++++ + +Additional Driver/Hardware Requirements: + +* Intel(R) 8260 Adapter + +* Intel(R) PROSet/Wireless Software + + +The wireless software can be downloaded from: + +https://downloadcenter.intel.com/ (Search) + +Running the daemon: + +Currently, the driver only works with peer-to-peer wireless connections. +The connection must be established prior to running the daemon. + +./gptp.exe -w + +Other limitations: + +Some versions of Windows(R) 10 do not allow WinPcap(R) to inject frames and +the BMCA algorithm can't complete. The result is both peers assume the master +role. To fix this, force one peer to be slave with the following command line: + +./gptp.exe -w -R 255 + Other Available PTP Daemons --------------------------- There are a number of existing ptp daemon projects. Some of the other known diff --git a/daemons/gptp/common/avbts_clock.hpp b/daemons/gptp/common/avbts_clock.hpp index feb8d9d1..614464c2 100644 --- a/daemons/gptp/common/avbts_clock.hpp +++ b/daemons/gptp/common/avbts_clock.hpp @@ -550,6 +550,15 @@ public: FrequencyRatio local_system_freq_offset, unsigned sync_count, unsigned pdelay_count, PortState port_state, bool asCapable ); + /** + * @brief Get local:system frequency ratio + * @return clock ratio + */ + FrequencyRatio getLocalSystemFreqOffset() + { + return _local_system_freq_offset; + } + /** * @brief Get the IEEE1588Clock identity value * @return clock identity diff --git a/daemons/gptp/common/avbts_message.hpp b/daemons/gptp/common/avbts_message.hpp index f18d1213..f5ccd9a1 100644 --- a/daemons/gptp/common/avbts_message.hpp +++ b/daemons/gptp/common/avbts_message.hpp @@ -203,6 +203,17 @@ public: } }; +/** + * @brief Builds PTP message from buffer + * @param buf [in] byte buffer containing PTP message + * @param size [in] length of buffer in bytes + * @param remote [in] address from where message was received + * @param port [in] port object that message was recieved on + * @return PTP message object + */ +PTPMessageCommon *buildPTPMessage +( char *buf, int size, LinkLayerAddress *remote, CommonPort *port ); + /** * @brief Provides the PTPMessage common interface used during building of * PTP messages. @@ -373,10 +384,10 @@ protected: /** * @brief Generic interface for processing PTP message - * @param port IEEE1588 port + * @param port CommonPort object * @return void */ - virtual void processMessage( EtherPort *port ); + virtual void processMessage( CommonPort *port ); /** * @brief Builds PTP common header @@ -386,8 +397,7 @@ protected: void buildCommonHeader(uint8_t * buf); friend PTPMessageCommon *buildPTPMessage - ( char *buf, int size, LinkLayerAddress * remote, - EtherPort *port ); + ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port ); }; /*Exact fit. No padding*/ @@ -589,12 +599,7 @@ class PTPMessageAnnounce:public PTPMessageCommon { return ret; } - /** - * @brief Processes PTP message - * @param port EtherPort - * @return void - */ - void processMessage( EtherPort *port ); + void processMessage( CommonPort *port ); /** * @brief Assembles PTPMessageAnnounce message on the @@ -608,8 +613,7 @@ class PTPMessageAnnounce:public PTPMessageCommon { ( CommonPort *port, PortIdentity *destIdentity); friend PTPMessageCommon *buildPTPMessage - ( char *buf, int size, LinkLayerAddress *remote, - EtherPort *port ); + ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port ); }; /** @@ -632,12 +636,7 @@ class PTPMessageSync : public PTPMessageCommon { */ ~PTPMessageSync(); - /** - * @brief Processes PTP messages - * @param port [in] EtherPort - * @return void - */ - void processMessage( EtherPort *port ); + void processMessage( CommonPort *port ); /** * @brief Gets origin timestamp value @@ -659,8 +658,7 @@ class PTPMessageSync : public PTPMessageCommon { (EtherPort *port, PortIdentity *destIdentity ); friend PTPMessageCommon *buildPTPMessage - ( char *buf, int size, LinkLayerAddress * remote, - EtherPort *port ); + ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port ); }; /* Exact fit. No padding*/ @@ -866,7 +864,15 @@ public: /** * @brief Builds the PTPMessageFollowUP object */ - PTPMessageFollowUp( EtherPort *port ); + PTPMessageFollowUp( CommonPort *port ); + + /** + * @brief write followup message into buffer + * @param port [in] associated CommonPort object + * @param buf_ptr [out] buffer to write data to + * @return number of bytes written to buffer + */ + size_t buildMessage(CommonPort *port, uint8_t *buf_ptr); /** * @brief Assembles PTPMessageFollowUp message on the @@ -879,12 +885,16 @@ public: bool sendPort ( EtherPort *port, PortIdentity *destIdentity ); + void processMessage( CommonPort *port ); + /** - * @brief Processes PTP messages - * @param port [in] EtherPort - * @return void - */ - void processMessage( EtherPort *port ); + * @brief Processes PTP messages + * @param port [in] CommonPort + * @param receipt [in] local time message was received + * @param delay + * @return void + */ + void processMessage( CommonPort *port, Timestamp receipt ); /** * @brief Gets the precise origin timestamp value @@ -916,8 +926,7 @@ public: } friend PTPMessageCommon *buildPTPMessage - ( char *buf, int size, LinkLayerAddress *remote, - EtherPort *port ); + ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port ); }; /** @@ -953,12 +962,7 @@ class PTPMessagePathDelayReq : public PTPMessageCommon { bool sendPort ( EtherPort *port, PortIdentity *destIdentity ); - /** - * @brief Processes PTP messages - * @param port [in] EtherPort - * @return void - */ - void processMessage( EtherPort *port ); + void processMessage( CommonPort *port ); /** * @brief Gets origin timestamp value @@ -969,8 +973,7 @@ class PTPMessagePathDelayReq : public PTPMessageCommon { } friend PTPMessageCommon *buildPTPMessage - ( char *buf, int size, LinkLayerAddress *remote, - EtherPort *port ); + ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port ); }; /** @@ -1004,12 +1007,7 @@ public: bool sendPort ( EtherPort *port, PortIdentity *destIdentity ); - /** - * @brief Processes PTP messages - * @param port [in] EtherPort - * @return void - */ - void processMessage( EtherPort *port ); + void processMessage( CommonPort *port ); /** * @brief Sets the request receipt timestamp @@ -1042,8 +1040,7 @@ public: } friend PTPMessageCommon *buildPTPMessage - ( char *buf, int size, LinkLayerAddress *remote, - EtherPort *port ); + ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port ); }; /** @@ -1078,12 +1075,7 @@ public: bool sendPort ( EtherPort *port, PortIdentity *destIdentity ); - /** - * @brief Processes PTP messages - * @param port [in] EtherPort - * @return void - */ - void processMessage( EtherPort *port ); + void processMessage( CommonPort *port ); /** * @brief Sets the response origin timestamp @@ -1116,8 +1108,7 @@ public: } friend PTPMessageCommon *buildPTPMessage - ( char *buf, int size, LinkLayerAddress *remote, - EtherPort *port ); + ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port ); }; /*Exact fit. No padding*/ @@ -1267,16 +1258,10 @@ public: bool sendPort ( EtherPort *port, PortIdentity *destIdentity ); - /** - * @brief Processes PTP messages - * @param port [in] EtherPort - * @return void - */ - void processMessage( EtherPort *port ); + void processMessage( CommonPort *port ); friend PTPMessageCommon *buildPTPMessage - ( char *buf, int size, LinkLayerAddress *remote, - EtherPort * port); + ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port ); }; #endif diff --git a/daemons/gptp/common/avbts_osipc.hpp b/daemons/gptp/common/avbts_osipc.hpp index 00bac5dd..2adb3dbd 100644 --- a/daemons/gptp/common/avbts_osipc.hpp +++ b/daemons/gptp/common/avbts_osipc.hpp @@ -36,7 +36,6 @@ #include #include -#include /**@file*/ diff --git a/daemons/gptp/common/common_port.cpp b/daemons/gptp/common/common_port.cpp index d8ba54f6..268bc62d 100644 --- a/daemons/gptp/common/common_port.cpp +++ b/daemons/gptp/common/common_port.cpp @@ -61,9 +61,11 @@ CommonPort::CommonPort( PortInit_t *portInit ) : port_state = PTP_INITIALIZING; clock->registerPort(this, ifindex); qualified_announce = NULL; + automotive_profile = portInit->automotive_profile; announce_sequence_id = 0; signal_sequence_id = 0; sync_sequence_id = 0; + initialLogPdelayReqInterval = portInit->initialLogPdelayReqInterval; initialLogSyncInterval = portInit->initialLogSyncInterval; log_mean_announce_interval = 0; pdelay_count = 0; @@ -563,7 +565,7 @@ bool CommonPort::processEvent( Event e ) else { clock->addEventTimerLocked(this, ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES, - ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER * pow(2.0, getAnnounceInterval()) * 1000000000.0); + (uint64_t) ( ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER * pow(2.0, getAnnounceInterval()) * 1000000000.0 )); } // Do any media specific initialization @@ -732,6 +734,11 @@ bool CommonPort::adjustClockPhase( int64_t phase_adjust ) return false; } +FrequencyRatio CommonPort::getLocalSystemFreqOffset() +{ + return clock->getLocalSystemFreqOffset(); +} + Timestamp CommonPort::getTxPhyDelay( uint32_t link_speed ) const { if( phy_delay->count( link_speed ) != 0 ) diff --git a/daemons/gptp/common/common_port.hpp b/daemons/gptp/common/common_port.hpp index d5d0adda..4f195836 100644 --- a/daemons/gptp/common/common_port.hpp +++ b/daemons/gptp/common/common_port.hpp @@ -45,6 +45,10 @@ #include +#define SYNC_RECEIPT_TIMEOUT_MULTIPLIER 3 /*!< Sync rcpt timeout multiplier */ +#define ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER 3 /*!< Annc rcpt timeout mult */ +#define LOG2_INTERVAL_INVALID -127 /* Invalid Log base 2 interval value */ + class IEEE1588Clock; /** @@ -214,6 +218,9 @@ typedef struct { /* net_label Network label */ InterfaceLabel *net_label; + /* Virtual Network label (e.g. WiFi Direct network MAC) */ + InterfaceLabel *virtual_label; + /* automotive_profile set the AVnu automotive profile */ bool automotive_profile; @@ -313,6 +320,7 @@ private: PortState port_state; bool testMode; + bool automotive_profile; signed char log_mean_sync_interval; signed char log_mean_announce_interval; @@ -336,6 +344,9 @@ private: * received as slave */ unsigned pdelay_count; + signed char initialLogPdelayReqInterval; + signed char log_min_mean_pdelay_req_interval; + PTPMessageAnnounce *qualified_announce; uint16_t announce_sequence_id; @@ -435,6 +446,13 @@ public: return (abs_delay <= neighbor_prop_delay_thresh); } + /** + * @brief Return frequency offset between local timestamp clock + * system clock + * @return local:system ratio + */ + FrequencyRatio getLocalSystemFreqOffset(); + /** * @brief Gets a pointer to IEEE1588Clock * @return Pointer to clock @@ -1114,6 +1132,63 @@ public: */ virtual void becomeSlave( bool restart_syntonization ) = 0; + /** + * @brief Gets the AVnu automotive profile flag + * @return automotive_profile flag + */ + bool getAutomotiveProfile() { return(automotive_profile); } + + /** + * @brief Sets the pDelay minimum interval + * @param val time interval + * @return none + */ + void setPDelayInterval(signed char val) { + log_min_mean_pdelay_req_interval = val; + } + + /** + * @brief Gets the pDelay minimum interval + * @return PDelay interval + */ + signed char getPDelayInterval(void) { + return log_min_mean_pdelay_req_interval; + } + + /** + * @brief Sets the pDelay minimum interval back to initial + * value + * @return none + */ + void resetInitPDelayInterval(void) { + log_min_mean_pdelay_req_interval = initialLogPdelayReqInterval; + } + + /** + * @brief set initial pdelay interval + * @param interval [in] log base 2 pdelay rate + */ + void setInitPDelayInterval( int8_t interval ) + { + initialLogPdelayReqInterval = interval; + } + + /** + * @brief get initial pdelay interval + * @return log base 2 pdelay rate + */ + int8_t getInitPDelayInterval(void) + { + return initialLogPdelayReqInterval; + } + + /** + * @brief Start pDelay interval timer + * @param waitTime time interval + * @return none + */ + virtual void startPDelayIntervalTimer( unsigned long long waitTime ) {} + /** * @brief Sets current sync count value. * @param cnt [in] sync count value @@ -1336,20 +1411,24 @@ public: */ virtual bool _processEvent( Event e ) = 0; + /** + * @brief Performs media specific setup after start sync is completed + * @return void + */ virtual void syncDone() = 0; /** - * @brief Sends a general message to a port. No timestamps - * @param buf [in] Pointer to the data buffer - * @param len Size of the message - * @param mcast_type Enumeration - * MulticastType (pdelay, none or other). Depracated. - * @param destIdentity Destination port identity - * @return void - */ + * @brief Sends a general message to a port. No timestamps + * @param buf [in] Pointer to the data buffer + * @param len Size of the message + * @param mcast_type Enumeration + * MulticastType (pdelay, none or other). Depracated. + * @param destIdentity Destination port identity + * @return void + */ virtual void sendGeneralPort (uint16_t etherType, uint8_t * buf, int len, MulticastType mcast_type, - PortIdentity * destIdentity) = 0; + PortIdentity * destIdentity) = 0; /** * @brief Sets link speed diff --git a/daemons/gptp/common/ether_port.cpp b/daemons/gptp/common/ether_port.cpp index 2325fb99..e576705c 100644 --- a/daemons/gptp/common/ether_port.cpp +++ b/daemons/gptp/common/ether_port.cpp @@ -85,7 +85,6 @@ EtherPort::~EtherPort() EtherPort::EtherPort( PortInit_t *portInit ) : CommonPort( portInit ) { - automotive_profile = portInit->automotive_profile; linkUp = portInit->linkUp; setTestMode( portInit->testMode ); @@ -98,29 +97,29 @@ EtherPort::EtherPort( PortInit_t *portInit ) : duplicate_resp_counter = 0; last_invalid_seqid = 0; - initialLogPdelayReqInterval = portInit->initialLogPdelayReqInterval; operLogPdelayReqInterval = portInit->operLogPdelayReqInterval; operLogSyncInterval = portInit->operLogSyncInterval; - if (automotive_profile) { + if (getAutomotiveProfile()) + { setAsCapable( true ); if (getInitSyncInterval() == LOG2_INTERVAL_INVALID) setInitSyncInterval( -5 ); // 31.25 ms - if (initialLogPdelayReqInterval == LOG2_INTERVAL_INVALID) - initialLogPdelayReqInterval = 0; // 1 second + if (getInitPDelayInterval() == LOG2_INTERVAL_INVALID) + setInitPDelayInterval( 0 ); // 1 second if (operLogPdelayReqInterval == LOG2_INTERVAL_INVALID) operLogPdelayReqInterval = 0; // 1 second if (operLogSyncInterval == LOG2_INTERVAL_INVALID) operLogSyncInterval = 0; // 1 second - } - else { + } else + { setAsCapable( false ); if ( getInitSyncInterval() == LOG2_INTERVAL_INVALID ) setInitSyncInterval( -3 ); // 125 ms - if (initialLogPdelayReqInterval == LOG2_INTERVAL_INVALID) - initialLogPdelayReqInterval = 0; // 1 second + if (getInitPDelayInterval() == LOG2_INTERVAL_INVALID) + setInitPDelayInterval( 0 ); // 1 second if (operLogPdelayReqInterval == LOG2_INTERVAL_INVALID) operLogPdelayReqInterval = 0; // 1 second if (operLogSyncInterval == LOG2_INTERVAL_INVALID) @@ -128,7 +127,7 @@ EtherPort::EtherPort( PortInit_t *portInit ) : } /*TODO: Add intervals below to a config interface*/ - log_min_mean_pdelay_req_interval = initialLogPdelayReqInterval; + resetInitPDelayInterval(); last_sync = NULL; last_pdelay_req = NULL; @@ -138,7 +137,8 @@ EtherPort::EtherPort( PortInit_t *portInit ) : setPdelayCount(0); setSyncCount(0); - if (automotive_profile) { + if( getAutomotiveProfile( )) + { if (isGM) { avbSyncState = 1; } @@ -169,10 +169,16 @@ bool EtherPort::_init_port( void ) void EtherPort::startPDelay() { if(!pdelayHalted()) { - if (automotive_profile) { - if (log_min_mean_pdelay_req_interval != PTPMessageSignalling::sigMsgInterval_NoSend) { + if( getAutomotiveProfile( )) + { + if( getPDelayInterval() != + PTPMessageSignalling::sigMsgInterval_NoSend) + { long long unsigned int waitTime; - waitTime = ((long long) (pow((double)2, log_min_mean_pdelay_req_interval) * 1000000000.0)); + waitTime = ((long long) + (pow((double)2, + getPDelayInterval()) * + 1000000000.0)); waitTime = waitTime > EVENT_TIMER_GRANULARITY ? waitTime : EVENT_TIMER_GRANULARITY; pdelay_started = true; startPDelayIntervalTimer(waitTime); @@ -194,7 +200,8 @@ void EtherPort::stopPDelay() void EtherPort::startSyncRateIntervalTimer() { - if (automotive_profile) { + if( getAutomotiveProfile( )) + { sync_rate_interval_timer_started = true; if (isGM) { // GM will wait up to 8 seconds for signaling rate @@ -325,7 +332,8 @@ bool EtherPort::_processEvent( Event e ) switch (e) { case POWERUP: case INITIALIZE: - if (!automotive_profile) { + if( !getAutomotiveProfile( )) + { if ( getPortState() != PTP_SLAVE && getPortState() != PTP_MASTER ) { @@ -355,7 +363,8 @@ bool EtherPort::_processEvent( Event e ) port_ready_condition->wait(); - if (automotive_profile) { + if( getAutomotiveProfile( )) + { setStationState(STATION_STATE_ETHERNET_READY); if (getTestMode()) { @@ -387,7 +396,7 @@ bool EtherPort::_processEvent( Event e ) // If the automotive profile is enabled, handle the event by // doing nothing and returning true, preventing the default // action from executing - if( automotive_profile ) + if( getAutomotiveProfile( )) ret = true; else ret = false; @@ -396,7 +405,8 @@ bool EtherPort::_processEvent( Event e ) case LINKUP: haltPdelay(false); startPDelay(); - if (automotive_profile) { + if( getAutomotiveProfile( )) + { GPTP_LOG_EXCEPTION("LINKUP"); } else { @@ -408,11 +418,16 @@ bool EtherPort::_processEvent( Event e ) } else if( getPortState() == PTP_MASTER ) { becomeMaster( true ); } else { - clock->addEventTimerLocked(this, ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES, - ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER * pow(2.0, getAnnounceInterval()) * 1000000000.0); + clock->addEventTimerLocked + ( this, ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES, + (uint64_t) + ( ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER * + pow( 2.0, getAnnounceInterval( )) * + 1000000000.0 )); } - if (automotive_profile) { + if( getAutomotiveProfile( )) + { setAsCapable( true ); setStationState(STATION_STATE_ETHERNET_READY); @@ -427,7 +442,7 @@ bool EtherPort::_processEvent( Event e ) resetInitSyncInterval(); setAnnounceInterval( 0 ); - log_min_mean_pdelay_req_interval = initialLogPdelayReqInterval; + resetInitPDelayInterval(); if (!isGM) { // Send an initial signaling message @@ -468,7 +483,8 @@ bool EtherPort::_processEvent( Event e ) break; case LINKDOWN: stopPDelay(); - if (automotive_profile) { + if( getAutomotiveProfile( )) + { GPTP_LOG_EXCEPTION("LINK DOWN"); } else { @@ -484,7 +500,7 @@ bool EtherPort::_processEvent( Event e ) break; case ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES: case SYNC_RECEIPT_TIMEOUT_EXPIRES: - if( !automotive_profile ) + if( !getAutomotiveProfile( )) { ret = false; break; @@ -568,8 +584,8 @@ bool EtherPort::_processEvent( Event e ) tx_succeed = sync->sendPort(this, NULL); GPTP_LOG_DEBUG("Sent SYNC message"); - if ( automotive_profile && - getPortState() == PTP_MASTER ) + if( getAutomotiveProfile() && + getPortState() == PTP_MASTER ) { if (avbSyncState > 0) { avbSyncState--; @@ -618,7 +634,8 @@ bool EtherPort::_processEvent( Event e ) break; case FAULT_DETECTED: GPTP_LOG_ERROR("Received FAULT_DETECTED event"); - if (!automotive_profile) { + if( !getAutomotiveProfile( )) + { setAsCapable(false); } break; @@ -637,7 +654,8 @@ bool EtherPort::_processEvent( Event e ) pdelay_rx_lock->unlock(); break; case PDELAY_RESP_RECEIPT_TIMEOUT_EXPIRES: - if (!automotive_profile) { + if( !getAutomotiveProfile( )) + { GPTP_LOG_EXCEPTION("PDelay Response Receipt Timeout"); setAsCapable(false); } @@ -668,8 +686,9 @@ bool EtherPort::_processEvent( Event e ) sendSignalMessage = true; } - if (log_min_mean_pdelay_req_interval != operLogPdelayReqInterval) { - log_min_mean_pdelay_req_interval = operLogPdelayReqInterval; + if( getPDelayInterval() != operLogPdelayReqInterval) + { + setPDelayInterval( operLogPdelayReqInterval ); sendSignalMessage = true; } @@ -678,10 +697,10 @@ bool EtherPort::_processEvent( Event e ) // Send operational signalling message PTPMessageSignalling *sigMsg = new PTPMessageSignalling(this); if (sigMsg) { - if (automotive_profile) + if( getAutomotiveProfile( )) sigMsg->setintervals(PTPMessageSignalling::sigMsgInterval_NoChange, getSyncInterval(), PTPMessageSignalling::sigMsgInterval_NoChange); else - sigMsg->setintervals(log_min_mean_pdelay_req_interval, getSyncInterval(), PTPMessageSignalling::sigMsgInterval_NoChange); + sigMsg->setintervals(getPDelayInterval(), getSyncInterval(), PTPMessageSignalling::sigMsgInterval_NoChange); sigMsg->sendPort(this, NULL); delete sigMsg; } @@ -720,7 +739,8 @@ void EtherPort::becomeMaster( bool annc ) { stopSyncReceiptTimer(); if( annc ) { - if (!automotive_profile) { + if( !getAutomotiveProfile( )) + { startAnnounce(); } } @@ -738,7 +758,8 @@ void EtherPort::becomeSlave( bool restart_syntonization ) { setPortState( PTP_SLAVE ); - if (!automotive_profile) { + if( !getAutomotiveProfile( )) + { clock->addEventTimerLocked (this, ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES, (ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER* @@ -832,7 +853,8 @@ void EtherPort::startPDelayIntervalTimer void EtherPort::syncDone() { GPTP_LOG_VERBOSE("Sync complete"); - if (automotive_profile && getPortState() == PTP_SLAVE) { + if( getAutomotiveProfile() && getPortState() == PTP_SLAVE ) + { if (avbSyncState > 0) { avbSyncState--; if (avbSyncState == 0) { @@ -849,7 +871,8 @@ void EtherPort::syncDone() { } } - if (automotive_profile) { + if( getAutomotiveProfile( )) + { if (!sync_rate_interval_timer_started) { if ( getSyncInterval() != operLogSyncInterval ) { diff --git a/daemons/gptp/common/ether_port.hpp b/daemons/gptp/common/ether_port.hpp index 32968dc4..4c878951 100644 --- a/daemons/gptp/common/ether_port.hpp +++ b/daemons/gptp/common/ether_port.hpp @@ -60,10 +60,6 @@ #define TEST_STATUS_MULTICAST 0x011BC50AC000ULL /*!< AVnu Automotive profile test status msg Multicast value */ #define PDELAY_RESP_RECEIPT_TIMEOUT_MULTIPLIER 3 /*!< PDelay timeout multiplier*/ -#define SYNC_RECEIPT_TIMEOUT_MULTIPLIER 3 /*!< Sync receipt timeout multiplier*/ -#define ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER 3 /*!< Announce receipt timeout multiplier*/ - -#define LOG2_INTERVAL_INVALID -127 /* Simple out of range Log base 2 value used for Sync and PDelay msg internvals */ /** * @brief PortType enumeration. Selects between delay request-response (E2E) mechanism @@ -98,7 +94,6 @@ class EtherPort : public CommonPort /* Port Configuration */ signed char log_mean_unicast_sync_interval; signed char log_min_mean_delay_req_interval; - signed char log_min_mean_pdelay_req_interval; unsigned int duplicate_resp_counter; uint16_t last_invalid_seqid; @@ -109,8 +104,6 @@ class EtherPort : public CommonPort // asCapable : already defined as asCapable signed char operLogPdelayReqInterval; signed char operLogSyncInterval; - signed char initialLogPdelayReqInterval; - bool automotive_profile; // Test Status variables uint32_t linkUpCount; @@ -200,12 +193,6 @@ protected: */ void syncDone(); - /** - * @brief Gets the AVnu automotive profile flag - * @return automotive_profile flag - */ - bool getAutomotiveProfile() { return( automotive_profile ); } - /** * @brief Destroys a EtherPort */ @@ -302,31 +289,6 @@ protected: */ void removeForeignMasterAll(void); - /** - * @brief Gets the pDelay minimum interval - * @return PDelay interval - */ - signed char getPDelayInterval(void) { - return log_min_mean_pdelay_req_interval; - } - - /** - * @brief Sets the pDelay minimum interval - * @param val time interval - * @return none - */ - void setPDelayInterval(signed char val) { - log_min_mean_pdelay_req_interval = val; - } - - /** - * @brief Sets the pDelay minimum interval back to initial - * value - * @return none */ - void setInitPDelayInterval(void) { - log_min_mean_pdelay_req_interval = initialLogPdelayReqInterval; - } - /** * @brief Start pDelay interval timer * @param waitTime time interval diff --git a/daemons/gptp/common/ieee1588.hpp b/daemons/gptp/common/ieee1588.hpp index bd95a5fa..cf3d59e4 100644 --- a/daemons/gptp/common/ieee1588.hpp +++ b/daemons/gptp/common/ieee1588.hpp @@ -452,16 +452,4 @@ static inline void TIMESTAMP_ADD_NS( Timestamp &ts, uint64_t ns ) { ts.nanoseconds = (uint32_t)nanos; } -/** - * @brief Builds a PTP message - * @param buf [in] message buffer to send - * @param size message length - * @param remote Destination link layer address - * @param port [in] IEEE1588 port - * @return PTP message instance of PTPMessageCommon - */ -PTPMessageCommon *buildPTPMessage -( char *buf, int size, LinkLayerAddress *remote, - EtherPort *port ); - #endif diff --git a/daemons/gptp/common/ptp_message.cpp b/daemons/gptp/common/ptp_message.cpp index 9219338b..6decc7a3 100644 --- a/daemons/gptp/common/ptp_message.cpp +++ b/daemons/gptp/common/ptp_message.cpp @@ -67,7 +67,7 @@ bool PTPMessageCommon::isSenderEqual(PortIdentity portIdentity) PTPMessageCommon *buildPTPMessage ( char *buf, int size, LinkLayerAddress *remote, - EtherPort *port ) + CommonPort *port ) { OSTimer *timer = port->getTimerFactory()->createTimer(); PTPMessageCommon *msg = NULL; @@ -80,6 +80,7 @@ PTPMessageCommon *buildPTPMessage PortIdentity *sourcePortIdentity; Timestamp timestamp(0, 0, 0); unsigned counter_value = 0; + EtherPort *eport = NULL; #if PTP_DEBUG { @@ -123,8 +124,18 @@ PTPMessageCommon *buildPTPMessage if (!(messageType >> 3)) { int iter = 5; long req = 4000; // = 1 ms + + eport = dynamic_cast ( port ); + if (eport == NULL) + { + GPTP_LOG_ERROR + ( "Received Event Message, but port type " + "doesn't support timestamping\n" ); + goto abort; + } + int ts_good = - port->getRxTimestamp + eport->getRxTimestamp (sourcePortIdentity, messageId, timestamp, counter_value, false); while (ts_good != GPTP_EC_SUCCESS && iter-- != 0) { // Waits at least 1 time slice regardless of size of 'req' @@ -134,7 +145,7 @@ PTPMessageCommon *buildPTPMessage "Error (RX) timestamping RX event packet (Retrying), error=%d", ts_good ); ts_good = - port->getRxTimestamp(sourcePortIdentity, messageId, + eport->getRxTimestamp(sourcePortIdentity, messageId, timestamp, counter_value, iter == 0); req *= 2; @@ -527,7 +538,8 @@ PTPMessageCommon *buildPTPMessage buf + PTP_COMMON_HDR_LOG_MSG_INTRVL(PTP_COMMON_HDR_OFFSET), sizeof(msg->logMeanMessageInterval)); - port->addSockAddrMap(msg->sourcePortIdentity, remote); + if( eport != NULL ) + eport->addSockAddrMap( msg->sourcePortIdentity, remote ); msg->_timestamp = timestamp; msg->_timestamp_counter_value = counter_value; @@ -587,7 +599,7 @@ bool PTPMessageCommon::getTxTimestamp( EtherPort *port, uint32_t link_speed ) return ts_good == GPTP_EC_SUCCESS; } -void PTPMessageCommon::processMessage( EtherPort *port ) +void PTPMessageCommon::processMessage( CommonPort *port ) { _gc = true; return; @@ -838,7 +850,7 @@ bool PTPMessageAnnounce::sendPort return true; } -void PTPMessageAnnounce::processMessage( EtherPort *port ) +void PTPMessageAnnounce::processMessage( CommonPort *port ) { ClockIdentity my_clock_identity; @@ -875,15 +887,25 @@ void PTPMessageAnnounce::processMessage( EtherPort *port ) 1000000000.0))); } -void PTPMessageSync::processMessage( EtherPort *port ) +void PTPMessageSync::processMessage( CommonPort *port ) { + EtherPort *eport = dynamic_cast (port); + PTPMessageSync *old_sync; + + if (eport == NULL) + { + GPTP_LOG_ERROR( "Discarding sync message on wrong port type" ); + _gc = true; + goto done; + } + if (port->getPortState() == PTP_DISABLED ) { - // Do nothing Sync messages should be ignored when in this state + // Do nothing Sync messages should be ignored in this state return; } if (port->getPortState() == PTP_FAULTY) { // According to spec recovery is implementation specific - port->recoverPort(); + eport->recoverPort(); return; } @@ -892,12 +914,12 @@ void PTPMessageSync::processMessage( EtherPort *port ) #if CHECK_ASSIST_BIT if( flags[PTP_ASSIST_BYTE] & (0x1<getLastSync(); + old_sync = eport->getLastSync(); if (old_sync != NULL) { delete old_sync; } - port->setLastSync(this); + eport->setLastSync(this); _gc = false; goto done; #if CHECK_ASSIST_BIT @@ -912,7 +934,7 @@ void PTPMessageSync::processMessage( EtherPort *port ) return; } -PTPMessageFollowUp::PTPMessageFollowUp( EtherPort *port ) : +PTPMessageFollowUp::PTPMessageFollowUp( CommonPort *port ) : PTPMessageCommon( port ) { messageType = FOLLOWUP_MESSAGE; /* This is an event message */ @@ -923,52 +945,65 @@ PTPMessageFollowUp::PTPMessageFollowUp( EtherPort *port ) : return; } -bool PTPMessageFollowUp::sendPort -( EtherPort *port, PortIdentity *destIdentity ) +size_t PTPMessageFollowUp::buildMessage( CommonPort *port, uint8_t *buf_ptr ) { - 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 */ + Copy in common header */ messageLength = - PTP_COMMON_HDR_LENGTH + PTP_FOLLOWUP_LENGTH + sizeof(tlv); + PTP_COMMON_HDR_LENGTH + PTP_FOLLOWUP_LENGTH + sizeof(tlv); + unsigned char tspec_msg_t = 0; + Timestamp preciseOriginTimestamp_BE; + tspec_msg_t |= messageType & 0xF; buildCommonHeader(buf_ptr); preciseOriginTimestamp_BE.seconds_ms = - PLAT_htons(preciseOriginTimestamp.seconds_ms); + PLAT_htons(preciseOriginTimestamp.seconds_ms); preciseOriginTimestamp_BE.seconds_ls = - PLAT_htonl(preciseOriginTimestamp.seconds_ls); + PLAT_htonl(preciseOriginTimestamp.seconds_ls); preciseOriginTimestamp_BE.nanoseconds = - PLAT_htonl(preciseOriginTimestamp.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)); + &(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)); + &(preciseOriginTimestamp_BE.seconds_ls), + sizeof(preciseOriginTimestamp.seconds_ls)); memcpy(buf_ptr + PTP_FOLLOWUP_NSEC(PTP_FOLLOWUP_OFFSET), - &(preciseOriginTimestamp_BE.nanoseconds), - sizeof(preciseOriginTimestamp.nanoseconds)); + &(preciseOriginTimestamp_BE.nanoseconds), + sizeof(preciseOriginTimestamp.nanoseconds)); /*Change time base indicator to Network Order before sending it*/ uint16_t tbi_NO = PLAT_htonl(tlv.getGMTimeBaseIndicator()); tlv.setGMTimeBaseIndicator(tbi_NO); - tlv.toByteString(buf_ptr + PTP_COMMON_HDR_LENGTH + PTP_FOLLOWUP_LENGTH); + tlv.toByteString(buf_ptr + PTP_COMMON_HDR_LENGTH + + PTP_FOLLOWUP_LENGTH); - GPTP_LOG_VERBOSE - ("Follow-Up Time: %u seconds(hi)", preciseOriginTimestamp.seconds_ms); - GPTP_LOG_VERBOSE - ("Follow-Up Time: %u seconds", preciseOriginTimestamp.seconds_ls); - GPTP_LOG_VERBOSE - ("FW-UP Time: %u nanoseconds", preciseOriginTimestamp.nanoseconds); - GPTP_LOG_VERBOSE - ("FW-UP Time: %x seconds", preciseOriginTimestamp.seconds_ls); - GPTP_LOG_VERBOSE - ("FW-UP Time: %x nanoseconds", preciseOriginTimestamp.nanoseconds); + port->incCounter_ieee8021AsPortStatTxFollowUpCount(); + + return PTP_COMMON_HDR_LENGTH + PTP_FOLLOWUP_LENGTH + sizeof(tlv); +} + +bool PTPMessageFollowUp::sendPort +( EtherPort *port, PortIdentity *destIdentity ) +{ + uint8_t buf_t[256]; + uint8_t *buf_ptr = buf_t + port->getPayloadOffset(); + memset(buf_t, 0, 256); + /* Create packet in buf + Copy in common header */ + buildMessage(port, buf_ptr); + + GPTP_LOG_VERBOSE( "Follow-Up Time: %u seconds(hi)", + preciseOriginTimestamp.seconds_ms); + GPTP_LOG_VERBOSE( "Follow-Up Time: %u seconds", + preciseOriginTimestamp.seconds_ls); + GPTP_LOG_VERBOSE( "FW-UP Time: %u nanoseconds", + preciseOriginTimestamp.nanoseconds); + GPTP_LOG_VERBOSE( "FW-UP Time: %x seconds", + preciseOriginTimestamp.seconds_ls); + GPTP_LOG_VERBOSE( "FW-UP Time: %x nanoseconds", + preciseOriginTimestamp.nanoseconds); #ifdef DEBUG GPTP_LOG_VERBOSE("Follow-up Dump:"); for (int i = 0; i < messageLength; ++i) { @@ -976,17 +1011,16 @@ bool PTPMessageFollowUp::sendPort } #endif - port->sendGeneralPort(PTP_ETHERTYPE, buf_t, messageLength, MCAST_OTHER, destIdentity); - - port->incCounter_ieee8021AsPortStatTxFollowUpCount(); + port->sendGeneralPort( PTP_ETHERTYPE, buf_t, messageLength, + MCAST_OTHER, destIdentity ); return true; } -void PTPMessageFollowUp::processMessage( EtherPort *port ) +void PTPMessageFollowUp::processMessage +( CommonPort *port, Timestamp sync_arrival ) { uint64_t delay; - Timestamp sync_arrival; Timestamp system_time(0, 0, 0); Timestamp device_time(0, 0, 0); @@ -1000,166 +1034,201 @@ void PTPMessageFollowUp::processMessage( EtherPort *port ) int32_t scaledLastGmFreqChange = 0; scaledNs scaledLastGmPhaseChange; - GPTP_LOG_DEBUG("Processing a follow-up message"); - - // Expire any SYNC_RECEIPT timers that exist - port->stopSyncReceiptTimer(); - - if (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; - } - port->incCounter_ieee8021AsPortStatRxFollowUpCount(); - PortIdentity sync_id; - PTPMessageSync *sync = port->getLastSync(); - if (sync == NULL) { - GPTP_LOG_ERROR("Received Follow Up but there is no sync message"); - return; - } - sync->getPortIdentity(&sync_id); - - if (sync->getSequenceId() != sequenceId || sync_id != *sourcePortIdentity) - { - unsigned int cnt = 0; - - if( !port->incWrongSeqIDCounter(&cnt) ) - { - port->becomeMaster( true ); - port->setWrongSeqIDCounter(0); - } - GPTP_LOG_ERROR - ("Received Follow Up %d times but cannot find corresponding Sync", cnt); + if (!port->getLinkDelay(&delay)) goto done; - } - if (sync->getTimestamp()._version != port->getTimestampVersion()) - { - GPTP_LOG_ERROR("Received Follow Up but timestamp version indicates Sync is out of date"); - goto done; - } - - sync_arrival = sync->getTimestamp(); - - if( !port->getLinkDelay(&delay) ) { - goto done; - } - - master_local_freq_offset = tlv.getRateOffset(); + master_local_freq_offset = tlv.getRateOffset(); master_local_freq_offset /= 1ULL << 41; master_local_freq_offset += 1.0; master_local_freq_offset /= port->getPeerRateOffset(); correctionField /= 1 << 16; - correction = (int64_t)((delay * master_local_freq_offset) + correctionField ); + correction = (int64_t) + ((delay * master_local_freq_offset) + correctionField); - if( correction > 0 ) - TIMESTAMP_ADD_NS( preciseOriginTimestamp, correction ); - else TIMESTAMP_SUB_NS( preciseOriginTimestamp, -correction ); + if (correction > 0) + TIMESTAMP_ADD_NS(preciseOriginTimestamp, correction); + else TIMESTAMP_SUB_NS(preciseOriginTimestamp, -correction); local_clock_adjustment = - port->getClock()-> - calcMasterLocalClockRateDifference - ( preciseOriginTimestamp, sync_arrival ); + port->getClock()-> + calcMasterLocalClockRateDifference + (preciseOriginTimestamp, sync_arrival); if( local_clock_adjustment == NEGATIVE_TIME_JUMP ) { - GPTP_LOG_VERBOSE("Received Follow Up but preciseOrigintimestamp indicates negative time jump"); + GPTP_LOG_VERBOSE + ( "Received Follow Up but preciseOrigintimestamp " + "indicates negative time jump" ); goto done; } - scalar_offset = TIMESTAMP_TO_NS( sync_arrival ); + scalar_offset = TIMESTAMP_TO_NS( sync_arrival ); scalar_offset -= TIMESTAMP_TO_NS( preciseOriginTimestamp ); - GPTP_LOG_VERBOSE - ("Followup Correction Field: %Ld, Link Delay: %lu", correctionField, - delay); - GPTP_LOG_VERBOSE - ("FollowUp Scalar = %lld", scalar_offset); + GPTP_LOG_VERBOSE( "Followup Correction Field: %lld, Link Delay: %lu", + correctionField, delay ); + GPTP_LOG_VERBOSE( "FollowUp Scalar = %lld", + scalar_offset ); - /* Otherwise synchronize clock with approximate time from Sync message */ + /* Otherwise synchronize clock with approximate Sync time */ uint32_t local_clock, nominal_clock_rate; uint32_t device_sync_time_offset; port->getDeviceTime(system_time, device_time, local_clock, - nominal_clock_rate); - GPTP_LOG_VERBOSE - ( "Device Time = %llu,System Time = %llu", - TIMESTAMP_TO_NS(device_time), TIMESTAMP_TO_NS(system_time)); + nominal_clock_rate); + GPTP_LOG_VERBOSE( "Device Time = %llu,System Time = %llu", + TIMESTAMP_TO_NS( device_time ), + TIMESTAMP_TO_NS( system_time )); /* Adjust local_clock to correspond to sync_arrival */ - device_sync_time_offset = - (uint32_t) (TIMESTAMP_TO_NS(device_time) - TIMESTAMP_TO_NS(sync_arrival)); + device_sync_time_offset = (uint32_t) + ( TIMESTAMP_TO_NS( device_time ) - + TIMESTAMP_TO_NS( sync_arrival )); GPTP_LOG_VERBOSE - ("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); + ( "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); /*Update information on local status structure.*/ - scaledLastGmFreqChange = (int32_t)((1.0/local_clock_adjustment -1.0) * (1ULL << 41)); - scaledLastGmPhaseChange.setLSB( tlv.getRateOffset() ); - port->getClock()->getFUPStatus()->setScaledLastGmFreqChange( scaledLastGmFreqChange ); - port->getClock()->getFUPStatus()->setScaledLastGmPhaseChange( scaledLastGmPhaseChange ); + scaledLastGmFreqChange = (int32_t) + ((1.0 / local_clock_adjustment - 1.0) * (1ULL << 41)); + scaledLastGmPhaseChange.setLSB(tlv.getRateOffset( )); + port->getClock()->getFUPStatus()->setScaledLastGmFreqChange + ( scaledLastGmFreqChange ); + port->getClock()->getFUPStatus()->setScaledLastGmPhaseChange + ( scaledLastGmPhaseChange ); if( port->getPortState() == PTP_SLAVE ) { - /* The sync_count counts the number of sync messages received - that influence the time on the device. Since adjustments are only - made in the PTP_SLAVE state, increment it here */ + /* + * The sync_count counts the number of sync messages received + * that influence the time on the device. Since adjustments are + * only made in the PTP_SLAVE state, increment it here + */ port->incSyncCount(); - /* Do not call calcLocalSystemClockRateDifference it updates state - global to the clock object and if we are master then the network - is transitioning to us not being master but the master process - is still running locally */ - local_system_freq_offset = - port->getClock() + /* + * Do not call calcLocalSystemClockRateDifference it updates + * state global to the clock object and if we are master then + * the network is transitioning to us not being master but + * the master process is still running locally + */ + local_system_freq_offset = port->getClock() ->calcLocalSystemClockRateDifference ( device_time, system_time ); TIMESTAMP_SUB_NS - ( system_time, (uint64_t) - (((FrequencyRatio) device_sync_time_offset)/ - local_system_freq_offset) ); + (system_time, (uint64_t) + (((FrequencyRatio)device_sync_time_offset) / + local_system_freq_offset)); local_system_offset = - TIMESTAMP_TO_NS(system_time) - TIMESTAMP_TO_NS(sync_arrival); + TIMESTAMP_TO_NS( system_time ) - + TIMESTAMP_TO_NS( sync_arrival ); port->getClock()->setMasterOffset - ( port, scalar_offset, sync_arrival, local_clock_adjustment, - local_system_offset, system_time, local_system_freq_offset, - port->getSyncCount(), port->getPdelayCount(), - port->getPortState(), port->getAsCapable() ); + ( port, scalar_offset, sync_arrival, local_clock_adjustment, + local_system_offset, system_time, local_system_freq_offset, + port->getSyncCount(), port->getPdelayCount(), + port->getPortState(), port->getAsCapable( )); + port->syncDone(); // Restart the SYNC_RECEIPT timer port->startSyncReceiptTimer((unsigned long long) - (SYNC_RECEIPT_TIMEOUT_MULTIPLIER * - ((double) pow((double)2, port->getSyncInterval()) * - 1000000000.0))); + (SYNC_RECEIPT_TIMEOUT_MULTIPLIER * + ((double)pow((double)2, port->getSyncInterval()) * + 1000000000.0))); } uint16_t lastGmTimeBaseIndicator; lastGmTimeBaseIndicator = port->getLastGmTimeBaseIndicator(); - if ((lastGmTimeBaseIndicator > 0) && (tlv.getGmTimeBaseIndicator() != lastGmTimeBaseIndicator)) { - GPTP_LOG_EXCEPTION("Sync discontinuity"); + if (( lastGmTimeBaseIndicator > 0 ) && + ( tlv.getGmTimeBaseIndicator( ) != lastGmTimeBaseIndicator )) + { + GPTP_LOG_EXCEPTION( "Sync discontinuity" ); } - port->setLastGmTimeBaseIndicator(tlv.getGmTimeBaseIndicator()); + port->setLastGmTimeBaseIndicator( tlv.getGmTimeBaseIndicator( )); done: _gc = true; - port->setLastSync(NULL); - delete sync; return; } +void PTPMessageFollowUp::processMessage( CommonPort *port ) +{ + Timestamp sync_arrival; + EtherPort *eport = dynamic_cast (port); + if (eport == NULL) + { + GPTP_LOG_ERROR + ( "Discarding followup message on wrong port type" ); + return; + } + + GPTP_LOG_DEBUG("Processing a follow-up message"); + + // Expire any SYNC_RECEIPT timers that exist + port->stopSyncReceiptTimer(); + + if (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 + eport->recoverPort(); + return; + } + + PTPMessageSync *sync = eport->getLastSync(); + { + PortIdentity sync_id; + if( sync == NULL ) + { + GPTP_LOG_ERROR("Received Follow Up but there is no " + "sync message"); + return; + } + sync->getPortIdentity(&sync_id); + + if( sync->getSequenceId() != sequenceId || + sync_id != *sourcePortIdentity ) + { + unsigned int cnt = 0; + + if( !port->incWrongSeqIDCounter( &cnt )) + { + port->becomeMaster( true ); + port->setWrongSeqIDCounter(0); + } + GPTP_LOG_ERROR + ( "Received Follow Up %d times but cannot " + "find corresponding Sync", cnt ); + goto done; + } + } + + if( sync->getTimestamp()._version != port->getTimestampVersion( )) + { + GPTP_LOG_ERROR( "Received Follow Up but timestamp version " + "indicates Sync is out of date" ); + goto done; + } + + sync_arrival = sync->getTimestamp(); + + processMessage(port, sync_arrival); + +done: + eport->setLastSync(NULL); + delete sync; +} + PTPMessagePathDelayReq::PTPMessagePathDelayReq ( EtherPort *port ) : PTPMessageCommon( port ) { @@ -1170,7 +1239,7 @@ PTPMessagePathDelayReq::PTPMessagePathDelayReq return; } -void PTPMessagePathDelayReq::processMessage( EtherPort *port ) +void PTPMessagePathDelayReq::processMessage( CommonPort *port ) { OSTimer *timer = port->getTimerFactory()->createTimer(); PortIdentity resp_fwup_id; @@ -1179,6 +1248,13 @@ void PTPMessagePathDelayReq::processMessage( EtherPort *port ) PortIdentity resp_id; PTPMessagePathDelayRespFollowUp *resp_fwup; + EtherPort *eport = dynamic_cast (port); + if (eport == NULL) + { + GPTP_LOG_ERROR( "Received Pdelay Request on wrong port type" ); + goto done; + } + if (port->getPortState() == PTP_DISABLED) { // Do nothing all messages should be ignored when in this state goto done; @@ -1186,14 +1262,14 @@ void PTPMessagePathDelayReq::processMessage( EtherPort *port ) if (port->getPortState() == PTP_FAULTY) { // According to spec recovery is implementation specific - port->recoverPort(); + eport->recoverPort(); goto done; } port->incCounter_ieee8021AsPortStatRxPdelayRequest(); /* Generate and send message */ - resp = new PTPMessagePathDelayResp(port); + resp = new PTPMessagePathDelayResp(eport); port->getPortIdentity(resp_id); resp->setPortIdentity(&resp_id); resp->setSequenceId(sequenceId); @@ -1211,7 +1287,7 @@ void PTPMessagePathDelayReq::processMessage( EtherPort *port ) resp->setRequestReceiptTimestamp(_timestamp); port->getTxLock(); - resp->sendPort(port, sourcePortIdentity); + resp->sendPort(eport, sourcePortIdentity); GPTP_LOG_DEBUG("*** Sent PDelay Response message"); port->putTxLock(); @@ -1224,7 +1300,7 @@ void PTPMessagePathDelayReq::processMessage( EtherPort *port ) #endif } - resp_fwup = new PTPMessagePathDelayRespFollowUp(port); + resp_fwup = new PTPMessagePathDelayRespFollowUp(eport); port->getPortIdentity(resp_fwup_id); resp_fwup->setPortIdentity(&resp_fwup_id); resp_fwup->setSequenceId(sequenceId); @@ -1248,7 +1324,7 @@ void PTPMessagePathDelayReq::processMessage( EtherPort *port ) GPTP_LOG_VERBOSE("#3 Correction Field: %Ld", turnaround); resp_fwup->setCorrectionField(0); - resp_fwup->sendPort(port, sourcePortIdentity); + resp_fwup->sendPort(eport, sourcePortIdentity); GPTP_LOG_DEBUG("*** Sent PDelay Response FollowUp message"); @@ -1306,21 +1382,29 @@ PTPMessagePathDelayResp::~PTPMessagePathDelayResp() delete requestingPortIdentity; } -void PTPMessagePathDelayResp::processMessage( EtherPort *port ) +void PTPMessagePathDelayResp::processMessage( CommonPort *port ) { + EtherPort *eport = dynamic_cast (port); + if (eport == NULL) + { + GPTP_LOG_ERROR( "Received Pdelay Resp on wrong port type" ); + _gc = true; + return; + } + if (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(); + eport->recoverPort(); return; } port->incCounter_ieee8021AsPortStatRxPdelayResponse(); - if (port->tryPDelayRxLock() != true) { + if (eport->tryPDelayRxLock() != true) { GPTP_LOG_ERROR("Failed to get PDelay RX Lock"); return; } @@ -1330,7 +1414,7 @@ void PTPMessagePathDelayResp::processMessage( EtherPort *port ) uint16_t resp_port_number; uint16_t oldresp_port_number; - PTPMessagePathDelayResp *old_pdelay_resp = port->getLastPDelayResp(); + PTPMessagePathDelayResp *old_pdelay_resp = eport->getLastPDelayResp(); if( old_pdelay_resp == NULL ) { goto bypass_verify_duplicate; } @@ -1349,36 +1433,36 @@ void PTPMessagePathDelayResp::processMessage( EtherPort *port ) { /*If the duplicates are in sequence and from different sources*/ if( (resp_port_number != oldresp_port_number ) && ( - (port->getLastInvalidSeqID() + 1 ) == getSequenceId() || - port->getDuplicateRespCounter() == 0 ) ){ + (eport->getLastInvalidSeqID() + 1 ) == getSequenceId() || + eport->getDuplicateRespCounter() == 0 ) ){ GPTP_LOG_ERROR("Two responses for same Request. seqID %d. First Response Port# %hu. Second Port# %hu. Counter %d", - getSequenceId(), oldresp_port_number, resp_port_number, port->getDuplicateRespCounter()); + getSequenceId(), oldresp_port_number, resp_port_number, eport->getDuplicateRespCounter()); - if( port->incrementDuplicateRespCounter() ) { + if( eport->incrementDuplicateRespCounter() ) { GPTP_LOG_ERROR("Remote misbehaving. Stopping PDelay Requests for 5 minutes."); - port->stopPDelay(); - port->getClock()->addEventTimerLocked + eport->stopPDelay(); + eport->getClock()->addEventTimerLocked (port, PDELAY_RESP_PEER_MISBEHAVING_TIMEOUT_EXPIRES, (int64_t)(300 * 1000000000.0)); } } else { - port->setDuplicateRespCounter(0); + eport->setDuplicateRespCounter(0); } - port->setLastInvalidSeqID(getSequenceId()); + eport->setLastInvalidSeqID(getSequenceId()); } else { - port->setDuplicateRespCounter(0); + eport->setDuplicateRespCounter(0); } bypass_verify_duplicate: - port->setLastPDelayResp(this); + eport->setLastPDelayResp(this); if (old_pdelay_resp != NULL) { delete old_pdelay_resp; } - port->putPDelayRxLock(); + eport->putPDelayRxLock(); _gc = false; return; @@ -1467,40 +1551,41 @@ PTPMessagePathDelayRespFollowUp::~PTPMessagePathDelayRespFollowUp() #define US_PER_SEC 1000000 void PTPMessagePathDelayRespFollowUp::processMessage -( EtherPort *port ) +( CommonPort *port ) { + PTPMessagePathDelayReq *req; + PTPMessagePathDelayResp *resp; + 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); + EtherPort *eport = dynamic_cast (port); + if (eport == NULL) + { + GPTP_LOG_ERROR( "Received Pdelay Response FollowUp on wrong " + "port type" ); + goto abort; + } + if (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(); + eport->recoverPort(); return; } port->incCounter_ieee8021AsPortStatRxPdelayResponseFollowUp(); - if (port->tryPDelayRxLock() != true) + if (eport->tryPDelayRxLock() != true) return; - PTPMessagePathDelayReq *req = port->getLastPDelayReq(); - PTPMessagePathDelayResp *resp = port->getLastPDelayResp(); - - PortIdentity req_id; - PortIdentity resp_id; - PortIdentity fup_sourcePortIdentity; - PortIdentity resp_sourcePortIdentity; - ClockIdentity req_clkId; - ClockIdentity resp_clkId; - - uint16_t resp_port_number; - uint16_t req_port_number; + req = eport->getLastPDelayReq(); + resp = eport->getLastPDelayResp(); if (req == NULL) { /* Shouldn't happen */ @@ -1517,63 +1602,81 @@ void PTPMessagePathDelayRespFollowUp::processMessage goto abort; } - req->getPortIdentity(&req_id); - resp->getRequestingPortIdentity(&resp_id); - req_clkId = req_id.getClockIdentity(); - resp_clkId = resp_id.getClockIdentity(); - resp_id.getPortNumber(&resp_port_number); - requestingPortIdentity->getPortNumber(&req_port_number); - resp->getPortIdentity(&resp_sourcePortIdentity); - getPortIdentity(&fup_sourcePortIdentity); - if( req->getSequenceId() != sequenceId ) { GPTP_LOG_ERROR - (">>> Received PDelay FUP has different seqID than the PDelay request (%d/%d)", - sequenceId, req->getSequenceId() ); + ( "Received PDelay FUP has different seqID than the " + "PDelay request (%d/%d)", + sequenceId, req->getSequenceId() ); goto abort; } - /* - * IEEE 802.1AS, Figure 11-8, subclause 11.2.15.3 - */ - if (resp->getSequenceId() != sequenceId) { - GPTP_LOG_ERROR + { + PortIdentity req_id; + PortIdentity resp_id; + uint16_t resp_port_number; + uint16_t req_port_number; + + ClockIdentity resp_clkId = resp_id.getClockIdentity(); + ClockIdentity req_clkId = req_id.getClockIdentity(); + PortIdentity fup_sourcePortIdentity; + PortIdentity resp_sourcePortIdentity; + + req->getPortIdentity(&req_id); + resp->getRequestingPortIdentity(&resp_id); + + resp_id.getPortNumber(&resp_port_number); + requestingPortIdentity->getPortNumber(&req_port_number); + + resp->getPortIdentity(&resp_sourcePortIdentity); + getPortIdentity(&fup_sourcePortIdentity); + + /* + * IEEE 802.1AS, Figure 11-8, subclause 11.2.15.3 + */ + if (resp->getSequenceId() != sequenceId) { + GPTP_LOG_ERROR ("Received PDelay Response Follow Up but cannot find " - "corresponding response"); - GPTP_LOG_ERROR("%hu, %hu, %hu, %hu", resp->getSequenceId(), - sequenceId, resp_port_number, req_port_number); + "corresponding response"); + GPTP_LOG_ERROR( "%hu, %hu, %hu, %hu", + resp->getSequenceId(), sequenceId, + resp_port_number, req_port_number ); - goto abort; - } + goto abort; + } - /* - * IEEE 802.1AS, Figure 11-8, subclause 11.2.15.3 - */ - if (req_clkId != resp_clkId ) { - GPTP_LOG_ERROR - ("ClockID Resp/Req differs. PDelay Response ClockID: %s PDelay Request ClockID: %s", - req_clkId.getIdentityString().c_str(), resp_clkId.getIdentityString().c_str() ); - goto abort; - } + /* + * IEEE 802.1AS, Figure 11-8, subclause 11.2.15.3 + */ + if (req_clkId != resp_clkId) { + GPTP_LOG_ERROR + ( "ClockID Resp/Req differs. PDelay Response ClockID: " + "%s PDelay Request ClockID: %s", + req_clkId.getIdentityString().c_str(), + resp_clkId.getIdentityString().c_str( )); + goto abort; + } - /* - * IEEE 802.1AS, Figure 11-8, subclause 11.2.15.3 - */ - if ( resp_port_number != req_port_number ) { - GPTP_LOG_ERROR - ("Request port number (%hu) is different from Response port number (%hu)", - resp_port_number, req_port_number); + /* + * IEEE 802.1AS, Figure 11-8, subclause 11.2.15.3 + */ + if (resp_port_number != req_port_number) { + GPTP_LOG_ERROR + ( "Request port number (%hu) is different from " + "Response port number (%hu)", + req_port_number, resp_port_number ); - goto abort; - } + goto abort; + } - /* - * IEEE 802.1AS, Figure 11-8, subclause 11.2.15.3 - */ - if ( fup_sourcePortIdentity != resp_sourcePortIdentity ) { - GPTP_LOG_ERROR("Source port identity from PDelay Response/FUP differ"); + /* + * IEEE 802.1AS, Figure 11-8, subclause 11.2.15.3 + */ + if (fup_sourcePortIdentity != resp_sourcePortIdentity) { + GPTP_LOG_ERROR( "Source port identity from " + "PDelay Response/FUP differ" ); - goto abort; + goto abort; + } } port->getClock()->deleteEventTimerLocked @@ -1589,20 +1692,22 @@ void PTPMessagePathDelayRespFollowUp::processMessage /* Assume that we are a two step clock, otherwise originTimestamp may be used */ request_tx_timestamp = req->getTimestamp(); - if( request_tx_timestamp.nanoseconds == INVALID_TIMESTAMP.nanoseconds ) { + if( request_tx_timestamp.nanoseconds == INVALID_TIMESTAMP.nanoseconds ) + { /* Stop processing the packet */ goto abort; } + if (request_tx_timestamp.nanoseconds == PDELAY_PENDING_TIMESTAMP.nanoseconds) { // Defer processing if( - port->getLastPDelayRespFollowUp() != NULL && - port->getLastPDelayRespFollowUp() != this ) + eport->getLastPDelayRespFollowUp() != NULL && + eport->getLastPDelayRespFollowUp() != this ) { - delete port->getLastPDelayRespFollowUp(); + delete eport->getLastPDelayRespFollowUp(); } - port->setLastPDelayRespFollowUp(this); + eport->setLastPDelayRespFollowUp(this); port->getClock()->addEventTimerLocked (port, PDELAY_DEFERRED_PROCESSING, 1000000); goto defer; @@ -1646,7 +1751,8 @@ void PTPMessagePathDelayRespFollowUp::processMessage if ( port->getPeerRateOffset() > .998 && port->getPeerRateOffset() < 1.002 ) { - turn_around = (int64_t) (turn_around * port->getPeerRateOffset()); + turn_around = (int64_t) + (turn_around * port->getPeerRateOffset()); } GPTP_LOG_VERBOSE @@ -1671,42 +1777,52 @@ void PTPMessagePathDelayRespFollowUp::processMessage FrequencyRatio rate_offset; if( port->getPeerOffset( prev_peer_ts_mine, prev_peer_ts_theirs )) { FrequencyRatio upper_ratio_limit, lower_ratio_limit; - upper_ratio_limit = PPM_OFFSET_TO_RATIO(UPPER_LIMIT_PPM); - lower_ratio_limit = PPM_OFFSET_TO_RATIO(LOWER_LIMIT_PPM); - - 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); + upper_ratio_limit = + PPM_OFFSET_TO_RATIO(UPPER_LIMIT_PPM); + lower_ratio_limit = + PPM_OFFSET_TO_RATIO(LOWER_LIMIT_PPM); + + 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); theirs_elapsed -= port->getLinkDelay(); theirs_elapsed += link_delay < 0 ? 0 : link_delay; - rate_offset = ((FrequencyRatio) mine_elapsed)/theirs_elapsed; + rate_offset = ((FrequencyRatio) mine_elapsed) + / theirs_elapsed; - if( rate_offset < upper_ratio_limit && rate_offset > lower_ratio_limit ) { + if( rate_offset < upper_ratio_limit && + rate_offset > lower_ratio_limit ) port->setPeerRateOffset(rate_offset); - } } } - if( !port->setLinkDelay( link_delay ) ) { - if (!port->getAutomotiveProfile()) { - GPTP_LOG_ERROR("Link delay %ld beyond neighborPropDelayThresh; not AsCapable", link_delay); + if( !port->setLinkDelay( link_delay )) + { + if( !eport->getAutomotiveProfile( )) + { + GPTP_LOG_ERROR( "Link delay %ld beyond " + "neighborPropDelayThresh; " + "not AsCapable", link_delay ); port->setAsCapable( false ); } - } else { - if (!port->getAutomotiveProfile()) { + } else + { + if( !eport->getAutomotiveProfile( )) port->setAsCapable( true ); - } } port->setPeerOffset( request_tx_timestamp, remote_req_rx_timestamp ); abort: delete req; - port->setLastPDelayReq(NULL); + eport->setLastPDelayReq(NULL); delete resp; - port->setLastPDelayResp(NULL); + eport->setLastPDelayResp(NULL); _gc = true; defer: - port->putPDelayRxLock(); + eport->putPDelayRxLock(); return; } @@ -1821,7 +1937,7 @@ bool PTPMessageSignalling::sendPort return true; } -void PTPMessageSignalling::processMessage( EtherPort *port ) +void PTPMessageSignalling::processMessage( CommonPort *port ) { long long unsigned int waitTime; @@ -1834,7 +1950,7 @@ void PTPMessageSignalling::processMessage( EtherPort *port ) char announceInterval = tlv.getAnnounceInterval(); if (linkDelayInterval == PTPMessageSignalling::sigMsgInterval_Initial) { - port->setInitPDelayInterval(); + port->resetInitPDelayInterval(); waitTime = ((long long) (pow((double)2, port->getPDelayInterval()) * 1000000000.0)); waitTime = waitTime > EVENT_TIMER_GRANULARITY ? waitTime : EVENT_TIMER_GRANULARITY; diff --git a/daemons/gptp/common/wireless_port.cpp b/daemons/gptp/common/wireless_port.cpp new file mode 100644 index 00000000..40db5015 --- /dev/null +++ b/daemons/gptp/common/wireless_port.cpp @@ -0,0 +1,242 @@ +/****************************************************************************** + +Copyright (c) 2009-2015, 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 + +WirelessPort::~WirelessPort() +{ + // Intentionally left blank +} + +WirelessPort::WirelessPort( PortInit_t *portInit, LinkLayerAddress peer_addr ) +: CommonPort( portInit ) +{ + this->peer_addr = peer_addr; + + setAsCapable( true ); + if ( getInitSyncInterval() == LOG2_INTERVAL_INVALID ) + setInitSyncInterval (-3 ); // 125 ms + if ( getInitPDelayInterval() == LOG2_INTERVAL_INVALID ) + setInitPDelayInterval( 127 ); // 1 second + + prev_dialog.dialog_token = 0; +} + +bool WirelessPort::_init_port(void) +{ + port_ready_condition = condition_factory->createCondition(); + + return true; +} + +bool WirelessPort::_processEvent( Event e ) +{ + bool ret; + + switch (e) + { + default: + GPTP_LOG_ERROR + ("Unhandled event type in " + "WirelessPort::processEvent(), %d", e); + ret = false; + break; + + case POWERUP: + case INITIALIZE: + { + port_ready_condition->wait_prelock(); + + if ( !linkOpen(_openPort, (void *)this )) + { + GPTP_LOG_ERROR( "Error creating port thread" ); + ret = false; + break; + } + + port_ready_condition->wait(); + + ret = true; + break; + } + + case SYNC_INTERVAL_TIMEOUT_EXPIRES: + { + WirelessTimestamper *timestamper = + dynamic_cast( _hw_timestamper ); + PTPMessageFollowUp *follow_up; + PortIdentity dest_id; + uint16_t seq; + uint8_t buffer[128]; + size_t length; + + memset(buffer, 0, sizeof( buffer )); + + if( prev_dialog.dialog_token != 0 ) + { + follow_up = new PTPMessageFollowUp( this ); + + getPortIdentity(dest_id); + follow_up->setPortIdentity(&dest_id); + follow_up->setSequenceId(prev_dialog.fwup_seq); + follow_up->setPreciseOriginTimestamp + ( prev_dialog.action ); + length = follow_up->buildMessage + (this, buffer + timestamper->getFwUpOffset()); + + delete follow_up; + } + + seq = getNextSyncSequenceId(); + ret = timestamper->requestTimingMeasurement + ( &peer_addr, seq, &prev_dialog, buffer, (int)length ) + == net_succeed; + + ret = true; + break; + } + + case ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES: + case SYNC_RECEIPT_TIMEOUT_EXPIRES: + ret = false; + break; + + case STATE_CHANGE_EVENT: + ret = false; + break; + } + + return ret; +} + +bool WirelessPort::openPort() +{ + port_ready_condition->signal(); + + while (true) + { + uint8_t buf[128]; + LinkLayerAddress remote; + net_result rrecv; + size_t length = sizeof(buf); + uint32_t link_speed; + + if(( rrecv = recv( &remote, buf, length, link_speed )) + == net_succeed ) + { + processMessage + ((char *)buf, (int)length, &remote, link_speed ); + } + else if( rrecv == net_fatal ) + { + GPTP_LOG_ERROR( "read from network interface failed" ); + this->processEvent( FAULT_DETECTED ); + break; + } + } + + return false; +} + +void WirelessPort::sendGeneralPort +(uint16_t etherType, uint8_t * buf, int len, MulticastType mcast_type, + PortIdentity * destIdentity) +{ + net_result rtx = send( &peer_addr, etherType, buf, len, false); + if( rtx != net_succeed ) + GPTP_LOG_ERROR( "sendGeneralPort(): failure" ); + + return; +} + +void WirelessPort::processMessage +(char *buf, int length, LinkLayerAddress *remote, uint32_t link_speed) +{ + GPTP_LOG_VERBOSE( "Processing network buffer" ); + + PTPMessageCommon *msg = + buildPTPMessage( buf, (int)length, remote, this ); + + if( msg == NULL ) + { + GPTP_LOG_ERROR( "Discarding invalid message" ); + return; + } + GPTP_LOG_VERBOSE( "Processing message" ); + + if( !msg->isEvent( )) + { + msg->processMessage( this ); + } else + { + GPTP_LOG_ERROR( "Received event message on port " + "incapable of processing" ); + msg->PTPMessageCommon::processMessage( this ); + } + + if (msg->garbage()) + delete msg; +} + +void WirelessPort::becomeSlave(bool restart_syntonization) +{ + clock->deleteEventTimerLocked(this, ANNOUNCE_INTERVAL_TIMEOUT_EXPIRES); + clock->deleteEventTimerLocked( this, SYNC_INTERVAL_TIMEOUT_EXPIRES ); + + setPortState( PTP_SLAVE ); + + GPTP_LOG_STATUS("Switching to Slave"); + if( restart_syntonization ) clock->newSyntonizationSetPoint(); + + getClock()->updateFUPInfo(); +} + +void WirelessPort::becomeMaster(bool annc) +{ + setPortState( PTP_MASTER ); + // Stop announce receipt timeout timer + clock->deleteEventTimerLocked( this, ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES); + + // Stop sync receipt timeout timer + stopSyncReceiptTimer(); + + if (annc) + startAnnounce(); + + startSyncIntervalTimer( 16000000 ); + GPTP_LOG_STATUS( "Switching to Master" ); + + clock->updateFUPInfo(); +} diff --git a/daemons/gptp/common/wireless_port.hpp b/daemons/gptp/common/wireless_port.hpp new file mode 100644 index 00000000..bfffbd47 --- /dev/null +++ b/daemons/gptp/common/wireless_port.hpp @@ -0,0 +1,179 @@ +/****************************************************************************** + +Copyright (c) 2009-2015, 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 WIRELESS_PORT_HPP +#define WIRELESS_PORT_HPP + +#include +#include + +class WirelessDialog +{ +public: + Timestamp action; + uint64_t action_devclk; + Timestamp ack; + uint64_t ack_devclk; + uint8_t dialog_token; + uint16_t fwup_seq; + + WirelessDialog( uint32_t action, uint32_t ack, uint8_t dialog_token ) + { + this->action_devclk = action; this->ack_devclk = ack; + this->dialog_token = dialog_token; + } + + WirelessDialog() { dialog_token = 0; } + + WirelessDialog & operator=( const WirelessDialog & a ) + { + if (this != &a) + { + this->ack = a.ack; + this->ack_devclk = a.ack_devclk; + this->action = a.action; + this->action_devclk = a.action_devclk; + this->dialog_token = a.dialog_token; + this->fwup_seq = a.fwup_seq; + } + return *this; + } +}; + +class WirelessPort : public CommonPort +{ +private: + OSCondition *port_ready_condition; + LinkLayerAddress peer_addr; + WirelessDialog prev_dialog; + +public: + WirelessPort( PortInit_t *portInit, LinkLayerAddress peer_addr ); + virtual ~WirelessPort(); + + /** + * @brief Media specific port initialization + * @return true on success + */ + bool _init_port( void ); + + /** + * @brief Perform media specific event handling action + * @return true if event is handled without errors + */ + bool _processEvent( Event e ); + + /** + * @brief Process message + * @param buf [in] Pointer to the data buffer + * @param length Size of the message + * @param remote [in] source address of message + * @param link_speed [in] for receive operation + * @return void + */ + void processMessage + (char *buf, int length, LinkLayerAddress *remote, uint32_t link_speed); + + /** + * @brief Sends a general message to a port. No timestamps + * @param buf [in] Pointer to the data buffer + * @param len Size of the message + * @param mcast_type Enumeration + * MulticastType (pdelay, none or other). Depracated. + * @param destIdentity Destination port identity + * @return void + */ + void sendGeneralPort + ( uint16_t etherType, uint8_t * buf, int len, MulticastType mcast_type, + PortIdentity * destIdentity ); + + /** + * @brief Nothing required for wireless port + */ + void syncDone() {} + + /** + * @brief Switches port to a gPTP master + * @param annc If TRUE, starts announce event timer. + * @return void + */ + void becomeMaster( bool annc ); + + /** + * @brief Switches port to a gPTP slave. + * @param restart_syntonization if TRUE, restarts the syntonization + * @return void + */ + void becomeSlave( bool restart_syntonization ); + + /** + * @brief Receives messages from the network interface + * @return Its an infinite loop. Returns false in case of error. + */ + bool openPort(); + + /** + * @brief Wraps open port method for argument to thread + * @param larg pointer to WirelessPort object + * @return thread exit code + */ + static OSThreadExitCode _openPort( void *larg ) + { + WirelessPort *port = (decltype(port))larg; + + if (!port->openPort()) + return osthread_error; + + return osthread_ok; + } + + /** + * @brief Sets previous dialog + * @param dialog new value of prev_dialog + */ + void setPrevDialog( WirelessDialog *dialog ) + { + prev_dialog = *dialog; + } + + /** + * @brief Sets previous dialog + * @return reference to prev_dialog + */ + WirelessDialog *getPrevDialog( void ) + { + return &prev_dialog; + } +}; + +#endif/*WIRELESS_PORT_HPP*/ diff --git a/daemons/gptp/common/wireless_tstamper.cpp b/daemons/gptp/common/wireless_tstamper.cpp new file mode 100644 index 00000000..10c3be06 --- /dev/null +++ b/daemons/gptp/common/wireless_tstamper.cpp @@ -0,0 +1,127 @@ +/****************************************************************************** + +Copyright (c) 2009-2015, 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 + +net_result WirelessTimestamper::requestTimingMeasurement +( LinkLayerAddress *dest, uint16_t seq, WirelessDialog *prev_dialog, + uint8_t *follow_up, int followup_length ) +{ + WirelessDialog next_dialog; + net_result retval; + TIMINGMSMT_REQUEST *req; + + // Valid dialog token > 0 && < 256 + next_dialog.dialog_token = (seq % MAX_DIALOG_TOKEN) + 1; + next_dialog.fwup_seq = seq; + next_dialog.action_devclk = 0; + req = (TIMINGMSMT_REQUEST *)follow_up; + + // File in request + req->DialogToken = next_dialog.dialog_token; + req->FollowUpDialogToken = prev_dialog->dialog_token; + req->Category = 0x0; + req->Action = 0x0; + req->WiFiVSpecHdr.ElementId = FWUP_VDEF_TAG; + req->WiFiVSpecHdr.Length = 0; + if( req->FollowUpDialogToken != 0 && prev_dialog->action_devclk != 0 ) + { + req->T1 = (uint32_t)prev_dialog->action_devclk; + req->T4 = (uint32_t)prev_dialog->ack_devclk; + req->WiFiVSpecHdr.Length = followup_length + + (uint8_t)sizeof(FwUpLabel); // 1 byte type + req->PtpSpec.FwUpLabel = FwUpLabel; + } + dest->toOctetArray(req->PeerMACAddress); + + retval = _requestTimingMeasurement( req ); + port->setPrevDialog(&next_dialog); + + return retval; +} + +void WirelessTimestamper::timingMeasurementConfirmCB +( LinkLayerAddress addr, WirelessDialog *dialog ) +{ + WirelessDialog *prev_dialog = port->getPrevDialog(); + + // Translate action dev clock to Timestamp + // ACK timestamp isn't needed + dialog->action.set64(dialog->action_devclk*10); + + if (dialog->dialog_token == prev_dialog->dialog_token) + { + dialog->fwup_seq = prev_dialog->fwup_seq; + port->setPrevDialog(dialog); + } +} + +void WirelessTimestamper::timeMeasurementIndicationCB +( LinkLayerAddress addr, WirelessDialog *current, WirelessDialog *previous, + uint8_t *buf, size_t buflen ) +{ + uint64_t link_delay; + WirelessDialog *prev_local; + PTPMessageCommon *msg; + PTPMessageFollowUp *fwup; + // Translate devclk scalar to Timestamp + + msg = buildPTPMessage((char *)buf, (int)buflen, &addr, port); + fwup = dynamic_cast (msg); + current->action_devclk *= 10; + current->ack_devclk *= 10; + current->action.set64(current->action_devclk); + prev_local = port->getPrevDialog(); + if ( previous->dialog_token == prev_local->dialog_token && + fwup != NULL && previous->action_devclk != 0) + { + previous->action_devclk *= 10; + previous->ack_devclk *= 10; + unsigned round_trip = (unsigned) + (previous->ack_devclk - previous->action_devclk); + unsigned turn_around = (unsigned) + (prev_local->ack_devclk - prev_local->action_devclk); + link_delay = (round_trip - turn_around) / 2; + port->setLinkDelay(link_delay); + GPTP_LOG_VERBOSE( "Link Delay = %llu(RT=%u,TA=%u," + "T4=%llu,T1=%llu,DT=%hhu)", + link_delay, round_trip, turn_around, + previous->ack_devclk, + previous->action_devclk, + previous->dialog_token); + fwup->processMessage(port, prev_local->action); + } + + port->setPrevDialog(current); +} diff --git a/daemons/gptp/common/wireless_tstamper.hpp b/daemons/gptp/common/wireless_tstamper.hpp new file mode 100644 index 00000000..79413d0d --- /dev/null +++ b/daemons/gptp/common/wireless_tstamper.hpp @@ -0,0 +1,221 @@ +/****************************************************************************** + +Copyright (c) 2009-2015, 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 WIRELESS_TSTAMPER_HPP +#define WIRELESS_TSTAMPER_HPP + +#include +#include + +#define MAX_DIALOG_TOKEN 255 +#define OUI_8021AS_OCTETS { 0x00, 0x80, 0xC2 } +static const uint8_t OUI_8021AS[] = OUI_8021AS_OCTETS; +#define FWUP_TYPE 0 +#define FWUP_VDEF_TAG 0xDD /* Vendor Defined Tag */ + +typedef enum _WIRELESS_EVENT_TYPE +{ + TIMINGMSMT_EVENT = 0, + TIMINGMSMT_CONFIRM_EVENT, + TIMINGMSMT_CORRELATEDTIME_EVENT, +} WIRELESS_EVENT_TYPE; + +#pragma pack(push, 1) +typedef struct +{ + uint8_t oui[sizeof(OUI_8021AS)]; + uint8_t type; +} FwUpLabel_t; + +typedef struct _PTP_SPEC +{ + FwUpLabel_t FwUpLabel; + uint8_t fwup_data[1]; +} PTP_SPEC; + +static const FwUpLabel_t FwUpLabel = { OUI_8021AS_OCTETS, FWUP_TYPE }; + +typedef struct _WIFI_VENDOR_SPEC_HDR +{ + uint8_t ElementId; + uint8_t Length; +} WIFI_VENDOR_SPEC_HDR; +#pragma pack(pop) + +typedef struct _TIMINGMSMT_REQUEST +{ + uint8_t PeerMACAddress[ETHER_ADDR_OCTETS]; + uint8_t Category; + uint8_t Action; + uint8_t DialogToken; + uint8_t FollowUpDialogToken; + uint32_t T1; + uint32_t T4; + uint8_t MaxT1Error; + uint8_t MaxT4Error; + + WIFI_VENDOR_SPEC_HDR WiFiVSpecHdr; + PTP_SPEC PtpSpec; +} TIMINGMSMT_REQUEST; + +typedef struct _TIMINGMSMT_EVENT_DATA +{ + uint8_t PeerMACAddress[ETHER_ADDR_OCTETS]; + uint32_t DialogToken; + uint32_t FollowUpDialogToken; + uint64_t T1; + uint32_t MaxT1Error; + uint64_t T4; + uint32_t MaxT4Error; + uint64_t T2; + uint32_t MaxT2Error; + uint64_t T3; + uint32_t MaxT3Error; + + WIFI_VENDOR_SPEC_HDR WiFiVSpecHdr; + PTP_SPEC PtpSpec; +} TIMINGMSMT_EVENT_DATA; + +typedef struct _TIMINGMSMT_CONFIRM_EVENT_DATA +{ + uint8_t PeerMACAddress[ETHER_ADDR_OCTETS]; + uint32_t DialogToken; + uint64_t T1; + uint32_t MaxT1Error; + uint64_t T4; + uint32_t MaxT4Error; +} TIMINGMSMT_CONFIRM_EVENT_DATA; + +typedef struct _WIRELESS_CORRELATEDTIME +{ + uint64_t TSC; + uint64_t LocalClk; +} WIRELESS_CORRELATEDTIME; + +struct S8021AS_Indication { + TIMINGMSMT_EVENT_DATA indication; + uint8_t followup[75]; // (34 header + 10 followup + 32 TLV) - 1 byte contained in indication struct = 75 +}; + +union TimeSyncEventData +{ + TIMINGMSMT_CONFIRM_EVENT_DATA confirm; + S8021AS_Indication indication; + WIRELESS_CORRELATEDTIME ptm_wa; +}; + +class WirelessTimestamper : public CommonTimestamper +{ +private: + WirelessPort *port; +public: + virtual ~WirelessTimestamper() {} + + /** + * @brief attach timestamper to port + * @param port port to attach + */ + void setPort( WirelessPort *port ) + { + this->port = port; + } + + /** + * @brief return reference to attached port + * @return reference to attached port + */ + WirelessPort *getPort(void) const + { + return port; + } + + /** + * @brief Return buffer offset where followup message should be placed + * @return byte offset + */ + uint8_t getFwUpOffset(void) + { + // Subtract 1 to compensate for 'bogus' vendor specific buffer + // length + return (uint8_t)((size_t) &((TIMINGMSMT_REQUEST *) 0)-> + PtpSpec.fwup_data ); + } + + /** + * @brief Request transmission of TM frame + * @param dest [in] MAC destination the frame should be sent to + * @param seq [in] 802.1AS sequence number + * @param prev_dialog [in] last dialog message + * @param follow_up [in] buffer containing followup message + * @param followup_length [in] fw-up message length in bytes + */ + virtual net_result requestTimingMeasurement + ( LinkLayerAddress *dest, uint16_t seq, WirelessDialog *prev_dialog, + uint8_t *follow_up, int followup_length ); + + /** + * @brief abstract method for driver/os specific TM transmit code + * @param timingmsmt_req fully formed TM message + */ + virtual net_result _requestTimingMeasurement + ( TIMINGMSMT_REQUEST *timingmsmt_req ) = 0; + + /** + * @brief Asynchronous completion of TM transmit + * @param addr [in] MAC the message was transmitted to + * @param dialog [in] dialog filled with T1, T4 timestamps + */ + void timingMeasurementConfirmCB( LinkLayerAddress addr, + WirelessDialog *dialog ); + + /** + * @brief Reception of TM frame + * @param addr [in] MAC the message was received from + * @param current [in] dialog filled with T2, T3 timestamps + * @param previous [in] dialog filled with T1, T4 timestamps + * @param buf [in] buffer containing followup message + * @param previous [in] length of followup message + */ + void timeMeasurementIndicationCB + ( LinkLayerAddress addr, WirelessDialog *current, + WirelessDialog *previous, uint8_t *buf, size_t buflen ); +}; + +struct WirelessTimestamperCallbackArg +{ + WIRELESS_EVENT_TYPE iEvent_type; + WirelessTimestamper *timestamper; + TimeSyncEventData event_data; +}; + +#endif/*WIRELESS_TSTAMPER_HPP*/ diff --git a/daemons/gptp/linux/src/daemon_cl.cpp b/daemons/gptp/linux/src/daemon_cl.cpp index 86806783..3a6d95e5 100644 --- a/daemons/gptp/linux/src/daemon_cl.cpp +++ b/daemons/gptp/linux/src/daemon_cl.cpp @@ -37,6 +37,7 @@ #include "avbts_oslock.hpp" #include "avbts_persist.hpp" #include "gptp_cfg.hpp" +#include "ether_port.hpp" #ifdef ARCH_INTELCE #include "linux_hal_intelce.hpp" diff --git a/daemons/gptp/windows/daemon_cl/daemon_cl.cpp b/daemons/gptp/windows/daemon_cl/daemon_cl.cpp index 5bb2f3c5..8da3ddfa 100644 --- a/daemons/gptp/windows/daemon_cl/daemon_cl.cpp +++ b/daemons/gptp/windows/daemon_cl/daemon_cl.cpp @@ -44,6 +44,10 @@ POSSIBILITY OF SUCH DAMAGE. #include #include +#include +#include +#include + /* Generic PCH delays */ #define PHY_DELAY_GB_TX_PCH 7750 //1G delay #define PHY_DELAY_GB_RX_PCH 7750 //1G delay @@ -125,9 +129,11 @@ int _tmain(int argc, _TCHAR* argv[]) CommonPort::NEIGHBOR_PROP_DELAY_THRESH; bool syntonize = false; + bool wireless = false; uint8_t priority1 = 248; int i; int phy_delays[4] = { -1, -1, -1, -1 }; + uint8_t addr_ostr[ETHER_ADDR_OCTETS]; // Register default network interface WindowsPCAPNetworkInterfaceFactory *default_factory = new WindowsPCAPNetworkInterfaceFactory(); @@ -140,6 +146,8 @@ int _tmain(int argc, _TCHAR* argv[]) portInit.condition_factory = new WindowsConditionFactory(); WindowsNamedPipeIPC *ipc = new WindowsNamedPipeIPC(); WindowsTimerQueueFactory *timerq_factory = new WindowsTimerQueueFactory(); + CommonPort *port; + WindowsWirelessAdapter *wl_adapter = NULL; if( !ipc->init() ) { delete ipc; @@ -155,49 +163,106 @@ int _tmain(int argc, _TCHAR* argv[]) /* Process optional arguments */ for( i = 1; i < argc; ++i ) { - if( ispunct(argv[i][0]) ) { - if( toupper( argv[i][1] ) == 'H' ) { - print_usage( argv[0] ); + if (ispunct(argv[i][0])) + { + if (toupper(argv[i][1]) == 'H') { + print_usage(argv[0]); return -1; } - else if( toupper( argv[i][1] ) == 'R' ) { - if( i+1 >= argc ) { - printf( "Priority 1 value must be specified on " - "command line, using default value\n" ); - } else { - unsigned long tmp = strtoul( argv[i+1], NULL, 0 ); ++i; - if( tmp > 254 ) { - printf( "Invalid priority 1 value, using " - "default value\n" ); - } else { - priority1 = (uint8_t) tmp; + if (toupper(argv[i][1]) == 'W') + { + wireless = true; + } + else if (toupper(argv[i][1]) == 'R') { + if (i + 1 >= argc) { + printf("Priority 1 value must be specified on " + "command line, using default value\n"); + } + else { + unsigned long tmp = strtoul(argv[i + 1], NULL, 0); ++i; + if (tmp > 255) { + printf("Invalid priority 1 value, using " + "default value\n"); + } + else { + priority1 = (uint8_t)tmp; } } } + } else + { + break; } } - // the last argument is supposed to be a MAC address, so decrement argv index to read it - i--; + // Parse local HW MAC address + if (i < argc) + { + parseMacAddr(argv[i++], addr_ostr); + portInit.net_label = new LinkLayerAddress(addr_ostr); + } else + { + printf("Local hardware MAC address required"); + return -1; + } + + if( wireless ) + { + if (i < argc) + { + parseMacAddr(argv[i++], addr_ostr); + portInit.virtual_label = new LinkLayerAddress(addr_ostr); + } else + { + printf("Wireless operation requires local virtual MAC address"); + return -1; + } + } + + if (!wireless) + { + // Create HWTimestamper object + portInit.timestamper = new WindowsEtherTimestamper(); + } else + { + portInit.timestamper = new WindowsWirelessTimestamper(); + (static_cast (portInit.timestamper))->setAdapter(new IntelWirelessAdapter()); + } - // Create Low level network interface object - uint8_t local_addr_ostr[ETHER_ADDR_OCTETS]; - parseMacAddr( argv[i], local_addr_ostr ); - LinkLayerAddress local_addr(local_addr_ostr); - portInit.net_label = &local_addr; - // Create HWTimestamper object - portInit.timestamper = new WindowsTimestamper(); // Create Clock object - portInit.clock = new IEEE1588Clock( false, false, priority1, timerq_factory, ipc, portInit.lock_factory ); // Do not force slave - // Create Port Object linked to clock and low level - portInit.phy_delay = ðer_phy_delay; - EtherPort *port = new EtherPort( &portInit ); - port->setLinkSpeed(findLinkSpeed(&local_addr)); - if ( !port->init_port() ) { - printf( "Failed to initialize port\n" ); - return -1; + portInit.clock = new IEEE1588Clock(false, false, priority1, timerq_factory, ipc, portInit.lock_factory); // Do not force slave + + if (!wireless) + { + // Create Port Object linked to clock and low level + portInit.phy_delay = ðer_phy_delay; + EtherPort *eport = new EtherPort(&portInit); + eport->setLinkSpeed( findLinkSpeed( static_cast ( portInit.net_label ))); + port = eport; + if (!eport->init_port()) { + printf("Failed to initialize port\n"); + return -1; + } + port->processEvent(POWERUP); + } else + { + if (i < argc) + { + parseMacAddr(argv[i++], addr_ostr); + LinkLayerAddress peer_addr(addr_ostr); + port = new WirelessPort(&portInit, peer_addr); + (static_cast (portInit.timestamper))->setPort( static_cast ( port )); + if (!port->init_port()) { + printf("Failed to initialize port\n"); + return -1; + } + port->processEvent(POWERUP); + } else + { + printf("Wireless operation requires remote MAC address"); + return -1; + } } - port->processEvent( POWERUP ); // Wait for Ctrl-C if( !SetConsoleCtrlHandler( ctrl_handler, true )) { diff --git a/daemons/gptp/windows/daemon_cl/intel_wireless.cpp b/daemons/gptp/windows/daemon_cl/intel_wireless.cpp new file mode 100644 index 00000000..a6f8f99f --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/intel_wireless.cpp @@ -0,0 +1,424 @@ +/****************************************************************************** + +Copyright (c) 2009-2015, 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 + +#define INTEL_EVENT_OFFSET 0x1141 +#define GP2_ROLLOVER 4294967296ULL + +GetAdapterList_t GetAdapterList; +WifiCmdTimingMeasurementRequest_t WifiCmdTimingMeasurementRequest; +WifiCmdTimingPtmWa_t WifiCmdTimingPtmWa; +RegisterIntelCallback_t RegisterIntelCallback; +DeregisterIntelCallback_t DeregisterIntelCallback; + +struct TimestamperContext +{ + WindowsWorkQueue work_queue; + WindowsWirelessTimestamper *timestamper; + ~TimestamperContext() + { + work_queue.stop(); + } +}; + +typedef std::map< LinkLayerAddress, TimestamperContext > TimestamperContextMap; + +class LockedTimestamperContextMap +{ +public: + LockedTimestamperContextMap() + { + InitializeSRWLock(&lock); + } + TimestamperContextMap map; + SRWLOCK lock; +}; + +LockedTimestamperContextMap timestamperContextMap; +HANDLE IntelTimestamperThread; +bool IntelTimestamperThreadStop = false; + +void IntelWirelessTimestamperEventHandler( INTEL_EVENT iEvent, void *pContext ) +{ + WindowsWirelessTimestamper *timestamper; + IntelWirelessAdapter *adapter; + LockedTimestamperContextMap *timestamperContextMap = + (LockedTimestamperContextMap *)pContext; + WirelessTimestamperCallbackArg *arg = + new WirelessTimestamperCallbackArg(); + int vendor_extension_copy_length; + bool error = false; + bool submit = false; // If true submit the event for later processing + + // We share driver callback with other events, subtract the offset for + // timing measurement related events and check this is time sync event + iEvent.eType = + (WIRELESS_EVENT_TYPE) (iEvent.eType - INTEL_EVENT_OFFSET); + switch( iEvent.eType ) + { + default: + return; + case TIMINGMSMT_CONFIRM_EVENT: + case TIMINGMSMT_EVENT: + case TIMINGMSMT_CORRELATEDTIME_EVENT: + break; + } + + LinkLayerAddress event_source( iEvent.BtMacAddress ); + arg->iEvent_type = (WIRELESS_EVENT_TYPE) iEvent.eType; + AcquireSRWLockShared( ×tamperContextMap->lock ); + if( timestamperContextMap->map.find( event_source ) + != timestamperContextMap->map.cend()) + { + arg->timestamper = + timestamperContextMap->map[event_source].timestamper; + } else + { + goto bail; + } + + timestamper = dynamic_cast + (arg->timestamper); + if( timestamper == NULL ) + { + GPTP_LOG_ERROR( "Unexpected timestamper type encountered" ); + goto bail; + } + adapter = dynamic_cast + (timestamper->getAdapter( )); + if( adapter == NULL ) + { + GPTP_LOG_ERROR( "Unexpected adapter type encountered" ); + goto bail; + } + + // The driver implementation makes no guarantee of the lifetime of the + // iEvent object we make a copy and delete the copy when processing is + // complete + switch( arg->iEvent_type ) + { + case TIMINGMSMT_CONFIRM_EVENT: + arg->event_data.confirm = + *(TIMINGMSMT_CONFIRM_EVENT_DATA *)iEvent.data; + arg->event_data.confirm.T1 = + adapter->calc_rollover( arg->event_data.confirm.T1 ); + arg->event_data.confirm.T4 = + adapter->calc_rollover( arg->event_data.confirm.T4 ); + submit = true; + + break; + + case TIMINGMSMT_EVENT: + arg->event_data.indication.indication = + *(TIMINGMSMT_EVENT_DATA *)iEvent.data; + arg->event_data.indication.indication.T2 = + adapter->calc_rollover + ( arg->event_data.indication.indication.T2/100 ); + arg->event_data.indication.indication.T3 = + adapter->calc_rollover + ( arg->event_data.indication.indication.T3/100 ); + // Calculate copy length, at most followup message + // (See S8021AS indication) + vendor_extension_copy_length = arg->event_data. + indication.indication.WiFiVSpecHdr.Length - 1; + vendor_extension_copy_length -= vendor_extension_copy_length - + sizeof(arg->event_data.indication.followup) > 0 ? + vendor_extension_copy_length - + sizeof(arg->event_data.indication.followup) : 0; + // Copy the rest of the vendor specific field + memcpy( arg->event_data.indication.followup, + ((BYTE *)iEvent.data) + + sizeof( arg->event_data.indication.indication ), + vendor_extension_copy_length ); + submit = true; + break; + case TIMINGMSMT_CORRELATEDTIME_EVENT: + AcquireSRWLockExclusive(&adapter->ct_lock); + if (adapter->ct_miss > 0) + { + --adapter->ct_miss; + } else + { + arg->event_data.ptm_wa = + *(WIRELESS_CORRELATEDTIME *)iEvent.data; + arg->event_data.ptm_wa.LocalClk = + adapter->calc_rollover + (arg->event_data.ptm_wa.LocalClk); + adapter->ct_done = true; + // No need to schedule this, it returns immediately + WirelessTimestamperCallback(arg); + WakeAllConditionVariable(&adapter->ct_cond); + } + ReleaseSRWLockExclusive(&adapter->ct_lock); + + break; + } + + if (submit && + !timestamperContextMap->map[event_source].work_queue.submit + ( WirelessTimestamperCallback, arg )) + GPTP_LOG_ERROR("Failed to submit WiFi event"); +bail: + ReleaseSRWLockShared(×tamperContextMap->lock); +} + +DWORD WINAPI IntelWirelessLoop(LPVOID arg) +{ + // Register for callback + INTEL_CALLBACK tempCallback; + tempCallback.fnIntelCallback = IntelWirelessTimestamperEventHandler; + tempCallback.pContext = arg; + + if (RegisterIntelCallback(&tempCallback) != S_OK) { + GPTP_LOG_ERROR( "Failed to register WiFi callback" ); + return 0; + } + + while (!IntelTimestamperThreadStop) + { + SleepEx(320, true); + } + + DeregisterIntelCallback(tempCallback.fnIntelCallback); + + return 0; +} + +bool IntelWirelessAdapter::initialize(void) +{ + HMODULE MurocApiDLL = LoadLibrary("MurocApi.dll"); + + GetAdapterList = (GetAdapterList_t) + GetProcAddress(MurocApiDLL, "GetAdapterList"); + if( GetAdapterList == NULL ) + return false; + + RegisterIntelCallback = (RegisterIntelCallback_t) + GetProcAddress(MurocApiDLL, "RegisterIntelCallback"); + if( RegisterIntelCallback == NULL ) + return false; + + DeregisterIntelCallback = (DeregisterIntelCallback_t) + GetProcAddress(MurocApiDLL, "DeregisterIntelCallback"); + if( DeregisterIntelCallback == NULL ) + return false; + + WifiCmdTimingPtmWa = (WifiCmdTimingPtmWa_t) + GetProcAddress(MurocApiDLL, "WifiCmdTimingPtmWa"); + if( WifiCmdTimingPtmWa == NULL ) + return false; + + WifiCmdTimingMeasurementRequest = (WifiCmdTimingMeasurementRequest_t) + GetProcAddress(MurocApiDLL, "WifiCmdTimingMeasurementRequest"); + if (WifiCmdTimingMeasurementRequest == NULL) + return false; + + // Initialize crosstimestamp condition + InitializeConditionVariable(&ct_cond); + InitializeSRWLock(&ct_lock); + ct_miss = 0; + + // Spawn thread + IntelTimestamperThread = CreateThread + ( NULL, 0, IntelWirelessLoop, ×tamperContextMap, 0, NULL); + return true; +} + +void IntelWirelessAdapter::shutdown(void) { + // Signal thread exit + IntelTimestamperThreadStop = true; + // Join thread + WaitForSingleObject(IntelTimestamperThread, INFINITE); +} + +bool IntelWirelessAdapter::refreshCrossTimestamp(void) { + INTEL_WIFI_HEADER header; + WIRELESS_CORRELATEDTIME ptm_wa; + DWORD wait_return = TRUE; + DWORD start = 0; + + AcquireSRWLockExclusive(&ct_lock); + ct_done = false; + header.dwSize = sizeof(ptm_wa); + header.dwStructVersion = INTEL_STRUCT_VER_LATEST; + HRESULT hres = WifiCmdTimingPtmWa(hAdapter, &header, &ptm_wa); + if (hres != S_OK) + return false; + + while (!ct_done && wait_return == TRUE) { + DWORD now = GetTickCount(); + start = start == 0 ? now : start; + DWORD wait = now - start < CORRELATEDTIME_TRANSACTION_TIMEOUT ? + CORRELATEDTIME_TRANSACTION_TIMEOUT - + (now - start) : 0; + wait_return = SleepConditionVariableSRW + ( &ct_cond, &ct_lock, wait, 0 ); + } + ct_miss += wait_return != TRUE ? 1 : 0; + ReleaseSRWLockExclusive(&ct_lock); + + return wait_return == TRUE ? true : false; +} + +bool IntelWirelessAdapter::initiateTimingRequest +( TIMINGMSMT_REQUEST *tm_request ) +{ + INTEL_WIFI_HEADER header; + HRESULT err; + + memset(&header, 0, sizeof(header)); + // Proset wants an equivalent, but slightly different struct definition + header.dwSize = sizeof(*tm_request) - sizeof(PTP_SPEC) + 1; + header.dwStructVersion = INTEL_STRUCT_VER_LATEST; + if(( err = WifiCmdTimingMeasurementRequest + (hAdapter, &header, tm_request)) != S_OK ) + return false; + + return true; +} + +// Find Intel adapter given MAC address and return adapter ID +// +// @adapter(out): Adapter identifier used for all driver interaction +// @mac_addr: HW address of the adapter +// @return: *false* if adapter is not found or driver communication failure +// occurs, otherwise *true* +// +bool IntelWirelessAdapter::attachAdapter(uint8_t *mac_addr) +{ + PINTEL_ADAPTER_LIST adapter_list; + int i; + + if (GetAdapterList(&adapter_list) != S_OK) + return false; + + GPTP_LOG_VERBOSE("Driver query returned %d adapters\n", + adapter_list->count); + for (i = 0; i < adapter_list->count; ++i) { + if( memcmp(adapter_list->adapter[i].btMACAddress, + mac_addr, ETHER_ADDR_OCTETS) == 0 ) + break; + } + + if (i == adapter_list->count) + { + GPTP_LOG_ERROR("Driver failed to find the requested adapter"); + return false; + } + hAdapter = adapter_list->adapter[i].hAdapter; + + return true; +} + +// Register timestamper with Intel wireless subsystem. +// +// @timestamper: timestamper object that should receive events +// @source: MAC address of local interface +// @return: *false* if the MAC address has already been registered, *true* if +// +bool IntelWirelessAdapter::registerTimestamper +(WindowsWirelessTimestamper *timestamper) +{ + bool ret = false; + + AcquireSRWLockExclusive(×tamperContextMap.lock); + // Do not "re-add" the same timestamper + if( timestamperContextMap.map.find + ( *timestamper->getPort()->getLocalAddr() ) + == timestamperContextMap.map.cend()) + { + // Initialize per timestamper context and add + timestamperContextMap.map + [*timestamper->getPort()->getLocalAddr()]. + work_queue.init(0); + timestamperContextMap.map + [*timestamper->getPort()->getLocalAddr()]. + timestamper = timestamper; + ret = true; + } + ReleaseSRWLockExclusive(×tamperContextMap.lock); + + return ret; +} + +bool IntelWirelessAdapter::deregisterTimestamper +( WindowsWirelessTimestamper *timestamper ) +{ + bool ret; + + TimestamperContextMap::iterator iter; + AcquireSRWLockExclusive(×tamperContextMap.lock); + // Find timestamper + iter = timestamperContextMap.map.find + ( *timestamper->getPort()->getLocalAddr( )); + if( iter != timestamperContextMap.map.end( )) + { + // Shutdown work queue + iter->second.work_queue.stop(); + // Remove timestamper from list + timestamperContextMap.map.erase(iter); + ret = true; + } + ReleaseSRWLockExclusive(×tamperContextMap.lock); + + return ret; +} + +uint64_t IntelWirelessAdapter::calc_rollover( uint64_t gp2 ) +{ + gp2 = gp2 & 0xFFFFFFFF; + uint64_t l_gp2_ext; + + if (gp2_last == ULLONG_MAX) + { + gp2_last = gp2; + + return gp2; + } + + if (gp2 < GP2_ROLLOVER/4 && gp2_last > (GP2_ROLLOVER*3)/4) + ++gp2_ext; + + l_gp2_ext = gp2_ext; + if( gp2_last < GP2_ROLLOVER / 4 && gp2 >(GP2_ROLLOVER * 3) / 4 ) + --l_gp2_ext; + else + gp2_last = gp2; + + return gp2 + ( l_gp2_ext * GP2_ROLLOVER ); +} diff --git a/daemons/gptp/windows/daemon_cl/intel_wireless.hpp b/daemons/gptp/windows/daemon_cl/intel_wireless.hpp new file mode 100644 index 00000000..bad97d71 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/intel_wireless.hpp @@ -0,0 +1,187 @@ +/****************************************************************************** + +Copyright (c) 2009-2015, 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 INTEL_WIRELESS_HPP +#define INTEL_WIRELESS_HPP + +#include +#include +#include + +#define INTEL_MAC_ADDR_LENGTH 6 +#define INTEL_MAX_ADAPTERS 20 +#define CORRELATEDTIME_TRANSACTION_TIMEOUT 110/*ms*/ + +enum _WIRELESS_EVENT_TYPE; + +typedef struct INTEL_EVENT +{ + BYTE BtMacAddress[INTEL_MAC_ADDR_LENGTH + 1]; + ///< Source of event (MAC address) + _WIRELESS_EVENT_TYPE eType; ///< Event type + HRESULT hErrCode; ///< Error code + DWORD dwSize; + BYTE* data; +} INTEL_EVENT, *PINTEL_EVENT; + +typedef void(*INTEL_EVENT_CALLBACK) ( INTEL_EVENT iEvent, void *pContext ); + +typedef struct INTEL_CALLBACK +{ + INTEL_EVENT_CALLBACK fnIntelCallback; + void *pContext; + +} INTEL_CALLBACK, *PINTEL_CALLBACK; + +/// Adapter handle +typedef DWORD HADAPTER; + +//intel Header structure +typedef struct _INTEL_WIFI_HEADER +{ + DWORD dwStructVersion; + //Structure version for which this header is created + DWORD dwSize; + //size of the structure for which this header is created + DWORD dwFlags; //For future use + DWORD dwReserved; //For future use +} INTEL_WIFI_HEADER, *PINTEL_WIFI_HEADER; + +typedef enum INTEL_ADAPTER_TYPE +{ + eStone, // Stone Peak - without BT Support + eStoneBT, // Stone Peak - with BT Support + eSnowfield // Snowfield Peak +} INTEL_ADAPTER_TYPE; + +#define INTEL_ADAPTER_NAME_SIZE 64 +#define INTEL_ADAPTER_DESCRIPTION_SIZE 64 +#define INTEL_ADAPTER_CLSGUID_SIZE 64 +#define INTEL_REGPATH_SIZE 512 + +#define INTEL_STRUCT_VER_LATEST 156 + +typedef enum INTEL_ADAPTER_PROTOCOL +{ + eUnknownProtocol, ///< Unknown adapter. + e802_11, ///< WiFi +} INTEL_ADAPTER_PROTOCOL; + +typedef struct INTEL_ADAPTER_LIST_ENTRY +{ + HADAPTER hAdapter; + ///< Adapter handle + UCHAR btMACAddress[INTEL_MAC_ADDR_LENGTH]; + ///< Adapter MAC address + CHAR szAdapterName[INTEL_ADAPTER_NAME_SIZE]; + ///< Adapter OS CLSGUID + CHAR szAdapterDescription[INTEL_ADAPTER_DESCRIPTION_SIZE]; + ///< Display name + CHAR szAdapterClsguid[INTEL_ADAPTER_CLSGUID_SIZE]; + ///< Muroc CLSGUID + CHAR szRegistryPath[INTEL_REGPATH_SIZE]; + ///< Adapter registry root + CHAR szPnPId[INTEL_REGPATH_SIZE]; ///< Plug-and-Play ID + BOOL bEnabled; ///< enabled in driver + INTEL_ADAPTER_TYPE eAdapterType; ///< Adapter type + INTEL_ADAPTER_PROTOCOL eAdapterProtocol; ///< Adapter type +} INTEL_ADAPTER_LIST_ENTRY, *PINTEL_ADAPTER_LIST_ENTRY; + +typedef struct INTEL_ADAPTER_LIST +{ + int count; ///< Number of entries + INTEL_ADAPTER_LIST_ENTRY adapter[INTEL_MAX_ADAPTERS]; + ///< Array of adapter entries +} INTEL_ADAPTER_LIST, *PINTEL_ADAPTER_LIST; + + +typedef HRESULT ( APIENTRY *GetAdapterList_t ) + ( PINTEL_ADAPTER_LIST* ppAdapterList ); +typedef HRESULT ( APIENTRY *WifiCmdTimingMeasurementRequest_t ) + ( HADAPTER, PINTEL_WIFI_HEADER, void * ); +typedef HRESULT ( APIENTRY *WifiCmdTimingPtmWa_t ) + (HADAPTER, PINTEL_WIFI_HEADER, void *); +typedef HRESULT ( APIENTRY *RegisterIntelCallback_t ) ( PINTEL_CALLBACK ); +typedef HRESULT ( APIENTRY *DeregisterIntelCallback_t ) + ( INTEL_EVENT_CALLBACK ); + +extern GetAdapterList_t GetAdapterList; +extern WifiCmdTimingMeasurementRequest_t WifiCmdTimingMeasurementRequest; +extern WifiCmdTimingPtmWa_t WifiCmdTimingPtmWa; +extern RegisterIntelCallback_t RegisterIntelCallback; +extern DeregisterIntelCallback_t DeregisterIntelCallback; + +typedef struct _TIMINGMSMT_REQUEST *tm_request_t; +typedef struct WirelessTimestamperCallbackArg +*WirelessTimestamperCallbackArg_t; +typedef void( *WirelessTimestamperCallback_t ) +( WirelessTimestamperCallbackArg_t arg ); + +class IntelWirelessAdapter : public WindowsWirelessAdapter +{ +private: + HADAPTER hAdapter; + + // Get correlated time operation may timeout + // Use condition wait to manage this + SRWLOCK ct_lock; + CONDITION_VARIABLE ct_cond; + bool ct_done; + int ct_miss; + + uint64_t gp2_last; + unsigned gp2_ext; + +public: + bool initialize( void ); + void shutdown( void ); + bool refreshCrossTimestamp(); + bool initiateTimingRequest( TIMINGMSMT_REQUEST *tm_request ); + bool attachAdapter( uint8_t *mac_addr ); + bool registerTimestamper( WindowsWirelessTimestamper *timestamper ); + bool deregisterTimestamper( WindowsWirelessTimestamper *timestamper ); + IntelWirelessAdapter(); + + uint64_t calc_rollover( uint64_t gp2 ); + + friend void IntelWirelessTimestamperEventHandler + ( INTEL_EVENT iEvent, void *pContext ); +}; + +inline IntelWirelessAdapter::IntelWirelessAdapter() +{ + gp2_ext = 0; + gp2_last = ULLONG_MAX; +} + +#endif diff --git a/daemons/gptp/windows/daemon_cl/tsc.hpp b/daemons/gptp/windows/daemon_cl/tsc.hpp index ed22752c..425205d3 100644 --- a/daemons/gptp/windows/daemon_cl/tsc.hpp +++ b/daemons/gptp/windows/daemon_cl/tsc.hpp @@ -95,13 +95,14 @@ inline uint32_t FindFrequencyByModel(uint8_t model_query) { /** * @brief Gets the TSC frequnecy - * @param millis time in miliseconds + * @param builtin whether device is connected to the Intel bus (not PCIE) * @return TSC frequency, or 0 on error */ -inline uint64_t getTSCFrequency( unsigned millis ) { +inline uint64_t getTSCFrequency( bool builtin ) { int max_cpuid_level; int tmp[4]; BOOL is_windows_10; + LARGE_INTEGER freq; // Find the max cpuid level, and if possible find clock info __cpuid(tmp, 0); @@ -124,9 +125,11 @@ inline uint64_t getTSCFrequency( unsigned millis ) { // clock will be returned, *else* use QPC for everything else // // EAX (tmp[0]) must be >= 1, See Intel SDM 17.15.4 "Invariant Time-keeping" - if (is_windows_10 && + if (!is_windows_10 && max_cpuid_level >= CLOCK_INFO_CPUID_LEAF && - tmp[0] >= 1) { + tmp[0] >= 1 && + builtin ) + { SYSTEM_INFO info; GetSystemInfo(&info); @@ -135,8 +138,6 @@ inline uint64_t getTSCFrequency( unsigned millis ) { return FindFrequencyByModel(info.wProcessorRevision >> 8); } - LARGE_INTEGER freq; - if (QueryPerformanceFrequency(&freq)) return freq.QuadPart; diff --git a/daemons/gptp/windows/daemon_cl/windows_hal.cpp b/daemons/gptp/windows/daemon_cl/windows_hal.cpp index a05d473c..3d034e39 100644 --- a/daemons/gptp/windows/daemon_cl/windows_hal.cpp +++ b/daemons/gptp/windows/daemon_cl/windows_hal.cpp @@ -67,8 +67,184 @@ VOID CALLBACK WindowsTimerQueueHandler( PVOID arg_in, BOOLEAN ignore ) { ReleaseSRWLockExclusive( &arg->queue->retiredTimersLock ); } +inline uint64_t scale64(uint64_t i, uint32_t m, uint32_t n) +{ + uint64_t tmp, res, rem; + + rem = i % n; + i /= n; + + res = i * m; + tmp = rem * m; + + tmp /= n; + + return res + tmp; +} + +void WirelessTimestamperCallback( LPVOID arg ) +{ + WirelessTimestamperCallbackArg *larg = + (WirelessTimestamperCallbackArg *)arg; + WindowsWirelessTimestamper *timestamper = + dynamic_cast (larg->timestamper); + WirelessDialog tmp1, tmp2; + LinkLayerAddress *peer_addr = NULL; + + if (timestamper == NULL) + { + GPTP_LOG_ERROR( "Wrong timestamper type: %p", + larg->timestamper ); + return; + } + + switch( larg->iEvent_type ) + { + default: + case TIMINGMSMT_CONFIRM_EVENT: + tmp1.action_devclk = larg->event_data.confirm.T1; + tmp1.ack_devclk = larg->event_data.confirm.T4; + tmp1.dialog_token = (BYTE)larg->event_data.confirm.DialogToken; + GPTP_LOG_VERBOSE + ( "Got confirm, %hhu(%llu,%llu)", tmp1.dialog_token, + tmp1.action_devclk, tmp1.ack_devclk ); + peer_addr = new LinkLayerAddress + ( larg->event_data.confirm.PeerMACAddress ); + larg->timestamper-> + timingMeasurementConfirmCB( *peer_addr, &tmp1 ); + + break; + + case TIMINGMSMT_EVENT: + tmp1/*prev*/.action_devclk = larg->event_data.indication. + indication.T1; + tmp1/*prev*/.ack_devclk = larg->event_data.indication. + indication.T4; + tmp1/*prev*/.dialog_token = (BYTE)larg->event_data.indication. + indication.FollowUpDialogToken; + tmp2/*curr*/.action_devclk = larg->event_data.indication. + indication.T2; + tmp2/*curr*/.ack_devclk = larg->event_data.indication. + indication.T3; + tmp2/*curr*/.dialog_token = (BYTE)larg->event_data.indication. + indication.DialogToken; + GPTP_LOG_VERBOSE + ("Got indication, %hhu(%llu,%llu) %hhu(%llu,%llu)", + tmp1.dialog_token, tmp1.action_devclk, + tmp1.ack_devclk, tmp2.dialog_token, + tmp2.action_devclk, tmp2.ack_devclk); + peer_addr = new LinkLayerAddress(larg->event_data.indication. + indication.PeerMACAddress); + + larg->timestamper->timeMeasurementIndicationCB + ( *peer_addr, &tmp2, &tmp1, + larg->event_data.indication. + indication.PtpSpec.fwup_data, + larg->event_data.indication. + indication.WiFiVSpecHdr.Length - (sizeof(PTP_SPEC) - + 1)); + + break; + + case TIMINGMSMT_CORRELATEDTIME_EVENT: + timestamper->system_counter = + scale64( larg->event_data.ptm_wa.TSC, NS_PER_SECOND, + (uint32_t)timestamper->tsc_hz.QuadPart ); + timestamper->system_time.set64(timestamper->system_counter); + // Scale from TM timescale to nanoseconds + larg->event_data.ptm_wa.LocalClk *= 10; + timestamper->device_time.set64 + (larg->event_data.ptm_wa.LocalClk*10); + + break; + } + + delete peer_addr; +} + +net_result WindowsWirelessTimestamper::_requestTimingMeasurement +(TIMINGMSMT_REQUEST *timingmsmt_req) +{ + net_result ret = net_succeed; + + if (!adapter->initiateTimingRequest(timingmsmt_req)) { + GPTP_LOG_ERROR("Failed to send timing measurement request\n"); + ret = net_fatal; + } + + return ret; +} + +bool WindowsWirelessTimestamper::HWTimestamper_gettime +( Timestamp *system_time, + Timestamp * device_time, + uint32_t * local_clock, + uint32_t * nominal_clock_rate ) const +{ + bool refreshed = adapter->refreshCrossTimestamp(); + if (refreshed) + { + // We have a fresh cross-timestamp just use it + *system_time = this->system_time; + *device_time = this->device_time; + } else + { + // We weren't able to get a fresh timestamp, + // extrapolate from the last + LARGE_INTEGER tsc_now; + QueryPerformanceCounter(&tsc_now); + unsigned device_delta = (unsigned) + (((long double) (tsc_now.QuadPart - system_counter)) / + (((long double)tsc_hz.QuadPart) / 1000000000)); + device_delta = (unsigned)(device_delta*getPort()-> + getLocalSystemFreqOffset()); + system_time->set64((uint64_t) + (((long double)tsc_now.QuadPart) / + ((long double)tsc_hz.QuadPart / + 1000000000))); + device_time->set64(device_delta); + *device_time = *device_time + this->device_time; + } + + return true; +} + +bool WindowsWirelessTimestamper::HWTimestamper_init +(InterfaceLabel *iface_label, OSNetworkInterface *iface) +{ + uint8_t mac_addr_local[ETHER_ADDR_OCTETS]; + + if (!initialized) { + if (!adapter->initialize()) return false; + if (getPort()->getLocalAddr() == NULL) + return false; + + getPort()->getLocalAddr()->toOctetArray(mac_addr_local); + if (!adapter->attachAdapter(mac_addr_local)) { + return false; + } + + tsc_hz.QuadPart = getTSCFrequency(false); + if (tsc_hz.QuadPart == 0) { + return false; + } + + if (!adapter->registerTimestamper(this)) + return false; + } + + initialized = true; + return true; +} + +WindowsWirelessTimestamper::~WindowsWirelessTimestamper() { + if (adapter->deregisterTimestamper(this)) + adapter->shutdown(); + else + GPTP_LOG_INFO("Failed to shutdown time sync on adapter"); +} -bool WindowsTimestamper::HWTimestamper_init( InterfaceLabel *iface_label, OSNetworkInterface *net_iface ) { +bool WindowsEtherTimestamper::HWTimestamper_init( InterfaceLabel *iface_label, OSNetworkInterface *net_iface ) { char network_card_id[64]; LinkLayerAddress *addr = dynamic_cast(iface_label); if( addr == NULL ) return false; @@ -112,7 +288,7 @@ bool WindowsTimestamper::HWTimestamper_init( InterfaceLabel *iface_label, OSNetw NULL, OPEN_EXISTING, 0, NULL ); if( miniport == INVALID_HANDLE_VALUE ) return false; - tsc_hz.QuadPart = getTSCFrequency( 1000 ); + tsc_hz.QuadPart = getTSCFrequency( true ); if( tsc_hz.QuadPart == 0 ) { return false; } diff --git a/daemons/gptp/windows/daemon_cl/windows_hal.hpp b/daemons/gptp/windows/daemon_cl/windows_hal.hpp index f5192910..5f1a6f61 100644 --- a/daemons/gptp/windows/daemon_cl/windows_hal.hpp +++ b/daemons/gptp/windows/daemon_cl/windows_hal.hpp @@ -36,7 +36,6 @@ /**@file*/ -#include #include #include "avbts_osnet.hpp" #include "avbts_oslock.hpp" @@ -47,6 +46,7 @@ #include "packet.hpp" #include "ieee1588.hpp" #include "ether_tstamper.hpp" +#include "wireless_tstamper.hpp" #include "iphlpapi.h" #include "windows_ipc.hpp" #include "tsc.hpp" @@ -590,6 +590,114 @@ public: } }; +void WirelessTimestamperCallback(LPVOID arg); + +class WindowsWirelessAdapter; + +/** +* @brief Windows Wireless (802.11) HWTimestamper implementation +*/ +class WindowsWirelessTimestamper : public WirelessTimestamper +{ +private: + WindowsWirelessAdapter *adapter; + + uint64_t system_counter; + Timestamp system_time; + Timestamp device_time; + LARGE_INTEGER tsc_hz; + bool initialized; + +public: + WindowsWirelessTimestamper() + { + initialized = false; + } + + net_result _requestTimingMeasurement + ( TIMINGMSMT_REQUEST *timingmsmt_req ); + + bool HWTimestamper_gettime + ( Timestamp *system_time, Timestamp * device_time, + uint32_t * local_clock, uint32_t * nominal_clock_rate ) const; + + virtual bool HWTimestamper_init + ( InterfaceLabel *iface_label, OSNetworkInterface *iface ); + + /** + * @brief attach adapter to timestamper + * @param adapter [in] adapter to attach + */ + void setAdapter( WindowsWirelessAdapter *adapter ) + { + this->adapter = adapter; + } + + /** + * @brief get attached adapter + * @return attached adapter + */ + WindowsWirelessAdapter *getAdapter(void) + { + return adapter; + } + + ~WindowsWirelessTimestamper(); + + friend void WirelessTimestamperCallback( LPVOID arg ); +}; + +class WindowsWirelessAdapter +{ +public: + /** + * @brief initiate wireless TM request (completion is asynchronous) + * @param tm_request [in] pointer to TM request object + * @return true on success + */ + virtual bool initiateTimingRequest(TIMINGMSMT_REQUEST *tm_request) = 0; + + /** + * @brief attempt to refresh cross timestamp (extrapolate on failure) + * @return true on success + */ + virtual bool refreshCrossTimestamp() = 0; + + /** + * @brief register timestamper with adapter + * @param timestamper [in] timestamper object + * @return true on success + */ + virtual bool registerTimestamper + ( WindowsWirelessTimestamper *timestamper ) = 0; + + /** + * @brief deregister timestamper + * @param timestamper [in] timestamper object + * @return true on success + */ + virtual bool deregisterTimestamper + ( WindowsWirelessTimestamper *timestamper ) = 0; + + /** + * @brief initialize adapter object + * @return true on success + */ + virtual bool initialize() = 0; + + /** + * @brief shutdown adapter + */ + virtual void shutdown() = 0; + + /** + * @brief attach adapter to MAC address + * @param mac_addr [in] MAC address to attach to + * @return true on success + */ + virtual bool attachAdapter( uint8_t *mac_addr ) = 0; +}; + #define I217_DESC "I217-LM" #define I219_DESC "I219-V" @@ -616,9 +724,9 @@ static DeviceClockRateMapping DeviceClockRateMap[] = }; /** - * @brief Windows HWTimestamper implementation + * @brief Windows Ethernet HWTimestamper implementation */ -class WindowsTimestamper : public EtherTimestamper { +class WindowsEtherTimestamper : public EtherTimestamper { private: // No idea whether the underlying implementation is thread safe HANDLE miniport; diff --git a/daemons/gptp/windows/daemon_cl/work_queue.cpp b/daemons/gptp/windows/daemon_cl/work_queue.cpp new file mode 100644 index 00000000..461d6923 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/work_queue.cpp @@ -0,0 +1,106 @@ +/****************************************************************************** + +Copyright (c) 2009-2015, 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 + + +struct WWQueueThreadState { + bool running; + bool stop; + WWQueueCallback task; + LPVOID arg; +}; + +DWORD WINAPI WindowsWorkQueueLoop(LPVOID arg) { + WWQueueThreadState *state = (WWQueueThreadState *)arg; + state->running = true; + while (!state->stop) { + if (state->task != NULL) { + state->task(state->arg); + delete state->arg; + state->task = NULL; + } + Sleep(1); + } + state->running = false; + + return 0; +} + +bool WindowsWorkQueue::init(int number_threads) +{ + if (number_threads == 0) number_threads = DEFAULT_THREAD_COUNT; + state = new WWQueueThreadState[number_threads]; + for (int i = 0; i < number_threads; ++i) { + state[i].running = false; + state[i].stop = false; + state[i].task = NULL; + } + workers = new HANDLE[number_threads]; + for (int i = 0; i < number_threads; ++i) { + workers[i] = CreateThread(NULL, 0, WindowsWorkQueueLoop, state + i, 0, NULL); + if (workers[i] == INVALID_HANDLE_VALUE) + return false; + while (!state[i].running) + Sleep(1); + } + this->number_threads = number_threads; + return true; +} + +bool WindowsWorkQueue::submit(WWQueueCallback cb, LPVOID arg) +{ + int i; + + for (i = 0; i < number_threads; ++i) { + if (state[i].task == NULL) { + state[i].arg = arg; + state[i].task = cb; + break; + } + } + if (i == number_threads) + return false; + + return true; +} + +void WindowsWorkQueue::stop() +{ + for (int i = 0; i < number_threads; ++i) { + state[i].stop = true; + while (state[i].running) + Sleep(1); + } +} diff --git a/daemons/gptp/windows/daemon_cl/work_queue.hpp b/daemons/gptp/windows/daemon_cl/work_queue.hpp new file mode 100644 index 00000000..4ac750c5 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/work_queue.hpp @@ -0,0 +1,74 @@ +/****************************************************************************** + +Copyright (c) 2009-2015, 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 WORK_QUEUE_HPP +#define WORK_QUEUE_HPP + +#include + +#define DEFAULT_THREAD_COUNT (5) + +struct WWQueueThreadState; + +typedef void(*WWQueueCallback)(LPVOID arg); + +class WindowsWorkQueue +{ +private: + HANDLE *workers; + WWQueueThreadState *state; + int number_threads; + +public: + /** + * @brief initialize work queue + * @param number_threads [in] number of threads (0 = default) + * @return true on success + */ + bool init( int number_threads ); + + /** + * @brief submit job to work queue + * @param cb [in] function to call + * @param arg [in] parameter provided to callback + * @return true on success + */ + bool submit( WWQueueCallback cb, LPVOID arg ); + + /** + * @brief stop work queue + */ + void stop(); +}; + +#endif/*WORK_QUEUE_HPP*/ diff --git a/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp b/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp index 713a2b4a..9a34a4ef 100644 --- a/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp +++ b/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp @@ -60,7 +60,7 @@ int _tmain(int argc, _TCHAR* argv[]) strcpy_s( pipename, 64, PIPE_PREFIX ); strcat_s( pipename, 64-strlen(pipename), P802_1AS_PIPENAME ); HANDLE pipe; - uint64_t tsc_frequency = getTSCFrequency( 1000 ); + uint64_t tsc_frequency = getTSCFrequency( true ); // Wait for Ctrl-C if( !SetConsoleCtrlHandler( ctrl_handler, true )) { -- cgit v1.2.1 From 004811f0b51f89c8123a484028f36dbd7776e637 Mon Sep 17 00:00:00 2001 From: Brant Thomsen Date: Fri, 15 Dec 2017 14:43:10 -0700 Subject: Apple PTP Profile references point to new feature-aptp-experimental branch --- README.rst | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index b8fecdf1..f1c29cd4 100644 --- a/README.rst +++ b/README.rst @@ -98,6 +98,16 @@ Starting from the OpenAvnu dir, one would go:: to build in OpenAvnu/tmp +Apple Vendor PTP Profile +======================== + +Support for the Apple Vendor PTP Profile can be found on the +feature-aptp-experimental branch of the OpenAvnu repository. + +These changes allow interaction with Apple proprietary PTP clocks. This +implementation has been tested with the Apple AirPlay SDK on a Raspberry Pi 3 +running within a group of devices playing the same music stream. + RELATED OPEN SOURCE PROJECTS ============================ @@ -119,11 +129,3 @@ XMOS XMOS is a semiconductor company providing a reference design for AVB/TSN endpoints in pro audio and automotive. XMOS endpoint source code is open source and available on Github - https://github.com/xcore/sw_avb - -Apple Vendor PTP Profile ------------------------- -Support for the Apple Vendor PTP Profile can be found on this fork of the OpenAvnu code within the branch ArtAndLogic-aPTP-changes. - -+ https://github.com/rroussel/OpenAvnu/tree/ArtAndLogic-aPTP-changes - -These changes allow interaction with Apple proprietary PTP clocks. This implementation has been tested with the Apple AirPlay SDK on a Raspberry Pi 3 running within a group of devices playing the same music stream. -- cgit v1.2.1 From d851bf5316d27a6f135e258b8ee479341fe50463 Mon Sep 17 00:00:00 2001 From: "Christopher S. Hall" Date: Mon, 8 Jan 2018 08:37:51 -0500 Subject: Add brackets fixing broken if statement --- daemons/gptp/common/ptp_message.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/daemons/gptp/common/ptp_message.cpp b/daemons/gptp/common/ptp_message.cpp index 79c85e43..e8835570 100644 --- a/daemons/gptp/common/ptp_message.cpp +++ b/daemons/gptp/common/ptp_message.cpp @@ -1037,8 +1037,11 @@ void PTPMessageFollowUp::processMessage port->incCounter_ieee8021AsPortStatRxFollowUpCount(); if (!port->getLinkDelay(&delay)) - GPTP_LOG_ERROR("Received Follow up but there is no valid link delay"); + { + GPTP_LOG_ERROR( "Received Follow up but " + "there is no valid link delay" ); goto done; + } master_local_freq_offset = tlv.getRateOffset(); master_local_freq_offset /= 1ULL << 41; -- cgit v1.2.1 From 3442b063cb95ffce9f60f3ead1b03cf3e62892b9 Mon Sep 17 00:00:00 2001 From: Pawel Modrzejewski Date: Mon, 8 Jan 2018 14:17:27 +0100 Subject: gptp: simple fixing/adding logging Details: - changed fprintf to proper error log message; - error log messege added to avoid silent passing Sync/FollowUp processing; --- daemons/gptp/common/ptp_message.cpp | 1 + daemons/gptp/linux/src/linux_hal_common.cpp | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/daemons/gptp/common/ptp_message.cpp b/daemons/gptp/common/ptp_message.cpp index 6decc7a3..79c85e43 100644 --- a/daemons/gptp/common/ptp_message.cpp +++ b/daemons/gptp/common/ptp_message.cpp @@ -1037,6 +1037,7 @@ void PTPMessageFollowUp::processMessage port->incCounter_ieee8021AsPortStatRxFollowUpCount(); if (!port->getLinkDelay(&delay)) + GPTP_LOG_ERROR("Received Follow up but there is no valid link delay"); goto done; master_local_freq_offset = tlv.getRateOffset(); diff --git a/daemons/gptp/linux/src/linux_hal_common.cpp b/daemons/gptp/linux/src/linux_hal_common.cpp index 3be16c86..77bf664f 100644 --- a/daemons/gptp/linux/src/linux_hal_common.cpp +++ b/daemons/gptp/linux/src/linux_hal_common.cpp @@ -528,9 +528,7 @@ bool LinuxTimerQueue::addEvent its.it_value.tv_nsec = (micros % 1000000) * 1000; err = timer_settime( outer_arg->timer_handle, 0, &its, NULL ); if( err < 0 ) { - fprintf - ( stderr, "Failed to arm timer: %s\n", - strerror( errno )); + GPTP_LOG_ERROR("Failed to arm timer: %s", strerror(errno)); return false; } } -- cgit v1.2.1