diff options
author | Jakub Fornal <jakub.fornal@intel.com> | 2018-01-12 11:43:10 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-12 11:43:10 +0100 |
commit | 899696d954d97f88953eecb40ef27b36b88eb6ec (patch) | |
tree | 610ebabef2f508ff8da7dc4cbd1aad4be92e8283 | |
parent | d52cfef90665dd171f59fcd4a88a5b0c24e8f58a (diff) | |
parent | 7ad93aff68346de33c09e65fc829281f71d5e408 (diff) | |
download | Open-AVB-899696d954d97f88953eecb40ef27b36b88eb6ec.tar.gz |
Merge pull request #19 from AVnu/open-avb-next
Open avb next
40 files changed, 3043 insertions, 510 deletions
@@ -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 = ðer_phy_delay; - EtherPort *port = new EtherPort( &portInit ); - port->setLinkSpeed(findLinkSpeed(&local_addr)); - if ( !port->init_port() ) { - printf( "Failed to initialize port\n" ); - return -1; + portInit.clock = new IEEE1588Clock(false, false, priority1, timerq_factory, ipc, portInit.lock_factory); // Do not force slave + + if (!wireless) + { + // Create Port Object linked to clock and low level + portInit.phy_delay = ðer_phy_delay; + EtherPort *eport = new EtherPort(&portInit); + eport->setLinkSpeed( findLinkSpeed( static_cast <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( ×tamperContextMap->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(×tamperContextMap->lock); +} + +DWORD WINAPI IntelWirelessLoop(LPVOID arg) +{ + // Register for callback + INTEL_CALLBACK tempCallback; + tempCallback.fnIntelCallback = IntelWirelessTimestamperEventHandler; + tempCallback.pContext = arg; + + if (RegisterIntelCallback(&tempCallback) != S_OK) { + GPTP_LOG_ERROR( "Failed to register WiFi callback" ); + return 0; + } + + while (!IntelTimestamperThreadStop) + { + SleepEx(320, true); + } + + DeregisterIntelCallback(tempCallback.fnIntelCallback); + + return 0; +} + +bool IntelWirelessAdapter::initialize(void) +{ + HMODULE MurocApiDLL = LoadLibrary("MurocApi.dll"); + + GetAdapterList = (GetAdapterList_t) + GetProcAddress(MurocApiDLL, "GetAdapterList"); + if( GetAdapterList == NULL ) + return false; + + RegisterIntelCallback = (RegisterIntelCallback_t) + GetProcAddress(MurocApiDLL, "RegisterIntelCallback"); + if( RegisterIntelCallback == NULL ) + return false; + + DeregisterIntelCallback = (DeregisterIntelCallback_t) + GetProcAddress(MurocApiDLL, "DeregisterIntelCallback"); + if( DeregisterIntelCallback == NULL ) + return false; + + WifiCmdTimingPtmWa = (WifiCmdTimingPtmWa_t) + GetProcAddress(MurocApiDLL, "WifiCmdTimingPtmWa"); + if( WifiCmdTimingPtmWa == NULL ) + return false; + + WifiCmdTimingMeasurementRequest = (WifiCmdTimingMeasurementRequest_t) + GetProcAddress(MurocApiDLL, "WifiCmdTimingMeasurementRequest"); + if (WifiCmdTimingMeasurementRequest == NULL) + return false; + + // Initialize crosstimestamp condition + InitializeConditionVariable(&ct_cond); + InitializeSRWLock(&ct_lock); + ct_miss = 0; + + // Spawn thread + IntelTimestamperThread = CreateThread + ( NULL, 0, IntelWirelessLoop, ×tamperContextMap, 0, NULL); + return true; +} + +void IntelWirelessAdapter::shutdown(void) { + // Signal thread exit + IntelTimestamperThreadStop = true; + // Join thread + WaitForSingleObject(IntelTimestamperThread, INFINITE); +} + +bool IntelWirelessAdapter::refreshCrossTimestamp(void) { + INTEL_WIFI_HEADER header; + WIRELESS_CORRELATEDTIME ptm_wa; + DWORD wait_return = TRUE; + DWORD start = 0; + + AcquireSRWLockExclusive(&ct_lock); + ct_done = false; + header.dwSize = sizeof(ptm_wa); + header.dwStructVersion = INTEL_STRUCT_VER_LATEST; + HRESULT hres = WifiCmdTimingPtmWa(hAdapter, &header, &ptm_wa); + if (hres != S_OK) + return false; + + while (!ct_done && wait_return == TRUE) { + DWORD now = GetTickCount(); + start = start == 0 ? now : start; + DWORD wait = now - start < CORRELATEDTIME_TRANSACTION_TIMEOUT ? + CORRELATEDTIME_TRANSACTION_TIMEOUT - + (now - start) : 0; + wait_return = SleepConditionVariableSRW + ( &ct_cond, &ct_lock, wait, 0 ); + } + ct_miss += wait_return != TRUE ? 1 : 0; + ReleaseSRWLockExclusive(&ct_lock); + + return wait_return == TRUE ? true : false; +} + +bool IntelWirelessAdapter::initiateTimingRequest +( TIMINGMSMT_REQUEST *tm_request ) +{ + INTEL_WIFI_HEADER header; + HRESULT err; + + memset(&header, 0, sizeof(header)); + // Proset wants an equivalent, but slightly different struct definition + header.dwSize = sizeof(*tm_request) - sizeof(PTP_SPEC) + 1; + header.dwStructVersion = INTEL_STRUCT_VER_LATEST; + if(( err = WifiCmdTimingMeasurementRequest + (hAdapter, &header, tm_request)) != S_OK ) + return false; + + return true; +} + +// Find Intel adapter given MAC address and return adapter ID +// +// @adapter(out): Adapter identifier used for all driver interaction +// @mac_addr: HW address of the adapter +// @return: *false* if adapter is not found or driver communication failure +// occurs, otherwise *true* +// +bool IntelWirelessAdapter::attachAdapter(uint8_t *mac_addr) +{ + PINTEL_ADAPTER_LIST adapter_list; + int i; + + if (GetAdapterList(&adapter_list) != S_OK) + return false; + + GPTP_LOG_VERBOSE("Driver query returned %d adapters\n", + adapter_list->count); + for (i = 0; i < adapter_list->count; ++i) { + if( memcmp(adapter_list->adapter[i].btMACAddress, + mac_addr, ETHER_ADDR_OCTETS) == 0 ) + break; + } + + if (i == adapter_list->count) + { + GPTP_LOG_ERROR("Driver failed to find the requested adapter"); + return false; + } + hAdapter = adapter_list->adapter[i].hAdapter; + + return true; +} + +// Register timestamper with Intel wireless subsystem. +// +// @timestamper: timestamper object that should receive events +// @source: MAC address of local interface +// @return: *false* if the MAC address has already been registered, *true* if +// +bool IntelWirelessAdapter::registerTimestamper +(WindowsWirelessTimestamper *timestamper) +{ + bool ret = false; + + AcquireSRWLockExclusive(×tamperContextMap.lock); + // Do not "re-add" the same timestamper + if( timestamperContextMap.map.find + ( *timestamper->getPort()->getLocalAddr() ) + == timestamperContextMap.map.cend()) + { + // Initialize per timestamper context and add + timestamperContextMap.map + [*timestamper->getPort()->getLocalAddr()]. + work_queue.init(0); + timestamperContextMap.map + [*timestamper->getPort()->getLocalAddr()]. + timestamper = timestamper; + ret = true; + } + ReleaseSRWLockExclusive(×tamperContextMap.lock); + + return ret; +} + +bool IntelWirelessAdapter::deregisterTimestamper +( WindowsWirelessTimestamper *timestamper ) +{ + bool ret; + + TimestamperContextMap::iterator iter; + AcquireSRWLockExclusive(×tamperContextMap.lock); + // Find timestamper + iter = timestamperContextMap.map.find + ( *timestamper->getPort()->getLocalAddr( )); + if( iter != timestamperContextMap.map.end( )) + { + // Shutdown work queue + iter->second.work_queue.stop(); + // Remove timestamper from list + timestamperContextMap.map.erase(iter); + ret = true; + } + ReleaseSRWLockExclusive(×tamperContextMap.lock); + + return ret; +} + +uint64_t IntelWirelessAdapter::calc_rollover( uint64_t gp2 ) +{ + gp2 = gp2 & 0xFFFFFFFF; + uint64_t l_gp2_ext; + + if (gp2_last == ULLONG_MAX) + { + gp2_last = gp2; + + return gp2; + } + + if (gp2 < GP2_ROLLOVER/4 && gp2_last > (GP2_ROLLOVER*3)/4) + ++gp2_ext; + + l_gp2_ext = gp2_ext; + if( gp2_last < GP2_ROLLOVER / 4 && gp2 >(GP2_ROLLOVER * 3) / 4 ) + --l_gp2_ext; + else + gp2_last = gp2; + + return gp2 + ( l_gp2_ext * GP2_ROLLOVER ); +} diff --git a/daemons/gptp/windows/daemon_cl/intel_wireless.hpp b/daemons/gptp/windows/daemon_cl/intel_wireless.hpp new file mode 100644 index 00000000..bad97d71 --- /dev/null +++ b/daemons/gptp/windows/daemon_cl/intel_wireless.hpp @@ -0,0 +1,187 @@ +/****************************************************************************** + +Copyright (c) 2009-2015, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the Intel Corporation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef INTEL_WIRELESS_HPP +#define INTEL_WIRELESS_HPP + +#include <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 + |