summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Fornal <jakub.fornal@intel.com>2018-01-12 11:43:10 +0100
committerGitHub <noreply@github.com>2018-01-12 11:43:10 +0100
commit899696d954d97f88953eecb40ef27b36b88eb6ec (patch)
tree610ebabef2f508ff8da7dc4cbd1aad4be92e8283
parentd52cfef90665dd171f59fcd4a88a5b0c24e8f58a (diff)
parent7ad93aff68346de33c09e65fc829281f71d5e408 (diff)
downloadOpen-AVB-899696d954d97f88953eecb40ef27b36b88eb6ec.tar.gz
Merge pull request #19 from AVnu/open-avb-next
Open avb next
-rw-r--r--README.rst18
-rw-r--r--daemons/gptp/README.rst29
-rw-r--r--daemons/gptp/common/avbts_clock.hpp9
-rw-r--r--daemons/gptp/common/avbts_message.hpp105
-rw-r--r--daemons/gptp/common/avbts_osipc.hpp1
-rw-r--r--daemons/gptp/common/common_port.cpp9
-rw-r--r--daemons/gptp/common/common_port.hpp97
-rw-r--r--daemons/gptp/common/ether_port.cpp97
-rw-r--r--daemons/gptp/common/ether_port.hpp38
-rw-r--r--daemons/gptp/common/ieee1588.hpp12
-rw-r--r--daemons/gptp/common/ptp_message.cpp624
-rw-r--r--daemons/gptp/common/ptptypes.hpp1
-rw-r--r--daemons/gptp/common/wireless_port.cpp242
-rw-r--r--daemons/gptp/common/wireless_port.hpp179
-rw-r--r--daemons/gptp/common/wireless_tstamper.cpp127
-rw-r--r--daemons/gptp/common/wireless_tstamper.hpp221
-rw-r--r--daemons/gptp/linux/src/daemon_cl.cpp1
-rw-r--r--daemons/gptp/linux/src/linux_hal_common.cpp4
-rw-r--r--daemons/gptp/linux/src/linux_hal_generic.cpp21
-rw-r--r--daemons/gptp/windows/daemon_cl/daemon_cl.cpp129
-rw-r--r--daemons/gptp/windows/daemon_cl/intel_wireless.cpp424
-rw-r--r--daemons/gptp/windows/daemon_cl/intel_wireless.hpp187
-rw-r--r--daemons/gptp/windows/daemon_cl/tsc.hpp13
-rw-r--r--daemons/gptp/windows/daemon_cl/windows_hal.cpp180
-rw-r--r--daemons/gptp/windows/daemon_cl/windows_hal.hpp114
-rw-r--r--daemons/gptp/windows/daemon_cl/work_queue.cpp106
-rw-r--r--daemons/gptp/windows/daemon_cl/work_queue.hpp74
-rw-r--r--daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp2
-rw-r--r--daemons/maap/linux/src/maap_log_linux.c17
-rw-r--r--daemons/mrpd/mvrp.c3
-rw-r--r--daemons/shaper/src/shaper_log_linux.c16
-rw-r--r--examples/mrp_client/mrpdhelper.c4
-rw-r--r--lib/avtp_pipeline/README.md64
-rw-r--r--lib/avtp_pipeline/avtp_avdecc.mk9
-rw-r--r--lib/avtp_pipeline/avtp_pipeline.mk7
-rw-r--r--lib/avtp_pipeline/platform/Linux/intf_alsa/example_listener.ini135
-rw-r--r--lib/avtp_pipeline/platform/Linux/intf_alsa/example_talker.ini181
-rw-r--r--lib/avtp_pipeline/platform/Linux/openavb_tasks.h10
-rwxr-xr-xrun_avtp_pipeline.sh24
-rwxr-xr-xstop_avtp_pipeline.sh19
40 files changed, 3043 insertions, 510 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.
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 <local hw device MAC> <local P2P MAC> <remove P2P MAC>
+
+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 <local hw device MAC> <local P2P MAC> <remove P2P MAC>
+
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
@@ -204,6 +204,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 <stdint.h>
#include <ptptypes.hpp>
-#include <ether_port.hpp>
/**@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 <math.h>
+#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;
@@ -436,6 +447,13 @@ public:
}
/**
+ * @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
*/
@@ -1115,6 +1133,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
* @return void
@@ -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;
@@ -201,12 +194,6 @@ protected:
void syncDone();
/**
- * @brief Gets the AVnu automotive profile flag
- * @return automotive_profile flag
- */
- bool getAutomotiveProfile() { return( automotive_profile ); }
-
- /**
* @brief Destroys a EtherPort
*/
~EtherPort();
@@ -303,31 +290,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
* @return none
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..e8835570 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 <EtherPort *> ( 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 <EtherPort *> (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<<PTP_ASSIST_BIT)) {
#endif
- PTPMessageSync *old_sync = port->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,205 @@ 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);
- goto done;
- }
-
- if (sync->getTimestamp()._version != port->getTimestampVersion())
+ if (!port->getLinkDelay(&delay))
{
- GPTP_LOG_ERROR("Received Follow Up but timestamp version indicates Sync is out of date");
+ GPTP_LOG_ERROR( "Received Follow up but "
+ "there is no valid link delay" );
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 <EtherPort *> (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 +1243,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 +1252,13 @@ void PTPMessagePathDelayReq::processMessage( EtherPort *port )
PortIdentity resp_id;
PTPMessagePathDelayRespFollowUp *resp_fwup;
+ EtherPort *eport = dynamic_cast <EtherPort *> (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 +1266,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 +1291,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 +1304,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 +1328,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 +1386,29 @@ PTPMessagePathDelayResp::~PTPMessagePathDelayResp()
delete requestingPortIdentity;
}
-void PTPMessagePathDelayResp::processMessage( EtherPort *port )
+void PTPMessagePathDelayResp::processMessage( CommonPort *port )
{
+ EtherPort *eport = dynamic_cast <EtherPort *> (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 +1418,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 +1437,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 +1555,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 <EtherPort *> (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 +1606,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 +1696,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 +1755,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 +1781,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 +1941,7 @@ bool PTPMessageSignalling::sendPort
return true;
}
-void PTPMessageSignalling::processMessage( EtherPort *port )
+void PTPMessageSignalling::processMessage( CommonPort *port )
{
long long unsigned int waitTime;
@@ -1834,7 +1954,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/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/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 <wireless_port.hpp>
+#include <wireless_tstamper.hpp>
+#include <avbts_clock.hpp>
+
+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<WirelessTimestamper *>( _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 <common_port.hpp>
+#include <avbts_oscondition.hpp>
+
+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 <wireless_tstamper.hpp>
+#include <wireless_port.hpp>
+
+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<PTPMessageFollowUp *> (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 <common_tstamper.hpp>
+#include <wireless_port.hpp>
+
+#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/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;
}
}
diff --git a/daemons/gptp/linux/src/linux_hal_generic.cpp b/daemons/gptp/linux/src/linux_hal_generic.cpp
index 5364fa6a..9eeba46f 100644
--- a/daemons/gptp/linux/src/linux_hal_generic.cpp
+++ b/daemons/gptp/linux/src/linux_hal_generic.cpp
@@ -30,9 +30,10 @@
POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
-
#include <linux_hal_generic.hpp>
#include <linux_hal_generic_tsprivate.hpp>
+#include <platform.hpp>
+#include <avbts_message.hpp>
#include <sys/select.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
@@ -285,6 +286,10 @@ int LinuxTimestamperGeneric::HWTimestamper_txtimestamp
struct cmsghdr *cmsg;
struct sockaddr_ll remote;
struct iovec sgentry;
+ PTPMessageId reflectedMessageId;
+ uint8_t reflected_bytes[ETHER_HDR_LEN + PTP_COMMON_HDR_LENGTH];
+ uint8_t *gptpCommonHeader;
+ uint16_t sequenceId;
struct {
struct cmsghdr cm;
char control[256];
@@ -296,8 +301,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 + ETHER_HDR_LEN;
memset( &remote, 0, sizeof(remote));
msg.msg_name = (caddr_t) &remote;
@@ -316,6 +323,14 @@ int LinuxTimestamperGeneric::HWTimestamper_txtimestamp
goto done;
}
}
+ sequenceId = PLAT_ntohs(*((uint16_t*)(PTP_COMMON_HDR_SEQUENCE_ID(gptpCommonHeader))));
+ reflectedMessageId.setSequenceId(sequenceId);
+ 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;
+ goto done;
+ }
// Retrieve the timestamp
cmsg = CMSG_FIRSTHDR(&msg);
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 <tchar.h>
#include <iphlpapi.h>
+#include <ether_port.hpp>
+#include <wireless_port.hpp>
+#include <intel_wireless.hpp>
+
/* 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<WindowsWirelessTimestamper *> (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 = &ether_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 = &ether_phy_delay;
+ EtherPort *eport = new EtherPort(&portInit);
+ eport->setLinkSpeed( findLinkSpeed( static_cast <LinkLayerAddress *> ( 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 <WirelessTimestamper *> (portInit.timestamper))->setPort( static_cast<WirelessPort *> ( 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 <intel_wireless.hpp>
+#include <windows_hal.hpp>
+#include <work_queue.hpp>
+#include <memory>
+#include <map>
+
+#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( &timestamperContextMap->lock );
+ if( timestamperContextMap->map.find( event_source )
+ != timestamperContextMap->map.cend())
+ {
+ arg->timestamper =
+ timestamperContextMap->map[event_source].timestamper;
+ } else
+ {
+ goto bail;
+ }
+
+ timestamper = dynamic_cast <WindowsWirelessTimestamper *>
+ (arg->timestamper);
+ if( timestamper == NULL )
+ {
+ GPTP_LOG_ERROR( "Unexpected timestamper type encountered" );
+ goto bail;
+ }
+ adapter = dynamic_cast <IntelWirelessAdapter *>
+ (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(&timestamperContextMap->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, &timestamperContextMap, 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(&timestamperContextMap.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(&timestamperContextMap.lock);
+
+ return ret;
+}
+
+bool IntelWirelessAdapter::deregisterTimestamper
+( WindowsWirelessTimestamper *timestamper )
+{
+ bool ret;
+
+ TimestamperContextMap::iterator iter;
+ AcquireSRWLockExclusive(&timestamperContextMap.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(&timestamperContextMap.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 <windows_hal.hpp>
+#include <Windows.h>
+#include <stdint.h>
+
+#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<WindowsWirelessTimestamper *> (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<LinkLayerAddress *>(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 <minwindef.h>
#include <IPCListener.hpp>
#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 <work_queue.hpp>
+#include <stdio.h>
+
+
+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 <WTypesbase.h>
+
+#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 )) {
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 <stdarg.h>
#include <string.h>
#include <inttypes.h>
+#include <limits.h>
#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/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:
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 <stdarg.h>
#include <string.h>
#include <inttypes.h>
+#include <limits.h>
#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/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);
}
diff --git a/lib/avtp_pipeline/README.md b/lib/avtp_pipeline/README.md
index 23e6e525..62aba7da 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,30 +63,40 @@ 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.
-
-## 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.
+ - 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 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.
+- `$ 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. 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.
+
+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
-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) \
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/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 <limits.h>
+
+#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
diff --git a/run_avtp_pipeline.sh b/run_avtp_pipeline.sh
new file mode 100755
index 00000000..0a632bb0
--- /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
+
+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 &
+popd
+
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
+