summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandrew-elder <aelder@audioscience.com>2018-04-03 13:18:36 -0400
committerGitHub <noreply@github.com>2018-04-03 13:18:36 -0400
commitc253bd9204ed0fe85619266c0da1a47fbfdaab0a (patch)
treef256df34cf5062956ac28b4894044d3fa685f5de
parent5e43fa5ae27fbd2b89c320670a32143ce93e0e16 (diff)
parent7d0f6655b04ef9af9a74319997ead5e5a1dcc2be (diff)
downloadOpen-AVB-c253bd9204ed0fe85619266c0da1a47fbfdaab0a.tar.gz
Merge pull request #773 from AVnu/open-avb-next
Maintenance pull request
-rw-r--r--.travis.yml15
-rw-r--r--README.rst18
-rw-r--r--daemons/gptp/README.rst29
-rw-r--r--daemons/gptp/common/avbts_clock.hpp9
-rw-r--r--daemons/gptp/common/avbts_message.hpp105
-rw-r--r--daemons/gptp/common/avbts_osipc.hpp1
-rw-r--r--daemons/gptp/common/common_port.cpp9
-rw-r--r--daemons/gptp/common/common_port.hpp97
-rw-r--r--daemons/gptp/common/ether_port.cpp97
-rw-r--r--daemons/gptp/common/ether_port.hpp38
-rw-r--r--daemons/gptp/common/ieee1588.hpp12
-rw-r--r--daemons/gptp/common/ptp_message.cpp624
-rw-r--r--daemons/gptp/common/wireless_port.cpp242
-rw-r--r--daemons/gptp/common/wireless_port.hpp179
-rw-r--r--daemons/gptp/common/wireless_tstamper.cpp127
-rw-r--r--daemons/gptp/common/wireless_tstamper.hpp221
-rw-r--r--daemons/gptp/linux/src/daemon_cl.cpp1
-rw-r--r--daemons/gptp/linux/src/linux_hal_common.cpp24
-rw-r--r--daemons/gptp/windows/daemon_cl/daemon_cl.cpp129
-rw-r--r--daemons/gptp/windows/daemon_cl/intel_wireless.cpp424
-rw-r--r--daemons/gptp/windows/daemon_cl/intel_wireless.hpp187
-rw-r--r--daemons/gptp/windows/daemon_cl/tsc.hpp13
-rw-r--r--daemons/gptp/windows/daemon_cl/windows_hal.cpp180
-rw-r--r--daemons/gptp/windows/daemon_cl/windows_hal.hpp114
-rw-r--r--daemons/gptp/windows/daemon_cl/work_queue.cpp106
-rw-r--r--daemons/gptp/windows/daemon_cl/work_queue.hpp74
-rw-r--r--daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp2
-rw-r--r--kmod/igb/e1000_82575.c14
-rw-r--r--kmod/igb/e1000_api.c1
-rw-r--r--kmod/igb/e1000_hw.h1
-rw-r--r--kmod/igb/e1000_osdep.h2
-rw-r--r--kmod/igb/igb.h22
-rw-r--r--kmod/igb/igb_ethtool.c8
-rw-r--r--kmod/igb/igb_main.c189
-rw-r--r--kmod/igb/igb_param.c2
-rw-r--r--[-rwxr-xr-x]lib/avtp_pipeline/map_h264/openavb_map_h264.c26
-rw-r--r--[-rwxr-xr-x]lib/avtp_pipeline/map_h264/openavb_map_h264_pub.h2
-rw-r--r--lib/avtp_pipeline/platform/Linux/gst_al/gst_al.h8
-rw-r--r--lib/avtp_pipeline/platform/Linux/gst_al/gst_al_10.c5
-rw-r--r--lib/avtp_pipeline/platform/Linux/intf_h264_gst/openavb_intf_h264_gst.c5
-rw-r--r--lib/igb/e1000_hw.h1
-rw-r--r--lib/igb/igb.c33
-rw-r--r--lib/igb/igb_internal.h16
-rw-r--r--lib/libavtp/HACKING.md73
-rw-r--r--lib/libavtp/LICENSE24
-rw-r--r--lib/libavtp/README.md48
-rw-r--r--lib/libavtp/examples/aaf-listener.c536
-rw-r--r--lib/libavtp/examples/aaf-talker.c299
-rw-r--r--lib/libavtp/include/avtp.h106
-rw-r--r--lib/libavtp/include/avtp_aaf.h108
-rw-r--r--lib/libavtp/meson.build67
-rw-r--r--lib/libavtp/src/avtp.c98
-rw-r--r--lib/libavtp/src/avtp_aaf.c312
-rw-r--r--lib/libavtp/src/util.h45
-rw-r--r--lib/libavtp/unit/test-aaf.c575
-rw-r--r--lib/libavtp/unit/test-avtp.c153
-rwxr-xr-xtravis.sh6
57 files changed, 5322 insertions, 540 deletions
diff --git a/.travis.yml b/.travis.yml
index ecc2b9d6..9e599f08 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -27,6 +27,21 @@ install:
- sudo apt-get update -qq
- sudo apt-get install -y libpcap-dev libpci-dev libsndfile1-dev libjack-dev linux-headers-4.4.0-75-generic
- sudo apt-get install -y libgstreamer0.10-dev libgstreamer-plugins-base0.10-dev libasound2-dev
+ - sudo apt-get install -y python3-pip
+ - curl -L "https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip" -o ninja-linux.zip
+ - sudo unzip ninja-linux.zip -d /usr/local/bin
+ - sudo chmod 755 /usr/local/bin/ninja
+ - sudo pip3 install meson==0.44.0
+ - curl -L "https://cmocka.org/files/1.1/cmocka-1.1.1.tar.xz" -o cmocka-1.1.1.tar.xz
+ - tar -xf cmocka-1.1.1.tar.xz
+ - pushd cmocka-1.1.1
+ - mkdir build
+ - cd build
+ - cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug ..
+ - make
+ - sudo make install
+ - popd
+ - sudo cp /usr/src/linux-headers-4.4.0-75/include/uapi/linux/if_ether.h /usr/include/linux
- sudo cp /usr/src/linux-headers-4.4.0-75/include/uapi/linux/ethtool.h /usr/include/linux
- sudo cp /usr/src/linux-headers-4.4.0-75/include/uapi/linux/ptp_clock.h /usr/include/linux
- CMAKE_URL="https://cmake.org/files/v3.9/cmake-3.9.4-Linux-x86_64.tar.gz"
diff --git a/README.rst b/README.rst
index b8fecdf1..f1c29cd4 100644
--- a/README.rst
+++ b/README.rst
@@ -98,6 +98,16 @@ Starting from the OpenAvnu dir, one would go::
to build in OpenAvnu/tmp
+Apple Vendor PTP Profile
+========================
+
+Support for the Apple Vendor PTP Profile can be found on the
+feature-aptp-experimental branch of the OpenAvnu repository.
+
+These changes allow interaction with Apple proprietary PTP clocks. This
+implementation has been tested with the Apple AirPlay SDK on a Raspberry Pi 3
+running within a group of devices playing the same music stream.
+
RELATED OPEN SOURCE PROJECTS
============================
@@ -119,11 +129,3 @@ XMOS
XMOS is a semiconductor company providing a reference design for AVB/TSN
endpoints in pro audio and automotive. XMOS endpoint source code is open source
and available on Github - https://github.com/xcore/sw_avb
-
-Apple Vendor PTP Profile
-------------------------
-Support for the Apple Vendor PTP Profile can be found on this fork of the OpenAvnu code within the branch ArtAndLogic-aPTP-changes.
-
-+ https://github.com/rroussel/OpenAvnu/tree/ArtAndLogic-aPTP-changes
-
-These changes allow interaction with Apple proprietary PTP clocks. This implementation has been tested with the Apple AirPlay SDK on a Raspberry Pi 3 running within a group of devices playing the same music stream.
diff --git a/daemons/gptp/README.rst b/daemons/gptp/README.rst
index f56fd719..1e26543d 100644
--- a/daemons/gptp/README.rst
+++ b/daemons/gptp/README.rst
@@ -94,6 +94,35 @@ To run from the command line:
where xx-xx-xx-xx-xx-xx is the mac address of the local interface
+Windows Wireless Specific
++++++++++++++++++++++++++
+
+Additional Driver/Hardware Requirements:
+
+* Intel(R) 8260 Adapter
+
+* Intel(R) PROSet/Wireless Software
+
+
+The wireless software can be downloaded from:
+
+https://downloadcenter.intel.com/ (Search)
+
+Running the daemon:
+
+Currently, the driver only works with peer-to-peer wireless connections.
+The connection must be established prior to running the daemon.
+
+./gptp.exe -w <local hw device MAC> <local P2P MAC> <remove P2P MAC>
+
+Other limitations:
+
+Some versions of Windows(R) 10 do not allow WinPcap(R) to inject frames and
+the BMCA algorithm can't complete. The result is both peers assume the master
+role. To fix this, force one peer to be slave with the following command line:
+
+./gptp.exe -w -R 255 <local hw device MAC> <local P2P MAC> <remove P2P MAC>
+
Other Available PTP Daemons
---------------------------
There are a number of existing ptp daemon projects. Some of the other known
diff --git a/daemons/gptp/common/avbts_clock.hpp b/daemons/gptp/common/avbts_clock.hpp
index feb8d9d1..614464c2 100644
--- a/daemons/gptp/common/avbts_clock.hpp
+++ b/daemons/gptp/common/avbts_clock.hpp
@@ -550,6 +550,15 @@ public:
FrequencyRatio local_system_freq_offset, unsigned sync_count,
unsigned pdelay_count, PortState port_state, bool asCapable );
+ /**
+ * @brief Get local:system frequency ratio
+ * @return clock ratio
+ */
+ FrequencyRatio getLocalSystemFreqOffset()
+ {
+ return _local_system_freq_offset;
+ }
+
/**
* @brief Get the IEEE1588Clock identity value
* @return clock identity
diff --git a/daemons/gptp/common/avbts_message.hpp b/daemons/gptp/common/avbts_message.hpp
index f18d1213..f5ccd9a1 100644
--- a/daemons/gptp/common/avbts_message.hpp
+++ b/daemons/gptp/common/avbts_message.hpp
@@ -204,6 +204,17 @@ public:
};
/**
+ * @brief Builds PTP message from buffer
+ * @param buf [in] byte buffer containing PTP message
+ * @param size [in] length of buffer in bytes
+ * @param remote [in] address from where message was received
+ * @param port [in] port object that message was recieved on
+ * @return PTP message object
+ */
+PTPMessageCommon *buildPTPMessage
+( char *buf, int size, LinkLayerAddress *remote, CommonPort *port );
+
+/**
* @brief Provides the PTPMessage common interface used during building of
* PTP messages.
*/
@@ -373,10 +384,10 @@ protected:
/**
* @brief Generic interface for processing PTP message
- * @param port IEEE1588 port
+ * @param port CommonPort object
* @return void
*/
- virtual void processMessage( EtherPort *port );
+ virtual void processMessage( CommonPort *port );
/**
* @brief Builds PTP common header
@@ -386,8 +397,7 @@ protected:
void buildCommonHeader(uint8_t * buf);
friend PTPMessageCommon *buildPTPMessage
- ( char *buf, int size, LinkLayerAddress * remote,
- EtherPort *port );
+ ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port );
};
/*Exact fit. No padding*/
@@ -589,12 +599,7 @@ class PTPMessageAnnounce:public PTPMessageCommon {
return ret;
}
- /**
- * @brief Processes PTP message
- * @param port EtherPort
- * @return void
- */
- void processMessage( EtherPort *port );
+ void processMessage( CommonPort *port );
/**
* @brief Assembles PTPMessageAnnounce message on the
@@ -608,8 +613,7 @@ class PTPMessageAnnounce:public PTPMessageCommon {
( CommonPort *port, PortIdentity *destIdentity);
friend PTPMessageCommon *buildPTPMessage
- ( char *buf, int size, LinkLayerAddress *remote,
- EtherPort *port );
+ ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port );
};
/**
@@ -632,12 +636,7 @@ class PTPMessageSync : public PTPMessageCommon {
*/
~PTPMessageSync();
- /**
- * @brief Processes PTP messages
- * @param port [in] EtherPort
- * @return void
- */
- void processMessage( EtherPort *port );
+ void processMessage( CommonPort *port );
/**
* @brief Gets origin timestamp value
@@ -659,8 +658,7 @@ class PTPMessageSync : public PTPMessageCommon {
(EtherPort *port, PortIdentity *destIdentity );
friend PTPMessageCommon *buildPTPMessage
- ( char *buf, int size, LinkLayerAddress * remote,
- EtherPort *port );
+ ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port );
};
/* Exact fit. No padding*/
@@ -866,7 +864,15 @@ public:
/**
* @brief Builds the PTPMessageFollowUP object
*/
- PTPMessageFollowUp( EtherPort *port );
+ PTPMessageFollowUp( CommonPort *port );
+
+ /**
+ * @brief write followup message into buffer
+ * @param port [in] associated CommonPort object
+ * @param buf_ptr [out] buffer to write data to
+ * @return number of bytes written to buffer
+ */
+ size_t buildMessage(CommonPort *port, uint8_t *buf_ptr);
/**
* @brief Assembles PTPMessageFollowUp message on the
@@ -879,12 +885,16 @@ public:
bool sendPort
( EtherPort *port, PortIdentity *destIdentity );
+ void processMessage( CommonPort *port );
+
/**
- * @brief Processes PTP messages
- * @param port [in] EtherPort
- * @return void
- */
- void processMessage( EtherPort *port );
+ * @brief Processes PTP messages
+ * @param port [in] CommonPort
+ * @param receipt [in] local time message was received
+ * @param delay
+ * @return void
+ */
+ void processMessage( CommonPort *port, Timestamp receipt );
/**
* @brief Gets the precise origin timestamp value
@@ -916,8 +926,7 @@ public:
}
friend PTPMessageCommon *buildPTPMessage
- ( char *buf, int size, LinkLayerAddress *remote,
- EtherPort *port );
+ ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port );
};
/**
@@ -953,12 +962,7 @@ class PTPMessagePathDelayReq : public PTPMessageCommon {
bool sendPort
( EtherPort *port, PortIdentity *destIdentity );
- /**
- * @brief Processes PTP messages
- * @param port [in] EtherPort
- * @return void
- */
- void processMessage( EtherPort *port );
+ void processMessage( CommonPort *port );
/**
* @brief Gets origin timestamp value
@@ -969,8 +973,7 @@ class PTPMessagePathDelayReq : public PTPMessageCommon {
}
friend PTPMessageCommon *buildPTPMessage
- ( char *buf, int size, LinkLayerAddress *remote,
- EtherPort *port );
+ ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port );
};
/**
@@ -1004,12 +1007,7 @@ public:
bool sendPort
( EtherPort *port, PortIdentity *destIdentity );
- /**
- * @brief Processes PTP messages
- * @param port [in] EtherPort
- * @return void
- */
- void processMessage( EtherPort *port );
+ void processMessage( CommonPort *port );
/**
* @brief Sets the request receipt timestamp
@@ -1042,8 +1040,7 @@ public:
}
friend PTPMessageCommon *buildPTPMessage
- ( char *buf, int size, LinkLayerAddress *remote,
- EtherPort *port );
+ ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port );
};
/**
@@ -1078,12 +1075,7 @@ public:
bool sendPort
( EtherPort *port, PortIdentity *destIdentity );
- /**
- * @brief Processes PTP messages
- * @param port [in] EtherPort
- * @return void
- */
- void processMessage( EtherPort *port );
+ void processMessage( CommonPort *port );
/**
* @brief Sets the response origin timestamp
@@ -1116,8 +1108,7 @@ public:
}
friend PTPMessageCommon *buildPTPMessage
- ( char *buf, int size, LinkLayerAddress *remote,
- EtherPort *port );
+ ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port );
};
/*Exact fit. No padding*/
@@ -1267,16 +1258,10 @@ public:
bool sendPort
( EtherPort *port, PortIdentity *destIdentity );
- /**
- * @brief Processes PTP messages
- * @param port [in] EtherPort
- * @return void
- */
- void processMessage( EtherPort *port );
+ void processMessage( CommonPort *port );
friend PTPMessageCommon *buildPTPMessage
- ( char *buf, int size, LinkLayerAddress *remote,
- EtherPort * port);
+ ( char *buf, int size, LinkLayerAddress *remote, CommonPort *port );
};
#endif
diff --git a/daemons/gptp/common/avbts_osipc.hpp b/daemons/gptp/common/avbts_osipc.hpp
index 00bac5dd..2adb3dbd 100644
--- a/daemons/gptp/common/avbts_osipc.hpp
+++ b/daemons/gptp/common/avbts_osipc.hpp
@@ -36,7 +36,6 @@
#include <stdint.h>
#include <ptptypes.hpp>
-#include <ether_port.hpp>
/**@file*/
diff --git a/daemons/gptp/common/common_port.cpp b/daemons/gptp/common/common_port.cpp
index d8ba54f6..268bc62d 100644
--- a/daemons/gptp/common/common_port.cpp
+++ b/daemons/gptp/common/common_port.cpp
@@ -61,9 +61,11 @@ CommonPort::CommonPort( PortInit_t *portInit ) :
port_state = PTP_INITIALIZING;
clock->registerPort(this, ifindex);
qualified_announce = NULL;
+ automotive_profile = portInit->automotive_profile;
announce_sequence_id = 0;
signal_sequence_id = 0;
sync_sequence_id = 0;
+ initialLogPdelayReqInterval = portInit->initialLogPdelayReqInterval;
initialLogSyncInterval = portInit->initialLogSyncInterval;
log_mean_announce_interval = 0;
pdelay_count = 0;
@@ -563,7 +565,7 @@ bool CommonPort::processEvent( Event e )
else
{
clock->addEventTimerLocked(this, ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES,
- ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER * pow(2.0, getAnnounceInterval()) * 1000000000.0);
+ (uint64_t) ( ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER * pow(2.0, getAnnounceInterval()) * 1000000000.0 ));
}
// Do any media specific initialization
@@ -732,6 +734,11 @@ bool CommonPort::adjustClockPhase( int64_t phase_adjust )
return false;
}
+FrequencyRatio CommonPort::getLocalSystemFreqOffset()
+{
+ return clock->getLocalSystemFreqOffset();
+}
+
Timestamp CommonPort::getTxPhyDelay( uint32_t link_speed ) const
{
if( phy_delay->count( link_speed ) != 0 )
diff --git a/daemons/gptp/common/common_port.hpp b/daemons/gptp/common/common_port.hpp
index d5d0adda..4f195836 100644
--- a/daemons/gptp/common/common_port.hpp
+++ b/daemons/gptp/common/common_port.hpp
@@ -45,6 +45,10 @@
#include <math.h>
+#define SYNC_RECEIPT_TIMEOUT_MULTIPLIER 3 /*!< Sync rcpt timeout multiplier */
+#define ANNOUNCE_RECEIPT_TIMEOUT_MULTIPLIER 3 /*!< Annc rcpt timeout mult */
+#define LOG2_INTERVAL_INVALID -127 /* Invalid Log base 2 interval value */
+
class IEEE1588Clock;
/**
@@ -214,6 +218,9 @@ typedef struct {
/* net_label Network label */
InterfaceLabel *net_label;
+ /* Virtual Network label (e.g. WiFi Direct network MAC) */
+ InterfaceLabel *virtual_label;
+
/* automotive_profile set the AVnu automotive profile */
bool automotive_profile;
@@ -313,6 +320,7 @@ private:
PortState port_state;
bool testMode;
+ bool automotive_profile;
signed char log_mean_sync_interval;
signed char log_mean_announce_interval;
@@ -336,6 +344,9 @@ private:
* received as slave */
unsigned pdelay_count;
+ signed char initialLogPdelayReqInterval;
+ signed char log_min_mean_pdelay_req_interval;
+
PTPMessageAnnounce *qualified_announce;
uint16_t announce_sequence_id;
@@ -436,6 +447,13 @@ public:
}
/**
+ * @brief Return frequency offset between local timestamp clock
+ * system clock
+ * @return local:system ratio
+ */
+ FrequencyRatio getLocalSystemFreqOffset();
+
+ /**
* @brief Gets a pointer to IEEE1588Clock
* @return Pointer to clock
*/
@@ -1115,6 +1133,63 @@ public:
virtual void becomeSlave( bool restart_syntonization ) = 0;
/**
+ * @brief Gets the AVnu automotive profile flag
+ * @return automotive_profile flag
+ */
+ bool getAutomotiveProfile() { return(automotive_profile); }
+
+ /**
+ * @brief Sets the pDelay minimum interval
+ * @param val time interval
+ * @return none
+ */
+ void setPDelayInterval(signed char val) {
+ log_min_mean_pdelay_req_interval = val;
+ }
+
+ /**
+ * @brief Gets the pDelay minimum interval
+ * @return PDelay interval
+ */
+ signed char getPDelayInterval(void) {
+ return log_min_mean_pdelay_req_interval;
+ }
+
+ /**
+ * @brief Sets the pDelay minimum interval back to initial
+ * value
+ * @return none
+ */
+ void resetInitPDelayInterval(void) {
+ log_min_mean_pdelay_req_interval = initialLogPdelayReqInterval;
+ }
+
+ /**
+ * @brief set initial pdelay interval
+ * @param interval [in] log base 2 pdelay rate
+ */
+ void setInitPDelayInterval( int8_t interval )
+ {
+ initialLogPdelayReqInterval = interval;
+ }
+
+ /**
+ * @brief get initial pdelay interval
+ * @return log base 2 pdelay rate
+ */
+ int8_t getInitPDelayInterval(void)
+ {
+ return initialLogPdelayReqInterval;
+ }
+
+ /**
+ * @brief Start pDelay interval timer
+ * @param waitTime time interval
+ * @return none
+ */
+ virtual void startPDelayIntervalTimer( unsigned long long waitTime ) {}
+
+ /**
* @brief Sets current sync count value.
* @param cnt [in] sync count value
* @return void
@@ -1336,20 +1411,24 @@ public:
*/
virtual bool _processEvent( Event e ) = 0;
+ /**
+ * @brief Performs media specific setup after start sync is completed
+ * @return void
+ */
virtual void syncDone() = 0;
/**
- * @brief Sends a general message to a port. No timestamps
- * @param buf [in] Pointer to the data buffer
- * @param len Size of the message
- * @param mcast_type Enumeration
- * MulticastType (pdelay, none or other). Depracated.
- * @param destIdentity Destination port identity
- * @return void
- */
+ * @brief Sends a general message to a port. No timestamps
+ * @param buf [in] Pointer to the data buffer
+ * @param len Size of the message
+ * @param mcast_type Enumeration
+ * MulticastType (pdelay, none or other). Depracated.
+ * @param destIdentity Destination port identity
+ * @return void
+ */
virtual void sendGeneralPort
(uint16_t etherType, uint8_t * buf, int len, MulticastType mcast_type,
- PortIdentity * destIdentity) = 0;
+ PortIdentity * destIdentity) = 0;
/**
* @brief Sets link speed
diff --git a/daemons/gptp/common/ether_port.cpp b/daemons/gptp/common/ether_port.cpp
index 2325fb99..56db3d22 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,13 +169,13 @@ bool EtherPort::_init_port( void )
void EtherPort::startPDelay()
{
if(!pdelayHalted()) {
- if (automotive_profile) {
- if (log_min_mean_pdelay_req_interval != PTPMessageSignalling::sigMsgInterval_NoSend) {
- long long unsigned int waitTime;
- waitTime = ((long long) (pow((double)2, log_min_mean_pdelay_req_interval) * 1000000000.0));
- waitTime = waitTime > EVENT_TIMER_GRANULARITY ? waitTime : EVENT_TIMER_GRANULARITY;
+ if( getAutomotiveProfile( ))
+ {
+ if( getPDelayInterval() !=
+ PTPMessageSignalling::sigMsgInterval_NoSend)
+ {
pdelay_started = true;
- startPDelayIntervalTimer(waitTime);
+ startPDelayIntervalTimer(EVENT_TIMER_GRANULARITY);
}
}
else {
@@ -194,7 +194,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 +326,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 +357,8 @@ bool EtherPort::_processEvent( Event e )
port_ready_condition->wait();
- if (automotive_profile) {
+ if( getAutomotiveProfile( ))
+ {
setStationState(STATION_STATE_ETHERNET_READY);
if (getTestMode())
{
@@ -387,7 +390,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 +399,8 @@ bool EtherPort::_processEvent( Event e )
case LINKUP:
haltPdelay(false);
startPDelay();
- if (automotive_profile) {
+ if( getAutomotiveProfile( ))
+ {
GPTP_LOG_EXCEPTION("LINKUP");
}
else {
@@ -408,11 +412,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 +436,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 +477,8 @@ bool EtherPort::_processEvent( Event e )
break;
case LINKDOWN:
stopPDelay();
- if (automotive_profile) {
+ if( getAutomotiveProfile( ))
+ {
GPTP_LOG_EXCEPTION("LINK DOWN");
}
else {
@@ -484,7 +494,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 +578,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 +628,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 +648,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 +680,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 +691,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 +733,8 @@ void EtherPort::becomeMaster( bool annc ) {
stopSyncReceiptTimer();
if( annc ) {
- if (!automotive_profile) {
+ if( !getAutomotiveProfile( ))
+ {
startAnnounce();
}
}
@@ -738,7 +752,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 +847,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 +865,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/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..ab312c95 100644
--- a/daemons/gptp/linux/src/linux_hal_common.cpp
+++ b/daemons/gptp/linux/src/linux_hal_common.cpp
@@ -428,14 +428,19 @@ void *LinuxTimerQueueHandler( void *arg ) {
timeout.tv_sec = 0; timeout.tv_nsec = 100000000; /* 100 ms */
sigemptyset( &waitfor );
-
+ GPTP_LOG_DEBUG("Signal thread started");
while( !timerq->stop ) {
siginfo_t info;
LinuxTimerQueueMap_t::iterator iter;
sigaddset( &waitfor, SIGUSR1 );
if( sigtimedwait( &waitfor, &info, &timeout ) == -1 ) {
- if( errno == EAGAIN ) continue;
- else break;
+ if( errno == EAGAIN ) {
+ continue;
+ }
+ else {
+ GPTP_LOG_ERROR("Signal thread sigtimedwait error: %d", errno);
+ break;
+ }
}
if( timerq->lock->lock() != oslock_ok ) {
break;
@@ -456,7 +461,7 @@ void *LinuxTimerQueueHandler( void *arg ) {
break;
}
}
-
+ GPTP_LOG_DEBUG("Signal thread exit");
return NULL;
}
@@ -474,6 +479,9 @@ OSTimerQueue *LinuxTimerQueueFactory::createOSTimerQueue
return NULL;
}
+ ret->key = 0;
+ ret->stop = false;
+ ret->lock = clock->timerQLock();
if( pthread_create
( &(ret->_private->signal_thread),
NULL, LinuxTimerQueueHandler, ret ) != 0 ) {
@@ -481,10 +489,6 @@ OSTimerQueue *LinuxTimerQueueFactory::createOSTimerQueue
return NULL;
}
- ret->stop = false;
- ret->key = 0;
- ret->lock = clock->timerQLock();
-
return ret;
}
@@ -528,9 +532,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/windows/daemon_cl/daemon_cl.cpp b/daemons/gptp/windows/daemon_cl/daemon_cl.cpp
index 5bb2f3c5..8da3ddfa 100644
--- a/daemons/gptp/windows/daemon_cl/daemon_cl.cpp
+++ b/daemons/gptp/windows/daemon_cl/daemon_cl.cpp
@@ -44,6 +44,10 @@ POSSIBILITY OF SUCH DAMAGE.
#include <tchar.h>
#include <iphlpapi.h>
+#include <ether_port.hpp>
+#include <wireless_port.hpp>
+#include <intel_wireless.hpp>
+
/* Generic PCH delays */
#define PHY_DELAY_GB_TX_PCH 7750 //1G delay
#define PHY_DELAY_GB_RX_PCH 7750 //1G delay
@@ -125,9 +129,11 @@ int _tmain(int argc, _TCHAR* argv[])
CommonPort::NEIGHBOR_PROP_DELAY_THRESH;
bool syntonize = false;
+ bool wireless = false;
uint8_t priority1 = 248;
int i;
int phy_delays[4] = { -1, -1, -1, -1 };
+ uint8_t addr_ostr[ETHER_ADDR_OCTETS];
// Register default network interface
WindowsPCAPNetworkInterfaceFactory *default_factory = new WindowsPCAPNetworkInterfaceFactory();
@@ -140,6 +146,8 @@ int _tmain(int argc, _TCHAR* argv[])
portInit.condition_factory = new WindowsConditionFactory();
WindowsNamedPipeIPC *ipc = new WindowsNamedPipeIPC();
WindowsTimerQueueFactory *timerq_factory = new WindowsTimerQueueFactory();
+ CommonPort *port;
+ WindowsWirelessAdapter *wl_adapter = NULL;
if( !ipc->init() ) {
delete ipc;
@@ -155,49 +163,106 @@ int _tmain(int argc, _TCHAR* argv[])
/* Process optional arguments */
for( i = 1; i < argc; ++i ) {
- if( ispunct(argv[i][0]) ) {
- if( toupper( argv[i][1] ) == 'H' ) {
- print_usage( argv[0] );
+ if (ispunct(argv[i][0]))
+ {
+ if (toupper(argv[i][1]) == 'H') {
+ print_usage(argv[0]);
return -1;
}
- else if( toupper( argv[i][1] ) == 'R' ) {
- if( i+1 >= argc ) {
- printf( "Priority 1 value must be specified on "
- "command line, using default value\n" );
- } else {
- unsigned long tmp = strtoul( argv[i+1], NULL, 0 ); ++i;
- if( tmp > 254 ) {
- printf( "Invalid priority 1 value, using "
- "default value\n" );
- } else {
- priority1 = (uint8_t) tmp;
+ if (toupper(argv[i][1]) == 'W')
+ {
+ wireless = true;
+ }
+ else if (toupper(argv[i][1]) == 'R') {
+ if (i + 1 >= argc) {
+ printf("Priority 1 value must be specified on "
+ "command line, using default value\n");
+ }
+ else {
+ unsigned long tmp = strtoul(argv[i + 1], NULL, 0); ++i;
+ if (tmp > 255) {
+ printf("Invalid priority 1 value, using "
+ "default value\n");
+ }
+ else {
+ priority1 = (uint8_t)tmp;
}
}
}
+ } else
+ {
+ break;
}
}
- // the last argument is supposed to be a MAC address, so decrement argv index to read it
- i--;
+ // Parse local HW MAC address
+ if (i < argc)
+ {
+ parseMacAddr(argv[i++], addr_ostr);
+ portInit.net_label = new LinkLayerAddress(addr_ostr);
+ } else
+ {
+ printf("Local hardware MAC address required");
+ return -1;
+ }
+
+ if( wireless )
+ {
+ if (i < argc)
+ {
+ parseMacAddr(argv[i++], addr_ostr);
+ portInit.virtual_label = new LinkLayerAddress(addr_ostr);
+ } else
+ {
+ printf("Wireless operation requires local virtual MAC address");
+ return -1;
+ }
+ }
+
+ if (!wireless)
+ {
+ // Create HWTimestamper object
+ portInit.timestamper = new WindowsEtherTimestamper();
+ } else
+ {
+ portInit.timestamper = new WindowsWirelessTimestamper();
+ (static_cast<WindowsWirelessTimestamper *> (portInit.timestamper))->setAdapter(new IntelWirelessAdapter());
+ }
- // Create Low level network interface object
- uint8_t local_addr_ostr[ETHER_ADDR_OCTETS];
- parseMacAddr( argv[i], local_addr_ostr );
- LinkLayerAddress local_addr(local_addr_ostr);
- portInit.net_label = &local_addr;
- // Create HWTimestamper object
- portInit.timestamper = new WindowsTimestamper();
// Create Clock object
- portInit.clock = new IEEE1588Clock( false, false, priority1, timerq_factory, ipc, portInit.lock_factory ); // Do not force slave
- // Create Port Object linked to clock and low level
- portInit.phy_delay = &ether_phy_delay;
- EtherPort *port = new EtherPort( &portInit );
- port->setLinkSpeed(findLinkSpeed(&local_addr));
- if ( !port->init_port() ) {
- printf( "Failed to initialize port\n" );
- return -1;
+ portInit.clock = new IEEE1588Clock(false, false, priority1, timerq_factory, ipc, portInit.lock_factory); // Do not force slave
+
+ if (!wireless)
+ {
+ // Create Port Object linked to clock and low level
+ portInit.phy_delay = &ether_phy_delay;
+ EtherPort *eport = new EtherPort(&portInit);
+ eport->setLinkSpeed( findLinkSpeed( static_cast <LinkLayerAddress *> ( portInit.net_label )));
+ port = eport;
+ if (!eport->init_port()) {
+ printf("Failed to initialize port\n");
+ return -1;
+ }
+ port->processEvent(POWERUP);
+ } else
+ {
+ if (i < argc)
+ {
+ parseMacAddr(argv[i++], addr_ostr);
+ LinkLayerAddress peer_addr(addr_ostr);
+ port = new WirelessPort(&portInit, peer_addr);
+ (static_cast <WirelessTimestamper *> (portInit.timestamper))->setPort( static_cast<WirelessPort *> ( port ));
+ if (!port->init_port()) {
+ printf("Failed to initialize port\n");
+ return -1;
+ }
+ port->processEvent(POWERUP);
+ } else
+ {
+ printf("Wireless operation requires remote MAC address");
+ return -1;
+ }
}
- port->processEvent( POWERUP );
// Wait for Ctrl-C
if( !SetConsoleCtrlHandler( ctrl_handler, true )) {
diff --git a/daemons/gptp/windows/daemon_cl/intel_wireless.cpp b/daemons/gptp/windows/daemon_cl/intel_wireless.cpp
new file mode 100644
index 00000000..a6f8f99f
--- /dev/null
+++ b/daemons/gptp/windows/daemon_cl/intel_wireless.cpp
@@ -0,0 +1,424 @@
+/******************************************************************************
+
+Copyright (c) 2009-2015, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the Intel Corporation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#include <intel_wireless.hpp>
+#include <windows_hal.hpp>
+#include <work_queue.hpp>
+#include <memory>
+#include <map>
+
+#define INTEL_EVENT_OFFSET 0x1141
+#define GP2_ROLLOVER 4294967296ULL
+
+GetAdapterList_t GetAdapterList;
+WifiCmdTimingMeasurementRequest_t WifiCmdTimingMeasurementRequest;
+WifiCmdTimingPtmWa_t WifiCmdTimingPtmWa;
+RegisterIntelCallback_t RegisterIntelCallback;
+DeregisterIntelCallback_t DeregisterIntelCallback;
+
+struct TimestamperContext
+{
+ WindowsWorkQueue work_queue;
+ WindowsWirelessTimestamper *timestamper;
+ ~TimestamperContext()
+ {
+ work_queue.stop();
+ }
+};
+
+typedef std::map< LinkLayerAddress, TimestamperContext > TimestamperContextMap;
+
+class LockedTimestamperContextMap
+{
+public:
+ LockedTimestamperContextMap()
+ {
+ InitializeSRWLock(&lock);
+ }
+ TimestamperContextMap map;
+ SRWLOCK lock;
+};
+
+LockedTimestamperContextMap timestamperContextMap;
+HANDLE IntelTimestamperThread;
+bool IntelTimestamperThreadStop = false;
+
+void IntelWirelessTimestamperEventHandler( INTEL_EVENT iEvent, void *pContext )
+{
+ WindowsWirelessTimestamper *timestamper;
+ IntelWirelessAdapter *adapter;
+ LockedTimestamperContextMap *timestamperContextMap =
+ (LockedTimestamperContextMap *)pContext;
+ WirelessTimestamperCallbackArg *arg =
+ new WirelessTimestamperCallbackArg();
+ int vendor_extension_copy_length;
+ bool error = false;
+ bool submit = false; // If true submit the event for later processing
+
+ // We share driver callback with other events, subtract the offset for
+ // timing measurement related events and check this is time sync event
+ iEvent.eType =
+ (WIRELESS_EVENT_TYPE) (iEvent.eType - INTEL_EVENT_OFFSET);
+ switch( iEvent.eType )
+ {
+ default:
+ return;
+ case TIMINGMSMT_CONFIRM_EVENT:
+ case TIMINGMSMT_EVENT:
+ case TIMINGMSMT_CORRELATEDTIME_EVENT:
+ break;
+ }
+
+ LinkLayerAddress event_source( iEvent.BtMacAddress );
+ arg->iEvent_type = (WIRELESS_EVENT_TYPE) iEvent.eType;
+ AcquireSRWLockShared( &timestamperContextMap->lock );
+ if( timestamperContextMap->map.find( event_source )
+ != timestamperContextMap->map.cend())
+ {
+ arg->timestamper =
+ timestamperContextMap->map[event_source].timestamper;
+ } else
+ {
+ goto bail;
+ }
+
+ timestamper = dynamic_cast <WindowsWirelessTimestamper *>
+ (arg->timestamper);
+ if( timestamper == NULL )
+ {
+ GPTP_LOG_ERROR( "Unexpected timestamper type encountered" );
+ goto bail;
+ }
+ adapter = dynamic_cast <IntelWirelessAdapter *>
+ (timestamper->getAdapter( ));
+ if( adapter == NULL )
+ {
+ GPTP_LOG_ERROR( "Unexpected adapter type encountered" );
+ goto bail;
+ }
+
+ // The driver implementation makes no guarantee of the lifetime of the
+ // iEvent object we make a copy and delete the copy when processing is
+ // complete
+ switch( arg->iEvent_type )
+ {
+ case TIMINGMSMT_CONFIRM_EVENT:
+ arg->event_data.confirm =
+ *(TIMINGMSMT_CONFIRM_EVENT_DATA *)iEvent.data;
+ arg->event_data.confirm.T1 =
+ adapter->calc_rollover( arg->event_data.confirm.T1 );
+ arg->event_data.confirm.T4 =
+ adapter->calc_rollover( arg->event_data.confirm.T4 );
+ submit = true;
+
+ break;
+
+ case TIMINGMSMT_EVENT:
+ arg->event_data.indication.indication =
+ *(TIMINGMSMT_EVENT_DATA *)iEvent.data;
+ arg->event_data.indication.indication.T2 =
+ adapter->calc_rollover
+ ( arg->event_data.indication.indication.T2/100 );
+ arg->event_data.indication.indication.T3 =
+ adapter->calc_rollover
+ ( arg->event_data.indication.indication.T3/100 );
+ // Calculate copy length, at most followup message
+ // (See S8021AS indication)
+ vendor_extension_copy_length = arg->event_data.
+ indication.indication.WiFiVSpecHdr.Length - 1;
+ vendor_extension_copy_length -= vendor_extension_copy_length -
+ sizeof(arg->event_data.indication.followup) > 0 ?
+ vendor_extension_copy_length -
+ sizeof(arg->event_data.indication.followup) : 0;
+ // Copy the rest of the vendor specific field
+ memcpy( arg->event_data.indication.followup,
+ ((BYTE *)iEvent.data) +
+ sizeof( arg->event_data.indication.indication ),
+ vendor_extension_copy_length );
+ submit = true;
+ break;
+ case TIMINGMSMT_CORRELATEDTIME_EVENT:
+ AcquireSRWLockExclusive(&adapter->ct_lock);
+ if (adapter->ct_miss > 0)
+ {
+ --adapter->ct_miss;
+ } else
+ {
+ arg->event_data.ptm_wa =
+ *(WIRELESS_CORRELATEDTIME *)iEvent.data;
+ arg->event_data.ptm_wa.LocalClk =
+ adapter->calc_rollover
+ (arg->event_data.ptm_wa.LocalClk);
+ adapter->ct_done = true;
+ // No need to schedule this, it returns immediately
+ WirelessTimestamperCallback(arg);
+ WakeAllConditionVariable(&adapter->ct_cond);
+ }
+ ReleaseSRWLockExclusive(&adapter->ct_lock);
+
+ break;
+ }
+
+ if (submit &&
+ !timestamperContextMap->map[event_source].work_queue.submit
+ ( WirelessTimestamperCallback, arg ))
+ GPTP_LOG_ERROR("Failed to submit WiFi event");
+bail:
+ ReleaseSRWLockShared(&timestamperContextMap->lock);
+}
+
+DWORD WINAPI IntelWirelessLoop(LPVOID arg)
+{
+ // Register for callback
+ INTEL_CALLBACK tempCallback;
+ tempCallback.fnIntelCallback = IntelWirelessTimestamperEventHandler;
+ tempCallback.pContext = arg;
+
+ if (RegisterIntelCallback(&tempCallback) != S_OK) {
+ GPTP_LOG_ERROR( "Failed to register WiFi callback" );
+ return 0;
+ }
+
+ while (!IntelTimestamperThreadStop)
+ {
+ SleepEx(320, true);
+ }
+
+ DeregisterIntelCallback(tempCallback.fnIntelCallback);
+
+ return 0;
+}
+
+bool IntelWirelessAdapter::initialize(void)
+{
+ HMODULE MurocApiDLL = LoadLibrary("MurocApi.dll");
+
+ GetAdapterList = (GetAdapterList_t)
+ GetProcAddress(MurocApiDLL, "GetAdapterList");
+ if( GetAdapterList == NULL )
+ return false;
+
+ RegisterIntelCallback = (RegisterIntelCallback_t)
+ GetProcAddress(MurocApiDLL, "RegisterIntelCallback");
+ if( RegisterIntelCallback == NULL )
+ return false;
+
+ DeregisterIntelCallback = (DeregisterIntelCallback_t)
+ GetProcAddress(MurocApiDLL, "DeregisterIntelCallback");
+ if( DeregisterIntelCallback == NULL )
+ return false;
+
+ WifiCmdTimingPtmWa = (WifiCmdTimingPtmWa_t)
+ GetProcAddress(MurocApiDLL, "WifiCmdTimingPtmWa");
+ if( WifiCmdTimingPtmWa == NULL )
+ return false;
+
+ WifiCmdTimingMeasurementRequest = (WifiCmdTimingMeasurementRequest_t)
+ GetProcAddress(MurocApiDLL, "WifiCmdTimingMeasurementRequest");
+ if (WifiCmdTimingMeasurementRequest == NULL)
+ return false;
+
+ // Initialize crosstimestamp condition
+ InitializeConditionVariable(&ct_cond);
+ InitializeSRWLock(&ct_lock);
+ ct_miss = 0;
+
+ // Spawn thread
+ IntelTimestamperThread = CreateThread
+ ( NULL, 0, IntelWirelessLoop, &timestamperContextMap, 0, NULL);
+ return true;
+}
+
+void IntelWirelessAdapter::shutdown(void) {
+ // Signal thread exit
+ IntelTimestamperThreadStop = true;
+ // Join thread
+ WaitForSingleObject(IntelTimestamperThread, INFINITE);
+}
+
+bool IntelWirelessAdapter::refreshCrossTimestamp(void) {
+ INTEL_WIFI_HEADER header;
+ WIRELESS_CORRELATEDTIME ptm_wa;
+ DWORD wait_return = TRUE;
+ DWORD start = 0;
+
+ AcquireSRWLockExclusive(&ct_lock);
+ ct_done = false;
+ header.dwSize = sizeof(ptm_wa);
+ header.dwStructVersion = INTEL_STRUCT_VER_LATEST;
+ HRESULT hres = WifiCmdTimingPtmWa(hAdapter, &header, &ptm_wa);
+ if (hres != S_OK)
+ return false;
+
+ while (!ct_done && wait_return == TRUE) {
+ DWORD now = GetTickCount();
+ start = start == 0 ? now : start;
+ DWORD wait = now - start < CORRELATEDTIME_TRANSACTION_TIMEOUT ?
+ CORRELATEDTIME_TRANSACTION_TIMEOUT -
+ (now - start) : 0;
+ wait_return = SleepConditionVariableSRW
+ ( &ct_cond, &ct_lock, wait, 0 );
+ }
+ ct_miss += wait_return != TRUE ? 1 : 0;
+ ReleaseSRWLockExclusive(&ct_lock);
+
+ return wait_return == TRUE ? true : false;
+}
+
+bool IntelWirelessAdapter::initiateTimingRequest
+( TIMINGMSMT_REQUEST *tm_request )
+{
+ INTEL_WIFI_HEADER header;
+ HRESULT err;
+
+ memset(&header, 0, sizeof(header));
+ // Proset wants an equivalent, but slightly different struct definition
+ header.dwSize = sizeof(*tm_request) - sizeof(PTP_SPEC) + 1;
+ header.dwStructVersion = INTEL_STRUCT_VER_LATEST;
+ if(( err = WifiCmdTimingMeasurementRequest
+ (hAdapter, &header, tm_request)) != S_OK )
+ return false;
+
+ return true;
+}
+
+// Find Intel adapter given MAC address and return adapter ID
+//
+// @adapter(out): Adapter identifier used for all driver interaction
+// @mac_addr: HW address of the adapter
+// @return: *false* if adapter is not found or driver communication failure
+// occurs, otherwise *true*
+//
+bool IntelWirelessAdapter::attachAdapter(uint8_t *mac_addr)
+{
+ PINTEL_ADAPTER_LIST adapter_list;
+ int i;
+
+ if (GetAdapterList(&adapter_list) != S_OK)
+ return false;
+
+ GPTP_LOG_VERBOSE("Driver query returned %d adapters\n",
+ adapter_list->count);
+ for (i = 0; i < adapter_list->count; ++i) {
+ if( memcmp(adapter_list->adapter[i].btMACAddress,
+ mac_addr, ETHER_ADDR_OCTETS) == 0 )
+ break;
+ }
+
+ if (i == adapter_list->count)
+ {
+ GPTP_LOG_ERROR("Driver failed to find the requested adapter");
+ return false;
+ }
+ hAdapter = adapter_list->adapter[i].hAdapter;
+
+ return true;
+}
+
+// Register timestamper with Intel wireless subsystem.
+//
+// @timestamper: timestamper object that should receive events
+// @source: MAC address of local interface
+// @return: *false* if the MAC address has already been registered, *true* if
+//
+bool IntelWirelessAdapter::registerTimestamper
+(WindowsWirelessTimestamper *timestamper)
+{
+ bool ret = false;
+
+ AcquireSRWLockExclusive(&timestamperContextMap.lock);
+ // Do not "re-add" the same timestamper
+ if( timestamperContextMap.map.find
+ ( *timestamper->getPort()->getLocalAddr() )
+ == timestamperContextMap.map.cend())
+ {
+ // Initialize per timestamper context and add
+ timestamperContextMap.map
+ [*timestamper->getPort()->getLocalAddr()].
+ work_queue.init(0);
+ timestamperContextMap.map
+ [*timestamper->getPort()->getLocalAddr()].
+ timestamper = timestamper;
+ ret = true;
+ }
+ ReleaseSRWLockExclusive(&timestamperContextMap.lock);
+
+ return ret;
+}
+
+bool IntelWirelessAdapter::deregisterTimestamper
+( WindowsWirelessTimestamper *timestamper )
+{
+ bool ret;
+
+ TimestamperContextMap::iterator iter;
+ AcquireSRWLockExclusive(&timestamperContextMap.lock);
+ // Find timestamper
+ iter = timestamperContextMap.map.find
+ ( *timestamper->getPort()->getLocalAddr( ));
+ if( iter != timestamperContextMap.map.end( ))
+ {
+ // Shutdown work queue
+ iter->second.work_queue.stop();
+ // Remove timestamper from list
+ timestamperContextMap.map.erase(iter);
+ ret = true;
+ }
+ ReleaseSRWLockExclusive(&timestamperContextMap.lock);
+
+ return ret;
+}
+
+uint64_t IntelWirelessAdapter::calc_rollover( uint64_t gp2 )
+{
+ gp2 = gp2 & 0xFFFFFFFF;
+ uint64_t l_gp2_ext;
+
+ if (gp2_last == ULLONG_MAX)
+ {
+ gp2_last = gp2;
+
+ return gp2;
+ }
+
+ if (gp2 < GP2_ROLLOVER/4 && gp2_last > (GP2_ROLLOVER*3)/4)
+ ++gp2_ext;
+
+ l_gp2_ext = gp2_ext;
+ if( gp2_last < GP2_ROLLOVER / 4 && gp2 >(GP2_ROLLOVER * 3) / 4 )
+ --l_gp2_ext;
+ else
+ gp2_last = gp2;
+
+ return gp2 + ( l_gp2_ext * GP2_ROLLOVER );
+}
diff --git a/daemons/gptp/windows/daemon_cl/intel_wireless.hpp b/daemons/gptp/windows/daemon_cl/intel_wireless.hpp
new file mode 100644
index 00000000..bad97d71
--- /dev/null
+++ b/daemons/gptp/windows/daemon_cl/intel_wireless.hpp
@@ -0,0 +1,187 @@
+/******************************************************************************
+
+Copyright (c) 2009-2015, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the Intel Corporation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#ifndef INTEL_WIRELESS_HPP
+#define INTEL_WIRELESS_HPP
+
+#include <windows_hal.hpp>
+#include <Windows.h>
+#include <stdint.h>
+
+#define INTEL_MAC_ADDR_LENGTH 6
+#define INTEL_MAX_ADAPTERS 20
+#define CORRELATEDTIME_TRANSACTION_TIMEOUT 110/*ms*/
+
+enum _WIRELESS_EVENT_TYPE;
+
+typedef struct INTEL_EVENT
+{
+ BYTE BtMacAddress[INTEL_MAC_ADDR_LENGTH + 1];
+ ///< Source of event (MAC address)
+ _WIRELESS_EVENT_TYPE eType; ///< Event type
+ HRESULT hErrCode; ///< Error code
+ DWORD dwSize;
+ BYTE* data;
+} INTEL_EVENT, *PINTEL_EVENT;
+
+typedef void(*INTEL_EVENT_CALLBACK) ( INTEL_EVENT iEvent, void *pContext );
+
+typedef struct INTEL_CALLBACK
+{
+ INTEL_EVENT_CALLBACK fnIntelCallback;
+ void *pContext;
+
+} INTEL_CALLBACK, *PINTEL_CALLBACK;
+
+/// Adapter handle
+typedef DWORD HADAPTER;
+
+//intel Header structure
+typedef struct _INTEL_WIFI_HEADER
+{
+ DWORD dwStructVersion;
+ //Structure version for which this header is created
+ DWORD dwSize;
+ //size of the structure for which this header is created
+ DWORD dwFlags; //For future use
+ DWORD dwReserved; //For future use
+} INTEL_WIFI_HEADER, *PINTEL_WIFI_HEADER;
+
+typedef enum INTEL_ADAPTER_TYPE
+{
+ eStone, // Stone Peak - without BT Support
+ eStoneBT, // Stone Peak - with BT Support
+ eSnowfield // Snowfield Peak
+} INTEL_ADAPTER_TYPE;
+
+#define INTEL_ADAPTER_NAME_SIZE 64
+#define INTEL_ADAPTER_DESCRIPTION_SIZE 64
+#define INTEL_ADAPTER_CLSGUID_SIZE 64
+#define INTEL_REGPATH_SIZE 512
+
+#define INTEL_STRUCT_VER_LATEST 156
+
+typedef enum INTEL_ADAPTER_PROTOCOL
+{
+ eUnknownProtocol, ///< Unknown adapter.
+ e802_11, ///< WiFi
+} INTEL_ADAPTER_PROTOCOL;
+
+typedef struct INTEL_ADAPTER_LIST_ENTRY
+{
+ HADAPTER hAdapter;
+ ///< Adapter handle
+ UCHAR btMACAddress[INTEL_MAC_ADDR_LENGTH];
+ ///< Adapter MAC address
+ CHAR szAdapterName[INTEL_ADAPTER_NAME_SIZE];
+ ///< Adapter OS CLSGUID
+ CHAR szAdapterDescription[INTEL_ADAPTER_DESCRIPTION_SIZE];
+ ///< Display name
+ CHAR szAdapterClsguid[INTEL_ADAPTER_CLSGUID_SIZE];
+ ///< Muroc CLSGUID
+ CHAR szRegistryPath[INTEL_REGPATH_SIZE];
+ ///< Adapter registry root
+ CHAR szPnPId[INTEL_REGPATH_SIZE]; ///< Plug-and-Play ID
+ BOOL bEnabled; ///< enabled in driver
+ INTEL_ADAPTER_TYPE eAdapterType; ///< Adapter type
+ INTEL_ADAPTER_PROTOCOL eAdapterProtocol; ///< Adapter type
+} INTEL_ADAPTER_LIST_ENTRY, *PINTEL_ADAPTER_LIST_ENTRY;
+
+typedef struct INTEL_ADAPTER_LIST
+{
+ int count; ///< Number of entries
+ INTEL_ADAPTER_LIST_ENTRY adapter[INTEL_MAX_ADAPTERS];
+ ///< Array of adapter entries
+} INTEL_ADAPTER_LIST, *PINTEL_ADAPTER_LIST;
+
+
+typedef HRESULT ( APIENTRY *GetAdapterList_t )
+ ( PINTEL_ADAPTER_LIST* ppAdapterList );
+typedef HRESULT ( APIENTRY *WifiCmdTimingMeasurementRequest_t )
+ ( HADAPTER, PINTEL_WIFI_HEADER, void * );
+typedef HRESULT ( APIENTRY *WifiCmdTimingPtmWa_t )
+ (HADAPTER, PINTEL_WIFI_HEADER, void *);
+typedef HRESULT ( APIENTRY *RegisterIntelCallback_t ) ( PINTEL_CALLBACK );
+typedef HRESULT ( APIENTRY *DeregisterIntelCallback_t )
+ ( INTEL_EVENT_CALLBACK );
+
+extern GetAdapterList_t GetAdapterList;
+extern WifiCmdTimingMeasurementRequest_t WifiCmdTimingMeasurementRequest;
+extern WifiCmdTimingPtmWa_t WifiCmdTimingPtmWa;
+extern RegisterIntelCallback_t RegisterIntelCallback;
+extern DeregisterIntelCallback_t DeregisterIntelCallback;
+
+typedef struct _TIMINGMSMT_REQUEST *tm_request_t;
+typedef struct WirelessTimestamperCallbackArg
+*WirelessTimestamperCallbackArg_t;
+typedef void( *WirelessTimestamperCallback_t )
+( WirelessTimestamperCallbackArg_t arg );
+
+class IntelWirelessAdapter : public WindowsWirelessAdapter
+{
+private:
+ HADAPTER hAdapter;
+
+ // Get correlated time operation may timeout
+ // Use condition wait to manage this
+ SRWLOCK ct_lock;
+ CONDITION_VARIABLE ct_cond;
+ bool ct_done;
+ int ct_miss;
+
+ uint64_t gp2_last;
+ unsigned gp2_ext;
+
+public:
+ bool initialize( void );
+ void shutdown( void );
+ bool refreshCrossTimestamp();
+ bool initiateTimingRequest( TIMINGMSMT_REQUEST *tm_request );
+ bool attachAdapter( uint8_t *mac_addr );
+ bool registerTimestamper( WindowsWirelessTimestamper *timestamper );
+ bool deregisterTimestamper( WindowsWirelessTimestamper *timestamper );
+ IntelWirelessAdapter();
+
+ uint64_t calc_rollover( uint64_t gp2 );
+
+ friend void IntelWirelessTimestamperEventHandler
+ ( INTEL_EVENT iEvent, void *pContext );
+};
+
+inline IntelWirelessAdapter::IntelWirelessAdapter()
+{
+ gp2_ext = 0;
+ gp2_last = ULLONG_MAX;
+}
+
+#endif
diff --git a/daemons/gptp/windows/daemon_cl/tsc.hpp b/daemons/gptp/windows/daemon_cl/tsc.hpp
index ed22752c..425205d3 100644
--- a/daemons/gptp/windows/daemon_cl/tsc.hpp
+++ b/daemons/gptp/windows/daemon_cl/tsc.hpp
@@ -95,13 +95,14 @@ inline uint32_t FindFrequencyByModel(uint8_t model_query) {
/**
* @brief Gets the TSC frequnecy
- * @param millis time in miliseconds
+ * @param builtin whether device is connected to the Intel bus (not PCIE)
* @return TSC frequency, or 0 on error
*/
-inline uint64_t getTSCFrequency( unsigned millis ) {
+inline uint64_t getTSCFrequency( bool builtin ) {
int max_cpuid_level;
int tmp[4];
BOOL is_windows_10;
+ LARGE_INTEGER freq;
// Find the max cpuid level, and if possible find clock info
__cpuid(tmp, 0);
@@ -124,9 +125,11 @@ inline uint64_t getTSCFrequency( unsigned millis ) {
// clock will be returned, *else* use QPC for everything else
//
// EAX (tmp[0]) must be >= 1, See Intel SDM 17.15.4 "Invariant Time-keeping"
- if (is_windows_10 &&
+ if (!is_windows_10 &&
max_cpuid_level >= CLOCK_INFO_CPUID_LEAF &&
- tmp[0] >= 1) {
+ tmp[0] >= 1 &&
+ builtin )
+ {
SYSTEM_INFO info;
GetSystemInfo(&info);
@@ -135,8 +138,6 @@ inline uint64_t getTSCFrequency( unsigned millis ) {
return FindFrequencyByModel(info.wProcessorRevision >> 8);
}
- LARGE_INTEGER freq;
-
if (QueryPerformanceFrequency(&freq))
return freq.QuadPart;
diff --git a/daemons/gptp/windows/daemon_cl/windows_hal.cpp b/daemons/gptp/windows/daemon_cl/windows_hal.cpp
index a05d473c..3d034e39 100644
--- a/daemons/gptp/windows/daemon_cl/windows_hal.cpp
+++ b/daemons/gptp/windows/daemon_cl/windows_hal.cpp
@@ -67,8 +67,184 @@ VOID CALLBACK WindowsTimerQueueHandler( PVOID arg_in, BOOLEAN ignore ) {
ReleaseSRWLockExclusive( &arg->queue->retiredTimersLock );
}
+inline uint64_t scale64(uint64_t i, uint32_t m, uint32_t n)
+{
+ uint64_t tmp, res, rem;
+
+ rem = i % n;
+ i /= n;
+
+ res = i * m;
+ tmp = rem * m;
+
+ tmp /= n;
+
+ return res + tmp;
+}
+
+void WirelessTimestamperCallback( LPVOID arg )
+{
+ WirelessTimestamperCallbackArg *larg =
+ (WirelessTimestamperCallbackArg *)arg;
+ WindowsWirelessTimestamper *timestamper =
+ dynamic_cast<WindowsWirelessTimestamper *> (larg->timestamper);
+ WirelessDialog tmp1, tmp2;
+ LinkLayerAddress *peer_addr = NULL;
+
+ if (timestamper == NULL)
+ {
+ GPTP_LOG_ERROR( "Wrong timestamper type: %p",
+ larg->timestamper );
+ return;
+ }
+
+ switch( larg->iEvent_type )
+ {
+ default:
+ case TIMINGMSMT_CONFIRM_EVENT:
+ tmp1.action_devclk = larg->event_data.confirm.T1;
+ tmp1.ack_devclk = larg->event_data.confirm.T4;
+ tmp1.dialog_token = (BYTE)larg->event_data.confirm.DialogToken;
+ GPTP_LOG_VERBOSE
+ ( "Got confirm, %hhu(%llu,%llu)", tmp1.dialog_token,
+ tmp1.action_devclk, tmp1.ack_devclk );
+ peer_addr = new LinkLayerAddress
+ ( larg->event_data.confirm.PeerMACAddress );
+ larg->timestamper->
+ timingMeasurementConfirmCB( *peer_addr, &tmp1 );
+
+ break;
+
+ case TIMINGMSMT_EVENT:
+ tmp1/*prev*/.action_devclk = larg->event_data.indication.
+ indication.T1;
+ tmp1/*prev*/.ack_devclk = larg->event_data.indication.
+ indication.T4;
+ tmp1/*prev*/.dialog_token = (BYTE)larg->event_data.indication.
+ indication.FollowUpDialogToken;
+ tmp2/*curr*/.action_devclk = larg->event_data.indication.
+ indication.T2;
+ tmp2/*curr*/.ack_devclk = larg->event_data.indication.
+ indication.T3;
+ tmp2/*curr*/.dialog_token = (BYTE)larg->event_data.indication.
+ indication.DialogToken;
+ GPTP_LOG_VERBOSE
+ ("Got indication, %hhu(%llu,%llu) %hhu(%llu,%llu)",
+ tmp1.dialog_token, tmp1.action_devclk,
+ tmp1.ack_devclk, tmp2.dialog_token,
+ tmp2.action_devclk, tmp2.ack_devclk);
+ peer_addr = new LinkLayerAddress(larg->event_data.indication.
+ indication.PeerMACAddress);
+
+ larg->timestamper->timeMeasurementIndicationCB
+ ( *peer_addr, &tmp2, &tmp1,
+ larg->event_data.indication.
+ indication.PtpSpec.fwup_data,
+ larg->event_data.indication.
+ indication.WiFiVSpecHdr.Length - (sizeof(PTP_SPEC) -
+ 1));
+
+ break;
+
+ case TIMINGMSMT_CORRELATEDTIME_EVENT:
+ timestamper->system_counter =
+ scale64( larg->event_data.ptm_wa.TSC, NS_PER_SECOND,
+ (uint32_t)timestamper->tsc_hz.QuadPart );
+ timestamper->system_time.set64(timestamper->system_counter);
+ // Scale from TM timescale to nanoseconds
+ larg->event_data.ptm_wa.LocalClk *= 10;
+ timestamper->device_time.set64
+ (larg->event_data.ptm_wa.LocalClk*10);
+
+ break;
+ }
+
+ delete peer_addr;
+}
+
+net_result WindowsWirelessTimestamper::_requestTimingMeasurement
+(TIMINGMSMT_REQUEST *timingmsmt_req)
+{
+ net_result ret = net_succeed;
+
+ if (!adapter->initiateTimingRequest(timingmsmt_req)) {
+ GPTP_LOG_ERROR("Failed to send timing measurement request\n");
+ ret = net_fatal;
+ }
+
+ return ret;
+}
+
+bool WindowsWirelessTimestamper::HWTimestamper_gettime
+( Timestamp *system_time,
+ Timestamp * device_time,
+ uint32_t * local_clock,
+ uint32_t * nominal_clock_rate ) const
+{
+ bool refreshed = adapter->refreshCrossTimestamp();
+ if (refreshed)
+ {
+ // We have a fresh cross-timestamp just use it
+ *system_time = this->system_time;
+ *device_time = this->device_time;
+ } else
+ {
+ // We weren't able to get a fresh timestamp,
+ // extrapolate from the last
+ LARGE_INTEGER tsc_now;
+ QueryPerformanceCounter(&tsc_now);
+ unsigned device_delta = (unsigned)
+ (((long double) (tsc_now.QuadPart - system_counter)) /
+ (((long double)tsc_hz.QuadPart) / 1000000000));
+ device_delta = (unsigned)(device_delta*getPort()->
+ getLocalSystemFreqOffset());
+ system_time->set64((uint64_t)
+ (((long double)tsc_now.QuadPart) /
+ ((long double)tsc_hz.QuadPart /
+ 1000000000)));
+ device_time->set64(device_delta);
+ *device_time = *device_time + this->device_time;
+ }
+
+ return true;
+}
+
+bool WindowsWirelessTimestamper::HWTimestamper_init
+(InterfaceLabel *iface_label, OSNetworkInterface *iface)
+{
+ uint8_t mac_addr_local[ETHER_ADDR_OCTETS];
+
+ if (!initialized) {
+ if (!adapter->initialize()) return false;
+ if (getPort()->getLocalAddr() == NULL)
+ return false;
+
+ getPort()->getLocalAddr()->toOctetArray(mac_addr_local);
+ if (!adapter->attachAdapter(mac_addr_local)) {
+ return false;
+ }
+
+ tsc_hz.QuadPart = getTSCFrequency(false);
+ if (tsc_hz.QuadPart == 0) {
+ return false;
+ }
+
+ if (!adapter->registerTimestamper(this))
+ return false;
+ }
+
+ initialized = true;
+ return true;
+}
+
+WindowsWirelessTimestamper::~WindowsWirelessTimestamper() {
+ if (adapter->deregisterTimestamper(this))
+ adapter->shutdown();
+ else
+ GPTP_LOG_INFO("Failed to shutdown time sync on adapter");
+}
-bool WindowsTimestamper::HWTimestamper_init( InterfaceLabel *iface_label, OSNetworkInterface *net_iface ) {
+bool WindowsEtherTimestamper::HWTimestamper_init( InterfaceLabel *iface_label, OSNetworkInterface *net_iface ) {
char network_card_id[64];
LinkLayerAddress *addr = dynamic_cast<LinkLayerAddress *>(iface_label);
if( addr == NULL ) return false;
@@ -112,7 +288,7 @@ bool WindowsTimestamper::HWTimestamper_init( InterfaceLabel *iface_label, OSNetw
NULL, OPEN_EXISTING, 0, NULL );
if( miniport == INVALID_HANDLE_VALUE ) return false;
- tsc_hz.QuadPart = getTSCFrequency( 1000 );
+ tsc_hz.QuadPart = getTSCFrequency( true );
if( tsc_hz.QuadPart == 0 ) {
return false;
}
diff --git a/daemons/gptp/windows/daemon_cl/windows_hal.hpp b/daemons/gptp/windows/daemon_cl/windows_hal.hpp
index f5192910..5f1a6f61 100644
--- a/daemons/gptp/windows/daemon_cl/windows_hal.hpp
+++ b/daemons/gptp/windows/daemon_cl/windows_hal.hpp
@@ -36,7 +36,6 @@
/**@file*/
-#include <minwindef.h>
#include <IPCListener.hpp>
#include "avbts_osnet.hpp"
#include "avbts_oslock.hpp"
@@ -47,6 +46,7 @@
#include "packet.hpp"
#include "ieee1588.hpp"
#include "ether_tstamper.hpp"
+#include "wireless_tstamper.hpp"
#include "iphlpapi.h"
#include "windows_ipc.hpp"
#include "tsc.hpp"
@@ -590,6 +590,114 @@ public:
}
};
+void WirelessTimestamperCallback(LPVOID arg);
+
+class WindowsWirelessAdapter;
+
+/**
+* @brief Windows Wireless (802.11) HWTimestamper implementation
+*/
+class WindowsWirelessTimestamper : public WirelessTimestamper
+{
+private:
+ WindowsWirelessAdapter *adapter;
+
+ uint64_t system_counter;
+ Timestamp system_time;
+ Timestamp device_time;
+ LARGE_INTEGER tsc_hz;
+ bool initialized;
+
+public:
+ WindowsWirelessTimestamper()
+ {
+ initialized = false;
+ }
+
+ net_result _requestTimingMeasurement
+ ( TIMINGMSMT_REQUEST *timingmsmt_req );
+
+ bool HWTimestamper_gettime
+ ( Timestamp *system_time, Timestamp * device_time,
+ uint32_t * local_clock, uint32_t * nominal_clock_rate ) const;
+
+ virtual bool HWTimestamper_init
+ ( InterfaceLabel *iface_label, OSNetworkInterface *iface );
+
+ /**
+ * @brief attach adapter to timestamper
+ * @param adapter [in] adapter to attach
+ */
+ void setAdapter( WindowsWirelessAdapter *adapter )
+ {
+ this->adapter = adapter;
+ }
+
+ /**
+ * @brief get attached adapter
+ * @return attached adapter
+ */
+ WindowsWirelessAdapter *getAdapter(void)
+ {
+ return adapter;
+ }
+
+ ~WindowsWirelessTimestamper();
+
+ friend void WirelessTimestamperCallback( LPVOID arg );
+};
+
+class WindowsWirelessAdapter
+{
+public:
+ /**
+ * @brief initiate wireless TM request (completion is asynchronous)
+ * @param tm_request [in] pointer to TM request object
+ * @return true on success
+ */
+ virtual bool initiateTimingRequest(TIMINGMSMT_REQUEST *tm_request) = 0;
+
+ /**
+ * @brief attempt to refresh cross timestamp (extrapolate on failure)
+ * @return true on success
+ */
+ virtual bool refreshCrossTimestamp() = 0;
+
+ /**
+ * @brief register timestamper with adapter
+ * @param timestamper [in] timestamper object
+ * @return true on success
+ */
+ virtual bool registerTimestamper
+ ( WindowsWirelessTimestamper *timestamper ) = 0;
+
+ /**
+ * @brief deregister timestamper
+ * @param timestamper [in] timestamper object
+ * @return true on success
+ */
+ virtual bool deregisterTimestamper
+ ( WindowsWirelessTimestamper *timestamper ) = 0;
+
+ /**
+ * @brief initialize adapter object
+ * @return true on success
+ */
+ virtual bool initialize() = 0;
+
+ /**
+ * @brief shutdown adapter
+ */
+ virtual void shutdown() = 0;
+
+ /**
+ * @brief attach adapter to MAC address
+ * @param mac_addr [in] MAC address to attach to
+ * @return true on success
+ */
+ virtual bool attachAdapter( uint8_t *mac_addr ) = 0;
+};
+
#define I217_DESC "I217-LM"
#define I219_DESC "I219-V"
@@ -616,9 +724,9 @@ static DeviceClockRateMapping DeviceClockRateMap[] =
};
/**
- * @brief Windows HWTimestamper implementation
+ * @brief Windows Ethernet HWTimestamper implementation
*/
-class WindowsTimestamper : public EtherTimestamper {
+class WindowsEtherTimestamper : public EtherTimestamper {
private:
// No idea whether the underlying implementation is thread safe
HANDLE miniport;
diff --git a/daemons/gptp/windows/daemon_cl/work_queue.cpp b/daemons/gptp/windows/daemon_cl/work_queue.cpp
new file mode 100644
index 00000000..461d6923
--- /dev/null
+++ b/daemons/gptp/windows/daemon_cl/work_queue.cpp
@@ -0,0 +1,106 @@
+/******************************************************************************
+
+Copyright (c) 2009-2015, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the Intel Corporation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#include <work_queue.hpp>
+#include <stdio.h>
+
+
+struct WWQueueThreadState {
+ bool running;
+ bool stop;
+ WWQueueCallback task;
+ LPVOID arg;
+};
+
+DWORD WINAPI WindowsWorkQueueLoop(LPVOID arg) {
+ WWQueueThreadState *state = (WWQueueThreadState *)arg;
+ state->running = true;
+ while (!state->stop) {
+ if (state->task != NULL) {
+ state->task(state->arg);
+ delete state->arg;
+ state->task = NULL;
+ }
+ Sleep(1);
+ }
+ state->running = false;
+
+ return 0;
+}
+
+bool WindowsWorkQueue::init(int number_threads)
+{
+ if (number_threads == 0) number_threads = DEFAULT_THREAD_COUNT;
+ state = new WWQueueThreadState[number_threads];
+ for (int i = 0; i < number_threads; ++i) {
+ state[i].running = false;
+ state[i].stop = false;
+ state[i].task = NULL;
+ }
+ workers = new HANDLE[number_threads];
+ for (int i = 0; i < number_threads; ++i) {
+ workers[i] = CreateThread(NULL, 0, WindowsWorkQueueLoop, state + i, 0, NULL);
+ if (workers[i] == INVALID_HANDLE_VALUE)
+ return false;
+ while (!state[i].running)
+ Sleep(1);
+ }
+ this->number_threads = number_threads;
+ return true;
+}
+
+bool WindowsWorkQueue::submit(WWQueueCallback cb, LPVOID arg)
+{
+ int i;
+
+ for (i = 0; i < number_threads; ++i) {
+ if (state[i].task == NULL) {
+ state[i].arg = arg;
+ state[i].task = cb;
+ break;
+ }
+ }
+ if (i == number_threads)
+ return false;
+
+ return true;
+}
+
+void WindowsWorkQueue::stop()
+{
+ for (int i = 0; i < number_threads; ++i) {
+ state[i].stop = true;
+ while (state[i].running)
+ Sleep(1);
+ }
+}
diff --git a/daemons/gptp/windows/daemon_cl/work_queue.hpp b/daemons/gptp/windows/daemon_cl/work_queue.hpp
new file mode 100644
index 00000000..4ac750c5
--- /dev/null
+++ b/daemons/gptp/windows/daemon_cl/work_queue.hpp
@@ -0,0 +1,74 @@
+/******************************************************************************
+
+Copyright (c) 2009-2015, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the Intel Corporation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#ifndef WORK_QUEUE_HPP
+#define WORK_QUEUE_HPP
+
+#include <WTypesbase.h>
+
+#define DEFAULT_THREAD_COUNT (5)
+
+struct WWQueueThreadState;
+
+typedef void(*WWQueueCallback)(LPVOID arg);
+
+class WindowsWorkQueue
+{
+private:
+ HANDLE *workers;
+ WWQueueThreadState *state;
+ int number_threads;
+
+public:
+ /**
+ * @brief initialize work queue
+ * @param number_threads [in] number of threads (0 = default)
+ * @return true on success
+ */
+ bool init( int number_threads );
+
+ /**
+ * @brief submit job to work queue
+ * @param cb [in] function to call
+ * @param arg [in] parameter provided to callback
+ * @return true on success
+ */
+ bool submit( WWQueueCallback cb, LPVOID arg );
+
+ /**
+ * @brief stop work queue
+ */
+ void stop();
+};
+
+#endif/*WORK_QUEUE_HPP*/
diff --git a/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp b/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp
index 713a2b4a..9a34a4ef 100644
--- a/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp
+++ b/daemons/gptp/windows/named_pipe_test/named_pipe_test.cpp
@@ -60,7 +60,7 @@ int _tmain(int argc, _TCHAR* argv[])
strcpy_s( pipename, 64, PIPE_PREFIX );
strcat_s( pipename, 64-strlen(pipename), P802_1AS_PIPENAME );
HANDLE pipe;
- uint64_t tsc_frequency = getTSCFrequency( 1000 );
+ uint64_t tsc_frequency = getTSCFrequency( true );
// Wait for Ctrl-C
if( !SetConsoleCtrlHandler( ctrl_handler, true )) {
diff --git a/kmod/igb/e1000_82575.c b/kmod/igb/e1000_82575.c
index f8abc3e5..2fcc3bf3 100644
--- a/kmod/igb/e1000_82575.c
+++ b/kmod/igb/e1000_82575.c
@@ -98,10 +98,10 @@ static void e1000_clear_vfta_i350(struct e1000_hw *hw);
static void e1000_i2c_start(struct e1000_hw *hw);
static void e1000_i2c_stop(struct e1000_hw *hw);
-static s32 e1000_clock_in_i2c_byte(struct e1000_hw *hw, u8 *data);
+static void e1000_clock_in_i2c_byte(struct e1000_hw *hw, u8 *data);
static s32 e1000_clock_out_i2c_byte(struct e1000_hw *hw, u8 data);
static s32 e1000_get_i2c_ack(struct e1000_hw *hw);
-static s32 e1000_clock_in_i2c_bit(struct e1000_hw *hw, bool *data);
+static void e1000_clock_in_i2c_bit(struct e1000_hw *hw, bool *data);
static s32 e1000_clock_out_i2c_bit(struct e1000_hw *hw, bool data);
static void e1000_raise_i2c_clk(struct e1000_hw *hw, u32 *i2cctl);
static void e1000_lower_i2c_clk(struct e1000_hw *hw, u32 *i2cctl);
@@ -3217,9 +3217,7 @@ s32 e1000_read_i2c_byte_generic(struct e1000_hw *hw, u8 byte_offset,
if (status != E1000_SUCCESS)
goto fail;
- status = e1000_clock_in_i2c_byte(hw, data);
- if (status != E1000_SUCCESS)
- goto fail;
+ e1000_clock_in_i2c_byte(hw, data);
status = e1000_clock_out_i2c_bit(hw, nack);
if (status != E1000_SUCCESS)
@@ -3383,7 +3381,7 @@ static void e1000_i2c_stop(struct e1000_hw *hw)
*
* Clocks in one byte data via I2C data/clock
**/
-static s32 e1000_clock_in_i2c_byte(struct e1000_hw *hw, u8 *data)
+static void e1000_clock_in_i2c_byte(struct e1000_hw *hw, u8 *data)
{
s32 i;
bool bit = 0;
@@ -3396,7 +3394,6 @@ static s32 e1000_clock_in_i2c_byte(struct e1000_hw *hw, u8 *data)
*data |= bit << i;
}
- return E1000_SUCCESS;
}
/**
@@ -3485,7 +3482,7 @@ static s32 e1000_get_i2c_ack(struct e1000_hw *hw)
*
* Clocks in one bit via I2C data/clock
**/
-static s32 e1000_clock_in_i2c_bit(struct e1000_hw *hw, bool *data)
+static void e1000_clock_in_i2c_bit(struct e1000_hw *hw, bool *data)
{
u32 i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS);
@@ -3504,7 +3501,6 @@ static s32 e1000_clock_in_i2c_bit(struct e1000_hw *hw, bool *data)
/* Minimum low period of clock is 4.7 us */
usec_delay(E1000_I2C_T_LOW);
- return E1000_SUCCESS;
}
/**
diff --git a/kmod/igb/e1000_api.c b/kmod/igb/e1000_api.c
index 54faa686..87bccbd1 100644
--- a/kmod/igb/e1000_api.c
+++ b/kmod/igb/e1000_api.c
@@ -187,6 +187,7 @@ s32 e1000_set_mac_type(struct e1000_hw *hw)
case E1000_DEV_ID_I210_FIBER:
case E1000_DEV_ID_I210_SERDES:
case E1000_DEV_ID_I210_SGMII:
+ case E1000_DEV_ID_I210_AUTOMOTIVE:
mac->type = e1000_i210;
break;
case E1000_DEV_ID_I211_COPPER:
diff --git a/kmod/igb/e1000_hw.h b/kmod/igb/e1000_hw.h
index 3bcecf19..74cb22ee 100644
--- a/kmod/igb/e1000_hw.h
+++ b/kmod/igb/e1000_hw.h
@@ -59,6 +59,7 @@ struct e1000_hw;
#define E1000_DEV_ID_I210_FIBER 0x1536
#define E1000_DEV_ID_I210_SERDES 0x1537
#define E1000_DEV_ID_I210_SGMII 0x1538
+#define E1000_DEV_ID_I210_AUTOMOTIVE 0x15F6
#define E1000_DEV_ID_I210_COPPER_FLASHLESS 0x157B
#define E1000_DEV_ID_I210_SERDES_FLASHLESS 0x157C
#define E1000_DEV_ID_I211_COPPER 0x1539
diff --git a/kmod/igb/e1000_osdep.h b/kmod/igb/e1000_osdep.h
index 422bba05..3c6b7958 100644
--- a/kmod/igb/e1000_osdep.h
+++ b/kmod/igb/e1000_osdep.h
@@ -90,7 +90,7 @@ struct e1000_hw;
/* write operations, indexed using DWORDS */
#define E1000_WRITE_REG(hw, reg, val) \
do { \
- u8 __iomem *hw_addr = ACCESS_ONCE((hw)->hw_addr); \
+ u8 __iomem *hw_addr = READ_ONCE((hw)->hw_addr); \
if (!E1000_REMOVED(hw_addr)) \
writel((val), &hw_addr[(reg)]); \
} while (0)
diff --git a/kmod/igb/igb.h b/kmod/igb/igb.h
index 7fb3e53a..c8bbf307 100644
--- a/kmod/igb/igb.h
+++ b/kmod/igb/igb.h
@@ -92,6 +92,9 @@ struct igb_user_page {
#include <linux/i2c-algo-bit.h>
#endif /* HAVE_I2C_SUPPORT */
+#include <linux/miscdevice.h>
+typedef u64 cycle_t;
+
/* Interrupt defines */
#define IGB_START_ITR 648 /* ~6000 ints/sec */
#define IGB_4K_ITR 980
@@ -796,7 +799,7 @@ enum e1000_state_t {
extern char igb_driver_name[];
extern char igb_driver_version[];
-extern int igb_up(struct igb_adapter *);
+extern void igb_up(struct igb_adapter *);
extern void igb_down(struct igb_adapter *);
extern void igb_reinit_locked(struct igb_adapter *);
extern void igb_reset(struct igb_adapter *);
@@ -880,6 +883,22 @@ void igb_procfs_topdir_exit(void);
#define IGB_MAP_RX_RING _IOW('E', 207, int)
#define IGB_UNMAP_RX_RING _IOW('E', 208, int)
+/*set of newly defined ioctl calls - new libigb compatibility
+ each of them is an equivalent of the old ioctl
+ changed numberiong convention: new_ioctl = old_ioctl + 100*/
+
+#define IGB_IOCTL_MAPRING _IOW('E', 302, int)
+#define IGB_IOCTL_MAP_TX_RING IGB_IOCTL_MAPRING
+#define IGB_IOCTL_UNMAPRING _IOW('E', 303, int)
+#define IGB_IOCTL_UNMAP_TX_RING IGB_IOCTL_UNMAPRING
+#define IGB_IOCTL_MAPBUF _IOW('E', 304, int)
+#define IGB_IOCTL_UNMAPBUF _IOW('E', 305, int)
+#define IGB_IOCTL_MAP_RX_RING _IOW('E', 307, int)
+#define IGB_IOCTL_UNMAP_RX_RING _IOW('E', 308, int)
+
+
+/*END*/
+
#define IGB_BIND_NAMESZ 24
struct igb_bind_cmd {
@@ -897,6 +916,7 @@ struct igb_buf_cmd {
u64 physaddr;
u32 queue;
u32 mmap_size;
+ u64 pa;
};
struct igb_link_cmd {
diff --git a/kmod/igb/igb_ethtool.c b/kmod/igb/igb_ethtool.c
index 51ccf3a0..91e27861 100644
--- a/kmod/igb/igb_ethtool.c
+++ b/kmod/igb/igb_ethtool.c
@@ -1528,7 +1528,7 @@ static void igb_loopback_cleanup(struct igb_adapter *adapter)
e1000_read_phy_reg(hw, PHY_CONTROL, &phy_reg);
if (phy_reg & MII_CR_LOOPBACK) {
phy_reg &= ~MII_CR_LOOPBACK;
- if (hw->phy.type == I210_I_PHY_ID)
+ if (hw->phy.id == I210_I_PHY_ID)
e1000_write_phy_reg(hw, I347AT4_PAGE_SELECT, 0);
e1000_write_phy_reg(hw, PHY_CONTROL, phy_reg);
e1000_phy_commit(hw);
@@ -1713,10 +1713,10 @@ static int igb_loopback_test(struct igb_adapter *adapter, u64 *data)
*data = igb_setup_desc_rings(adapter);
if (*data)
goto out;
- *data = igb_setup_loopback_test(adapter);
+ igb_setup_loopback_test(adapter);
+ *data = igb_run_loopback_test(adapter);
if (*data)
goto err_loopback;
- *data = igb_run_loopback_test(adapter);
igb_loopback_cleanup(adapter);
@@ -2141,7 +2141,7 @@ static void igb_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
switch (stringset) {
case ETH_SS_TEST:
- memcpy(data, *igb_gstrings_test,
+ memcpy(data, igb_gstrings_test,
IGB_TEST_LEN*ETH_GSTRING_LEN);
break;
case ETH_SS_STATS:
diff --git a/kmod/igb/igb_main.c b/kmod/igb/igb_main.c
index c8a4a7e3..1f3ae697 100644
--- a/kmod/igb/igb_main.c
+++ b/kmod/igb/igb_main.c
@@ -96,6 +96,7 @@ static const struct pci_device_id igb_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_FIBER) },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SERDES) },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SGMII) },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_AUTOMOTIVE) },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_COPPER_FLASHLESS) },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SERDES_FLASHLESS) },
/* required last entry */
@@ -121,11 +122,17 @@ static void igb_clean_all_tx_rings(struct igb_adapter *);
static void igb_clean_all_rx_rings(struct igb_adapter *);
static void igb_clean_tx_ring(struct igb_ring *);
static void igb_set_rx_mode(struct net_device *);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+static void igb_update_phy_info(struct timer_list *);
+static void igb_watchdog(struct timer_list *);
+static void igb_dma_err_timer(struct timer_list *);
+#else
static void igb_update_phy_info(unsigned long);
static void igb_watchdog(unsigned long);
+static void igb_dma_err_timer(unsigned long data);
+#endif
static void igb_watchdog_task(struct work_struct *);
static void igb_dma_err_task(struct work_struct *);
-static void igb_dma_err_timer(unsigned long data);
/* AVB specific */
#ifdef HAVE_NDO_SELECT_QUEUE_ACCEL_FALLBACK
static u16 igb_select_queue(struct net_device *dev, struct sk_buff *skb,
@@ -214,7 +221,11 @@ static long igb_ioctl_file(struct file *file, unsigned int cmd,
unsigned long arg);
static void igb_vm_open(struct vm_area_struct *vma);
static void igb_vm_close(struct vm_area_struct *vma);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
+static int igb_vm_fault(struct vm_fault *fdata);
+#else
static int igb_vm_fault(struct vm_area_struct *area, struct vm_fault *fdata);
+#endif
static int igb_mmap(struct file *file, struct vm_area_struct *vma);
static ssize_t igb_read(struct file *file, char __user *buf, size_t count,
loff_t *pos);
@@ -458,7 +469,7 @@ static void igb_cache_ring_register(struct igb_adapter *adapter)
u32 e1000_read_reg(struct e1000_hw *hw, u32 reg)
{
struct igb_adapter *igb = container_of(hw, struct igb_adapter, hw);
- u8 __iomem *hw_addr = ACCESS_ONCE(hw->hw_addr);
+ u8 __iomem *hw_addr = READ_ONCE(hw->hw_addr);
u32 value = 0;
if (E1000_REMOVED(hw_addr))
@@ -1090,8 +1101,13 @@ static void igb_set_interrupt_capability(struct igb_adapter *adapter, bool msix)
for (i = 0; i < numvecs; i++)
adapter->msix_entries[i].entry = i;
- err = pci_enable_msix(pdev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,12,0)
+ err = pci_enable_msix_exact(pdev,
adapter->msix_entries, numvecs);
+#else
+ err = pci_enable_msix(pdev,
+ adapter->msix_entries, numvecs);
+#endif
if (err == 0)
break;
}
@@ -1810,7 +1826,7 @@ static s32 igb_init_i2c(struct igb_adapter *adapter)
* igb_up - Open the interface and prepare it to handle traffic
* @adapter: board private structure
**/
-int igb_up(struct igb_adapter *adapter)
+void igb_up(struct igb_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
int i;
@@ -1854,7 +1870,6 @@ int igb_up(struct igb_adapter *adapter)
(!hw->dev_spec._82575.eee_disable))
adapter->eee_advert = MDIO_EEE_100TX | MDIO_EEE_1000T;
- return 0;
}
void igb_down(struct igb_adapter *adapter)
@@ -2877,6 +2892,13 @@ static int igb_probe(struct pci_dev *pdev,
/* Check if Media Autosense is enabled */
if (hw->mac.type == e1000_82580)
igb_init_mas(adapter);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+ timer_setup(&adapter->watchdog_timer, &igb_watchdog, 0);
+ if (adapter->flags & IGB_FLAG_DETECT_BAD_DMA)
+ timer_setup(&adapter->dma_err_timer, &igb_dma_err_timer, 0);
+ timer_setup(&adapter->phy_info_timer, &igb_update_phy_info, 0);
+#else
setup_timer(&adapter->watchdog_timer, &igb_watchdog,
(unsigned long) adapter);
if (adapter->flags & IGB_FLAG_DETECT_BAD_DMA)
@@ -2884,6 +2906,7 @@ static int igb_probe(struct pci_dev *pdev,
(unsigned long) adapter);
setup_timer(&adapter->phy_info_timer, &igb_update_phy_info,
(unsigned long) adapter);
+#endif
INIT_WORK(&adapter->reset_task, igb_reset_task);
INIT_WORK(&adapter->watchdog_task, igb_watchdog_task);
@@ -4673,9 +4696,19 @@ static void igb_spoof_check(struct igb_adapter *adapter)
/* Need to wait a few seconds after link up to get diagnostic information from
* the phy
*/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+static void igb_update_phy_info(struct timer_list *timer)
+#else
static void igb_update_phy_info(unsigned long data)
+#endif
{
- struct igb_adapter *adapter = (struct igb_adapter *) data;
+ struct igb_adapter *adapter;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+ adapter = container_of(timer, struct igb_adapter, watchdog_timer);
+#else
+ adapter = (struct igb_adapter *) data;
+#endif
e1000_get_phy_info(&adapter->hw);
}
@@ -4725,9 +4758,20 @@ bool igb_has_link(struct igb_adapter *adapter)
* igb_watchdog - Timer Call-back
* @data: pointer to adapter cast into an unsigned long
**/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+static void igb_watchdog(struct timer_list *timer)
+#else
static void igb_watchdog(unsigned long data)
+#endif
{
- struct igb_adapter *adapter = (struct igb_adapter *)data;
+ struct igb_adapter *adapter;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+ adapter = container_of(timer, struct igb_adapter, watchdog_timer);
+#else
+ adapter = (struct igb_adapter *)data;
+#endif
+
/* Do the rest outside of interrupt context */
schedule_work(&adapter->watchdog_task);
}
@@ -4983,9 +5027,20 @@ dma_timer_reset:
* igb_dma_err_timer - Timer Call-back
* @data: pointer to adapter cast into an unsigned long
**/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+static void igb_dma_err_timer(struct timer_list *timer)
+#else
static void igb_dma_err_timer(unsigned long data)
+#endif
{
- struct igb_adapter *adapter = (struct igb_adapter *)data;
+ struct igb_adapter *adapter;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+ adapter = container_of(timer, struct igb_adapter, dma_err_timer);
+#else
+ adapter = (struct igb_adapter *)data;
+#endif
+
/* Do the rest outside of interrupt context */
schedule_work(&adapter->dma_err_task);
}
@@ -9606,10 +9661,7 @@ static void igb_io_resume(struct pci_dev *pdev)
}
if (netif_running(netdev)) {
- if (igb_up(adapter)) {
- dev_err(pci_dev_to_dev(pdev), "igb_up failed after reset\n");
- return;
- }
+ igb_up(adapter);
}
netif_device_attach(netdev);
@@ -10218,7 +10270,14 @@ static int igb_bind(struct file *file, void __user *argp)
if (copy_from_user(&req, argp, sizeof(req)))
return -EFAULT;
+
+ /*
+ * Set the last character of req.iface to '/0' to
+ * guarantee null termination of req.iface string
+ * param in printk call.
+ */
+ req.iface[IGB_BIND_NAMESZ-1] = 0;
printk("bind to iface %s\n", req.iface);
if (igb_priv == NULL) {
@@ -10309,11 +10368,16 @@ static long igb_mapbuf_user(struct file *file, void __user *arg, int ring)
struct igb_private_data *igb_priv = file->private_data;
struct igb_adapter *adapter;
struct igb_buf_cmd req;
+ /*size used for the purpose of copying the contents of igb_buf_cmd
+ between userspace and kernel space
+ introduced to handle possible mismatch in libigb and igb version*/
+ int buf_cmd_size = 0;
int err = 0;
struct page *page;
dma_addr_t page_dma;
struct igb_user_page *userpage;
+
if (igb_priv == NULL) {
printk("cannot find private data!\n");
return -ENOENT;
@@ -10325,7 +10389,22 @@ static long igb_mapbuf_user(struct file *file, void __user *arg, int ring)
return -ENOENT;
}
- if (copy_from_user(&req, arg, sizeof(req)))
+ if(ring != IGB_IOCTL_MAPBUF)
+ {
+ dev_warn(&adapter->pdev->dev, "Old ioctl value used: %d, consider using a new one from libigb \n", ring);
+ /* this situation suggest using an old ioctl by libigb
+ as a consequence the igb_buf_cmd struct from libigb perspective does not contain the "pa" field
+ we need to align the requested size in copy_from_user() for possibility */
+ buf_cmd_size = sizeof(req) - sizeof(u64);
+ } else {
+ /* assuming no compatibility issue:
+ libigb and kernel module have the same
+ igb_buf_cmd structs ("pa" field included in both)
+ */
+ buf_cmd_size = sizeof(req);
+ }
+
+ if (copy_from_user(&req, arg, buf_cmd_size))
return -EFAULT;
userpage = vzalloc(sizeof(struct igb_user_page));
@@ -10368,11 +10447,14 @@ static long igb_mapbuf_user(struct file *file, void __user *arg, int ring)
igb_priv->userpages->page = page;
igb_priv->userpages->page_dma = page_dma;
+ if(ring == IGB_IOCTL_MAPBUF)
+ req.pa = page_to_phys(page);
+
req.physaddr = page_dma;
req.mmap_size = PAGE_SIZE;
mutex_unlock(&adapter->lock);
- if (copy_to_user(arg, &req, sizeof(req))) {
+ if (copy_to_user(arg, &req, buf_cmd_size)) {
printk("copyout to user failed\n");
err = -EFAULT;
mutex_lock(&adapter->lock);
@@ -10405,15 +10487,17 @@ static long igb_mapbuf(struct file *file, void __user *arg, int ring)
struct igb_private_data *igb_priv = file->private_data;
struct igb_adapter *adapter;
struct igb_buf_cmd req;
+ /*size used for the purpose of copying the contents of igb_buf_cmd
+ between userspace and kernel space
+ introduced to handle possible mismatch in libigb and igb version*/
+ int buf_cmd_size = 0;
+
int err = 0;
if (igb_priv == NULL) {
printk("cannot find private data!\n");
return -ENOENT;
- }
-
- if (copy_from_user(&req, arg, sizeof(req)))
- return -EFAULT;
+ }
adapter = igb_priv->adapter;
if (adapter == NULL) {
@@ -10421,7 +10505,29 @@ static long igb_mapbuf(struct file *file, void __user *arg, int ring)
return -ENOENT;
}
- if ((ring == IGB_MAPRING) || (ring == IGB_MAP_TX_RING)) {
+ if((ring != IGB_IOCTL_MAP_TX_RING) && (ring != IGB_IOCTL_MAP_RX_RING))
+ {
+ dev_warn(&adapter->pdev->dev, "Old ioctl value used: %d, consider using new one from libigb \n", ring);
+ /* this situation suggest using an old ioctl by libigb
+ as a consequence the igb_buf_cmd struct from libigb perspective does not contain the "pa" field
+ we need to align the requested size in copy_from_user() for possibility */
+ buf_cmd_size = sizeof(req) - sizeof(u64);
+
+ } else {
+
+ /* assuming no compatibility issue:
+ libigb and kernel module have the same
+ igb_buf_cmd structs ("pa" field included in both)
+ */
+ buf_cmd_size = sizeof(req);
+
+ }
+
+ if (copy_from_user(&req, arg, buf_cmd_size))
+ return -EFAULT;
+
+ if ((ring == IGB_MAPRING) || (ring == IGB_MAP_TX_RING) ||
+ ring == IGB_IOCTL_MAP_TX_RING) {
if (req.queue >= 3) {
printk("mapring:invalid queue specified(%d)\n",
req.queue);
@@ -10443,10 +10549,13 @@ static long igb_mapbuf(struct file *file, void __user *arg, int ring)
adapter->uring_tx_init |= (1 << req.queue);
igb_priv->uring_tx_init |= (1 << req.queue);
+ if(ring == IGB_IOCTL_MAP_TX_RING)
+ req.pa = virt_to_phys(adapter->tx_ring[req.queue]->desc);
+
req.physaddr = adapter->tx_ring[req.queue]->dma;
req.mmap_size = adapter->tx_ring[req.queue]->size;
mutex_unlock(&adapter->lock);
- } else if (ring == IGB_MAP_RX_RING) {
+ } else if ((ring == IGB_MAP_RX_RING) || (ring == IGB_IOCTL_MAP_RX_RING)) {
if (req.queue >= 3) {
printk("mapring:invalid queue specified(%d)\n",
req.queue);
@@ -10467,6 +10576,9 @@ static long igb_mapbuf(struct file *file, void __user *arg, int ring)
adapter->uring_rx_init |= (1 << req.queue);
igb_priv->uring_rx_init |= (1 << req.queue);
+
+ if(ring == IGB_IOCTL_MAP_RX_RING)
+ req.pa = virt_to_phys(adapter->rx_ring[req.queue]->desc);
req.physaddr = adapter->rx_ring[req.queue]->dma;
req.mmap_size = adapter->rx_ring[req.queue]->size;
@@ -10476,7 +10588,7 @@ static long igb_mapbuf(struct file *file, void __user *arg, int ring)
return -EINVAL;
}
- if (copy_to_user(arg, &req, sizeof(req))) {
+ if (copy_to_user(arg, &req, buf_cmd_size)) {
printk("copyout to user failed\n");
err = -EFAULT;
goto failed;
@@ -10494,22 +10606,36 @@ static long igb_unmapbuf(struct file *file, void __user *arg, int ring)
struct igb_private_data *igb_priv = file->private_data;
struct igb_adapter *adapter;
struct igb_buf_cmd req;
+ int buf_cmd_size = 0;
if (igb_priv == NULL) {
printk("cannot find private data!\n");
return -ENOENT;
}
- if (copy_from_user(&req, arg, sizeof(req)))
- return -EFAULT;
-
adapter = igb_priv->adapter;
if (adapter == NULL) {
printk("map to unbound device!\n");
return -ENOENT;
}
- if ((ring == IGB_UNMAP_TX_RING)) {
+
+ if((ring != IGB_IOCTL_UNMAPBUF) && (ring != IGB_IOCTL_UNMAP_TX_RING) &&
+ (ring != IGB_IOCTL_UNMAP_RX_RING)) {
+
+ dev_warn(&adapter->pdev->dev, "Old ioctl number used: %d, consider using new one from libigb \n", ring);
+ buf_cmd_size = sizeof(req) - sizeof(u64);
+
+ } else {
+
+ buf_cmd_size = sizeof(req);
+ }
+
+ if (copy_from_user(&req, arg, buf_cmd_size))
+ return -EFAULT;
+
+
+ if ((ring == IGB_UNMAP_TX_RING) || (ring == IGB_IOCTL_UNMAP_TX_RING)) {
/* its easy to figure out what to free on the rings ... */
if (req.queue >= 3)
return -EINVAL;
@@ -10527,7 +10653,7 @@ static long igb_unmapbuf(struct file *file, void __user *arg, int ring)
adapter->uring_tx_init &= ~(1 << req.queue);
igb_priv->uring_tx_init &= ~(1 << req.queue);
mutex_unlock(&adapter->lock);
- } else if (ring == IGB_UNMAP_RX_RING) {
+ } else if ((ring == IGB_UNMAP_RX_RING) || (ring == IGB_IOCTL_UNMAP_RX_RING)) {
/* its easy to figure out what to free on the rings ... */
if (req.queue >= 3)
return -EINVAL;
@@ -10601,14 +10727,20 @@ static long igb_ioctl_file(struct file *file, unsigned int cmd,
break;
case IGB_MAP_TX_RING:
case IGB_MAP_RX_RING:
+ case IGB_IOCTL_MAP_TX_RING:
+ case IGB_IOCTL_MAP_RX_RING:
err = igb_mapbuf(file, argp, cmd);
break;
case IGB_MAPBUF:
+ case IGB_IOCTL_MAPBUF:
err = igb_mapbuf_user(file, argp, cmd);
break;
case IGB_UNMAP_TX_RING:
case IGB_UNMAP_RX_RING:
case IGB_UNMAPBUF:
+ case IGB_IOCTL_UNMAPBUF:
+ case IGB_IOCTL_UNMAP_TX_RING:
+ case IGB_IOCTL_UNMAP_RX_RING:
err = igb_unmapbuf(file, argp, cmd);
break;
case IGB_LINKSPEED:
@@ -10701,8 +10833,11 @@ static void igb_vm_open(struct vm_area_struct *vma)
static void igb_vm_close(struct vm_area_struct *vma)
{
}
-
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
+static int igb_vm_fault(struct vm_fault *fdata)
+#else
static int igb_vm_fault(struct vm_area_struct *area, struct vm_fault *fdata)
+#endif
{
return VM_FAULT_SIGBUS;
}
diff --git a/kmod/igb/igb_param.c b/kmod/igb/igb_param.c
index ca700b93..6c102ed5 100644
--- a/kmod/igb/igb_param.c
+++ b/kmod/igb/igb_param.c
@@ -539,7 +539,7 @@ void igb_check_options(struct igb_adapter *adapter)
.max = (MAX_VMDQ
- adapter->vfs_allocated_count)} }
};
- if ((hw->mac.type != e1000_i210) ||
+ if ((hw->mac.type != e1000_i210) &&
(hw->mac.type != e1000_i211)) {
#ifdef module_param_array
if (num_VMDQ > bd) {
diff --git a/lib/avtp_pipeline/map_h264/openavb_map_h264.c b/lib/avtp_pipeline/map_h264/openavb_map_h264.c
index 5fd114ac..6c2557f7 100755..100644
--- a/lib/avtp_pipeline/map_h264/openavb_map_h264.c
+++ b/lib/avtp_pipeline/map_h264/openavb_map_h264.c
@@ -59,11 +59,11 @@ https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
// Header sizes
#define AVTP_V0_HEADER_SIZE 12
-#define MAP_HEADER_SIZE 12
+#define MAP_HEADER_SIZE 16
#define TOTAL_HEADER_SIZE (AVTP_V0_HEADER_SIZE + MAP_HEADER_SIZE)
-#define MAX_PAYLOAD_SIZE 1412
+#define MAX_PAYLOAD_SIZE 1416
//////
// AVTP Version 0 Header
@@ -84,7 +84,7 @@ https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
//////
// - 4 bytes avtp_timestamp
-#define HIDX_AVTP_TIMESPAMP32 12
+#define HIDX_AVTP_TIMESTAMP32 12
// - 1 bytes Format information = 0x02 RTP Video
#define HIDX_FORMAT8 16
@@ -109,6 +109,9 @@ https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
// - 1 byte Reserved = binary 0x00
#define HIDX_RESV8 23
+// - 4 bytes h264_timestamp
+#define HIDX_H264_TIMESTAMP32 24
+
typedef struct {
/////////////
// Config data
@@ -261,7 +264,7 @@ tx_cb_ret_t openavbMapH264TxCB(media_q_t *pMediaQ, U8 *pData, U32 *dataLen)
return TX_CB_RET_PACKET_NOT_READY;
}
- //pHdr[HIDX_AVTP_TIMESPAMP32] = 0x00; // Set later
+ //pHdr[HIDX_AVTP_TIMESTAMP32] = 0x00; // Set later
pHdr[HIDX_FORMAT8] = 0x02; // RTP Payload type
pHdr[HIDX_FORMAT_SUBTYPE8] = 0x01; // H.264 subtype
pHdr[HIDX_RESV16] = 0x0000; // Reserved
@@ -295,7 +298,7 @@ tx_cb_ret_t openavbMapH264TxCB(media_q_t *pMediaQ, U8 *pData, U32 *dataLen)
else pHdr[HIDX_AVTP_HIDE7_TU1] &= ~0x01; // Clear
// Set the timestamp.
- *(U32 *)(&pHdr[HIDX_AVTP_TIMESPAMP32]) = htonl(openavbAvtpTimeGetAvtpTimestamp(pMediaQItem->pAvtpTime));
+ *(U32 *)(&pHdr[HIDX_AVTP_TIMESTAMP32]) = htonl(openavbAvtpTimeGetAvtpTimestamp(pMediaQItem->pAvtpTime));
if (((media_q_item_map_h264_pub_data_t *)pMediaQItem->pPubMapData)->lastPacket) {
pHdr[HIDX_M31_M21_M11_M01_EVT2_RESV2] = 0x10;;
@@ -304,7 +307,11 @@ tx_cb_ret_t openavbMapH264TxCB(media_q_t *pMediaQ, U8 *pData, U32 *dataLen)
pHdr[HIDX_M31_M21_M11_M01_EVT2_RESV2] = 0x00;
}
- // Copy the JPEG fragment into the outgoing avtp packet.
+ // Set h264_timestamp
+ *(U32 *)(&pHdr[HIDX_H264_TIMESTAMP32]) =
+ htonl(((media_q_item_map_h264_pub_data_t *)pMediaQItem->pPubMapData)->timestamp);
+
+ // Copy the h264 rtp payload into the outgoing avtp packet.
memcpy(pPayload, pMediaQItem->pPubData, pMediaQItem->dataLen);
*(U16 *)(&pHdr[HIDX_STREAM_DATA_LEN16]) = htons(pMediaQItem->dataLen);
@@ -347,7 +354,7 @@ bool openavbMapH264RxCB(media_q_t *pMediaQ, U8 *pData, U32 dataLen)
U8 *pPayload = pData + TOTAL_HEADER_SIZE;
- //pHdr[HIDX_AVTP_TIMESPAMP32]
+ //pHdr[HIDX_AVTP_TIMESTAMP32]
//pHdr[HIDX_FORMAT8]
//pHdr[HIDX_FORMAT_SUBTYPE8]
//pHdr[HIDX_RESV16]
@@ -367,7 +374,7 @@ bool openavbMapH264RxCB(media_q_t *pMediaQ, U8 *pData, U32 dataLen)
media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ);
if (pMediaQItem) {
// Get the timestamp and place it in the media queue item.
- U32 timestamp = ntohl(*(U32 *)(&pHdr[HIDX_AVTP_TIMESPAMP32]));
+ U32 timestamp = ntohl(*(U32 *)(&pHdr[HIDX_AVTP_TIMESTAMP32]));
openavbAvtpTimeSetToTimestamp(pMediaQItem->pAvtpTime, timestamp);
// Set timestamp valid and timestamp uncertain flags
@@ -379,6 +386,9 @@ bool openavbMapH264RxCB(media_q_t *pMediaQ, U8 *pData, U32 dataLen)
else
((media_q_item_map_h264_pub_data_t *)pMediaQItem->pPubMapData)->lastPacket = FALSE;
+ ((media_q_item_map_h264_pub_data_t *)pMediaQItem->pPubMapData)->timestamp =
+ ntohl(*(U32 *)(&pHdr[HIDX_H264_TIMESTAMP32]));
+
if (pMediaQItem->itemSize >= payloadLen) {
memcpy(pMediaQItem->pPubData, pPayload, payloadLen);
pMediaQItem->dataLen = payloadLen;
diff --git a/lib/avtp_pipeline/map_h264/openavb_map_h264_pub.h b/lib/avtp_pipeline/map_h264/openavb_map_h264_pub.h
index 307b4ba1..b547fb93 100755..100644
--- a/lib/avtp_pipeline/map_h264/openavb_map_h264_pub.h
+++ b/lib/avtp_pipeline/map_h264/openavb_map_h264_pub.h
@@ -54,6 +54,8 @@ https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
typedef struct {
// Last fragment of frame flag.
bool lastPacket; // For details see 1722a 9.4.3.1.1 M0 field
+ // The timestamp of h.264 NAL unit fragment.
+ U32 timestamp; // For details see 1722-2016 8.5.3.1 h264_timestamp field
} media_q_item_map_h264_pub_data_t;
#endif // OPENAVB_MAP_H264_PUB_H
diff --git a/lib/avtp_pipeline/platform/Linux/gst_al/gst_al.h b/lib/avtp_pipeline/platform/Linux/gst_al/gst_al.h
index dbdb5b09..7638729a 100644
--- a/lib/avtp_pipeline/platform/Linux/gst_al/gst_al.h
+++ b/lib/avtp_pipeline/platform/Linux/gst_al/gst_al.h
@@ -128,6 +128,14 @@ gboolean gst_al_rtp_buffer_get_marker(GstAlBuf *buf);
*/
void gst_al_rtp_buffer_set_marker(GstAlBuf *buf, gboolean mark);
/**
+ * \brief - gets a RTP buffer timestamp
+ *
+ * \param buf - a RTP buffer
+ *
+ * \return - a RTP buffer timestamp
+ */
+guint32 gst_al_rtp_buffer_get_timestamp(GstAlBuf *buf);
+/**
* \brief - set a RTP buffer params
*
* \param buf - a RTP buffer
diff --git a/lib/avtp_pipeline/platform/Linux/gst_al/gst_al_10.c b/lib/avtp_pipeline/platform/Linux/gst_al/gst_al_10.c
index 0d1898c5..0718e1fc 100644
--- a/lib/avtp_pipeline/platform/Linux/gst_al/gst_al_10.c
+++ b/lib/avtp_pipeline/platform/Linux/gst_al/gst_al_10.c
@@ -185,6 +185,11 @@ void gst_al_rtp_buffer_set_marker(GstAlBuf *buf, gboolean mark)
gst_rtp_buffer_set_marker(&buf->m_rtpbuf, mark);
}
+guint32 gst_al_rtp_buffer_get_timestamp(GstAlBuf *buf)
+{
+ return gst_rtp_buffer_get_timestamp(&buf->m_rtpbuf);
+}
+
void gst_al_rtp_buffer_set_params(GstAlBuf *buf, gint ssrc,
gint payload_type, gint version,
gint sequence)
diff --git a/lib/avtp_pipeline/platform/Linux/intf_h264_gst/openavb_intf_h264_gst.c b/lib/avtp_pipeline/platform/Linux/intf_h264_gst/openavb_intf_h264_gst.c
index 1be182d1..68508e91 100644
--- a/lib/avtp_pipeline/platform/Linux/intf_h264_gst/openavb_intf_h264_gst.c
+++ b/lib/avtp_pipeline/platform/Linux/intf_h264_gst/openavb_intf_h264_gst.c
@@ -340,6 +340,8 @@ bool openavbIntfH264RtpGstTxCB(media_q_t *pMediaQ)
{
((media_q_item_map_h264_pub_data_t *)pMediaQItem->pPubMapData)->lastPacket = FALSE;
}
+ ((media_q_item_map_h264_pub_data_t *)pMediaQItem->pPubMapData)->timestamp =
+ gst_al_rtp_buffer_get_timestamp(txBuf);
openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime);
openavbMediaQHeadPush(pMediaQ);
@@ -545,7 +547,8 @@ bool openavbIntfH264RtpGstRxCB(media_q_t *pMediaQ)
}
memcpy(GST_AL_BUF_DATA(rxBuf), pMediaQItem->pPubData, pMediaQItem->dataLen);
- GST_AL_BUFFER_TIMESTAMP(rxBuf) = GST_CLOCK_TIME_NONE;
+ //GST_AL_BUFFER_TIMESTAMP(rxBuf) = GST_CLOCK_TIME_NONE;
+ GST_AL_BUFFER_TIMESTAMP(rxBuf) = ((media_q_item_map_h264_pub_data_t *)pMediaQItem->pPubMapData)->timestamp;
GST_AL_BUFFER_DURATION(rxBuf) = GST_CLOCK_TIME_NONE;
if ( ((media_q_item_map_h264_pub_data_t *)pMediaQItem->pPubMapData)->lastPacket )
{
diff --git a/lib/igb/e1000_hw.h b/lib/igb/e1000_hw.h
index 1a3c46c7..1732d502 100644
--- a/lib/igb/e1000_hw.h
+++ b/lib/igb/e1000_hw.h
@@ -71,6 +71,7 @@ struct e1000_hw;
#define E1000_DEV_ID_I210_FIBER 0x1536
#define E1000_DEV_ID_I210_SERDES 0x1537
#define E1000_DEV_ID_I210_SGMII 0x1538
+#define E1000_DEV_ID_I210_AUTOMOTIVE 0x15F6
#define E1000_DEV_ID_I210_COPPER_FLASHLESS 0x157B
#define E1000_DEV_ID_I210_SERDES_FLASHLESS 0x157C
#define E1000_DEV_ID_I211_COPPER 0x1539
diff --git a/lib/igb/igb.c b/lib/igb/igb.c
index d59a0fcb..96e18951 100644
--- a/lib/igb/igb.c
+++ b/lib/igb/igb.c
@@ -79,6 +79,7 @@ static igb_vendor_info_t igb_vendor_info_array[] = {
{ 0x8086, E1000_DEV_ID_I210_SERDES_FLASHLESS,
PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_I210_SGMII, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_I210_AUTOMOTIVE, PCI_ANY_ID, PCI_ANY_ID, 0},
/* required last entry */
{ 0, 0, 0, 0, 0}
};
@@ -681,14 +682,15 @@ int igb_dma_malloc_page(device_t *dev, struct igb_dma_alloc *dma)
error = errno;
goto err;
}
- error = ioctl(adapter->ldev, IGB_MAPBUF, &ubuf);
+ error = ioctl(adapter->ldev, IGB_IOCTL_MAPBUF, &ubuf);
if (igb_unlock(dev) != 0) {
error = errno;
goto err;
}
if (error < 0) {
- error = -ENOMEM;
+ if(error != -EINVAL)
+ error = -ENOMEM;
goto err;
}
@@ -699,7 +701,7 @@ int igb_dma_malloc_page(device_t *dev, struct igb_dma_alloc *dma)
PROT_READ | PROT_WRITE,
MAP_SHARED,
adapter->ldev,
- ubuf.physaddr);
+ ubuf.pa);
if (dma->dma_vaddr == MAP_FAILED)
error = -ENOMEM;
@@ -729,7 +731,7 @@ void igb_dma_free_page(device_t *dev, struct igb_dma_alloc *dma)
if (igb_lock(dev) != 0)
goto err;
- ioctl(adapter->ldev, IGB_UNMAPBUF, &ubuf);
+ ioctl(adapter->ldev, IGB_IOCTL_UNMAPBUF, &ubuf);
if (igb_unlock(dev) != 0)
goto err;
@@ -767,8 +769,11 @@ static int igb_allocate_queues(struct adapter *adapter)
for (i = 0; i < adapter->num_queues; i++) {
ubuf.queue = i;
- error = ioctl(dev, IGB_MAPRING, &ubuf);
+ error = ioctl(dev, IGB_IOCTL_MAPRING, &ubuf);
if (error < 0) {
+ if(error == -EINVAL)
+ goto tx_fail;
+
error = EBUSY;
goto tx_desc;
}
@@ -784,7 +789,7 @@ static int igb_allocate_queues(struct adapter *adapter)
(struct e1000_tx_desc *)mmap(NULL, ubuf.mmap_size,
PROT_READ | PROT_WRITE,
MAP_SHARED, adapter->ldev,
- ubuf.physaddr);
+ ubuf.pa);
if (adapter->tx_rings[i].tx_base == MAP_FAILED) {
error = -ENOMEM;
@@ -828,7 +833,7 @@ tx_desc:
munmap(adapter->tx_rings[i].tx_base,
adapter->tx_rings[i].txdma.mmap_size);
ubuf.queue = i;
- ioctl(dev, IGB_UNMAPRING, &ubuf);
+ ioctl(dev, IGB_IOCTL_UNMAPRING, &ubuf);
};
tx_fail:
free(adapter->tx_rings);
@@ -908,7 +913,7 @@ static void igb_free_transmit_structures(struct adapter *adapter)
munmap(adapter->tx_rings[i].tx_base,
adapter->tx_rings[i].txdma.mmap_size);
ubuf.queue = i;
- ioctl(adapter->ldev, IGB_UNMAPRING, &ubuf);
+ ioctl(adapter->ldev, IGB_IOCTL_UNMAPRING, &ubuf);
free(adapter->tx_rings[i].tx_buffers);
}
@@ -1352,8 +1357,12 @@ static int igb_allocate_rx_queues(struct adapter *adapter)
}
ubuf.queue = i;
- error = ioctl(dev, IGB_MAP_RX_RING, &ubuf);
+ error = ioctl(dev, IGB_IOCTL_MAP_RX_RING, &ubuf);
if (error < 0) {
+
+ if(error == -EINVAL)
+ goto rx_fail;
+
error = EBUSY;
goto rx_desc;
}
@@ -1367,7 +1376,7 @@ static int igb_allocate_rx_queues(struct adapter *adapter)
adapter->rx_rings[i].rx_base = NULL;
adapter->rx_rings[i].rx_base =
mmap(NULL, ubuf.mmap_size, PROT_READ | PROT_WRITE,
- MAP_SHARED, adapter->ldev, ubuf.physaddr);
+ MAP_SHARED, adapter->ldev, ubuf.pa);
if (adapter->rx_rings[i].rx_base == MAP_FAILED) {
error = -ENOMEM;
@@ -1416,7 +1425,7 @@ rx_desc:
munmap(adapter->rx_rings[i].rx_base,
adapter->rx_rings[i].rxdma.mmap_size);
ubuf.queue = i;
- ioctl(dev, IGB_UNMAP_RX_RING, &ubuf);
+ ioctl(dev, IGB_IOCTL_UNMAP_RX_RING, &ubuf);
sem_destroy(&adapter->rx_rings[i].lock);
};
@@ -1571,7 +1580,7 @@ static void igb_free_receive_structures(struct adapter *adapter)
munmap(rxr->rx_base, rxr->rxdma.mmap_size);
}
ubuf.queue = i;
- ioctl(adapter->ldev, IGB_UNMAP_RX_RING, &ubuf);
+ ioctl(adapter->ldev, IGB_IOCTL_UNMAP_RX_RING, &ubuf);
igb_free_receive_buffers(rxr);
(void)sem_destroy(&adapter->rx_rings[i].lock);
diff --git a/lib/igb/igb_internal.h b/lib/igb/igb_internal.h
index b99ee063..24dd7496 100644
--- a/lib/igb/igb_internal.h
+++ b/lib/igb/igb_internal.h
@@ -216,6 +216,21 @@ typedef struct _igb_vendor_info_t {
#define IGB_MAP_RX_RING _IOW('E', 207, int)
#define IGB_UNMAP_RX_RING _IOW('E', 208, int)
+/*set of newly defined ioctl calls - new libigb compatibility
+ each of them is an equivalent of the old ioctl
+ changed numberiong convention: new_ioctl = old_ioctl + 100*/
+
+#define IGB_IOCTL_MAPRING _IOW('E', 302, int)
+#define IGB_IOCTL_MAP_TX_RING IGB_IOCTL_MAPRING
+#define IGB_IOCTL_UNMAPRING _IOW('E', 303, int)
+#define IGB_IOCTL_UNMAP_TX_RING IGB_IOCTL_UNMAPRING
+#define IGB_IOCTL_MAPBUF _IOW('E', 304, int)
+#define IGB_IOCTL_UNMAPBUF _IOW('E', 305, int)
+#define IGB_IOCTL_MAP_RX_RING _IOW('E', 307, int)
+#define IGB_IOCTL_UNMAP_RX_RING _IOW('E', 308, int)
+
+/*END*/
+
#define IGB_BIND_NAMESZ 24
struct igb_bind_cmd {
@@ -227,6 +242,7 @@ struct igb_buf_cmd {
u_int64_t physaddr; /* dma_addr_t is 64-bit */
unsigned int queue;
unsigned int mmap_size;
+ u_int64_t pa;
};
struct igb_link_cmd {
diff --git a/lib/libavtp/HACKING.md b/lib/libavtp/HACKING.md
new file mode 100644
index 00000000..8b93064a
--- /dev/null
+++ b/lib/libavtp/HACKING.md
@@ -0,0 +1,73 @@
+# Contributing
+
+If you have a bug fixed or added support for some AVTP feature, your patches
+are welcomed! In order to get your patches merged faster, please follow the
+guidelines:
+
+* Check if all patches are following the coding style from libavtp project. See
+section 'Coding Style' for more information.
+
+* Before submitting your patch to review, make sure it doesn't break any unit
+test. See section 'Running Unit Tests' for information about how to build and
+run unit tests.
+
+* Besides the bugfix/feature itself, also provide unit test covering the code
+you're contributing. See section 'Code Coverage' to check how you can easily
+generate coverage reports and see where you need to work on to get your code
+covered.
+
+* If your patch adds new public APIs to libavtp, please also provide patches
+adding example applications (or modify an existing one if it makes sense)
+which demonstrate how to use the new APIs.
+
+* Make sure the author's name and email are set properly.
+
+# Coding Style
+
+The coding style from libavtp is pretty much the same from Linux kernel
+described in https://www.kernel.org/doc/html/latest/process/coding-style.html.
+
+The style for line wrapping is to indent as far as possible to the right
+without hitting the 80 columns limit.
+
+Example:
+
+```
+/* Correct */
+int avtp_aaf_pdu_set(struct avtp_stream_pdu *pdu, enum avtp_aaf_field field,
+ uint64_t val)
+
+/* Wrong */
+int avtp_aaf_pdu_set(struct avtp_stream_pdu *pdu, enum avtp_aaf_field field,
+ uint64_t val)
+```
+
+# Running Unit Tests
+
+Unit tests required libcmocka so make sure you have cmocka packages installed
+in your system.
+
+Run the following command to build and run all unit tests. Make sure the build
+system files are generated already, see section 'Build' in the README.md file
+for more information.
+
+```
+$ ninja -C build test
+```
+
+# Code Coverage
+
+Meson build system provides some built-in code coverage support based on `lcov`
+so make sure you have this package installed on your system in order to
+generate coverage reports.
+
+To generate html reports run the following commands:
+
+```
+$ rm -rf build/
+$ meson build -Db_coverage=true
+$ ninja -C build/ test
+$ ninja -C build/ coverage-html
+```
+
+The coverage report can be found in build/meson-logs/coveragereport/ directory.
diff --git a/lib/libavtp/LICENSE b/lib/libavtp/LICENSE
new file mode 100644
index 00000000..f0eb281f
--- /dev/null
+++ b/lib/libavtp/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2017, Intel Corporation
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * 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.
+ * Neither the name of 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.
diff --git a/lib/libavtp/README.md b/lib/libavtp/README.md
new file mode 100644
index 00000000..4aa025b0
--- /dev/null
+++ b/lib/libavtp/README.md
@@ -0,0 +1,48 @@
+# About
+
+Open source implementation of Audio Video Transport Protocol (AVTP) specified
+in IEEE 1722-2016 spec.
+
+Libavtp is under BSD License. For more information see LICENSE file.
+
+# Build
+
+Before building libavtp make sure you have all the required software installed
+in your system. Below are the requirements and their tested versions:
+
+* Meson >= 0.43
+* Ninja >= 1.8.2
+
+The first step to build libavtp is to generate the build system files.
+
+```
+$ meson build
+```
+
+Then build libavtp by running the following command. The building artifacts
+will be created under the build/ in the top-level directory.
+
+```
+$ ninja -C build
+```
+
+To install libavtp on your system run:
+```
+$ sudo ninja -C build install
+```
+
+# AVTP Formats Support
+
+AVTP protocol defines several AVTPDU type formats (see Table 6 from IEEE
+1722-2016 spec). Libavtp doesn't support all of them yet. The list of supported
+formarts is:
+* AAF (PCM encapsulation only)
+
+# Examples
+
+The `examples/` directory in the top-level directory provides example
+applications which demonstrate the libavtp functionalities. To build an
+example application run `$ ninja -C build <example name>`.
+
+Information about what exactly each example application does and how it works
+is provided in the beginning of the .c file from each application.
diff --git a/lib/libavtp/examples/aaf-listener.c b/lib/libavtp/examples/aaf-listener.c
new file mode 100644
index 00000000..61578482
--- /dev/null
+++ b/lib/libavtp/examples/aaf-listener.c
@@ -0,0 +1,536 @@
+/*
+ * Copyright (c) 2017, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of 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.
+ */
+
+/* AAF Listener example.
+ *
+ * This example implements a very simple AAF listener application which
+ * receives AFF packets from the network, retrieves the PCM samples, and
+ * writes them to stdout once the presentation time is reached.
+ *
+ * For simplicity, the example accepts only AAF packets with the following
+ * specification:
+ * - Sample format: 16-bit little endian
+ * - Sample rate: 48 kHz
+ * - Number of channels: 2 (stereo)
+ *
+ * TSN stream parameters such as destination mac address are passed via
+ * command-line arguments. Run 'aaf-listener --help' for more information.
+ *
+ * This example relies on the system clock to schedule PCM samples for
+ * playback. So make sure the system clock is synchronized with the PTP
+ * Hardware Clock (PHC) from your NIC and that the PHC is synchronized with
+ * the PTP time from the network. For further information on how to synchronize
+ * those clocks see ptp4l(8) and phc2sys(8) man pages.
+ *
+ * The easiest way to use this example is combining it with 'aplay' tool
+ * provided by alsa-utils. 'aplay' reads a PCM stream from stdin and sends it
+ * to a ALSA playback device (e.g. your speaker). So, to play Audio from a TSN
+ * stream, you should do something like this:
+ *
+ * $ aaf-listener <args> | aplay -f dat -t raw -D <playback-device>
+ */
+
+#include <assert.h>
+#include <argp.h>
+#include <arpa/inet.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/timerfd.h>
+#include <unistd.h>
+
+#include "avtp.h"
+#include "avtp_aaf.h"
+
+#define STREAM_ID 0xAABBCCDDEEFF0001
+#define SAMPLE_SIZE 2 /* Sample size in bytes. */
+#define NUM_CHANNELS 2
+#define DATA_LEN (SAMPLE_SIZE * NUM_CHANNELS)
+#define PDU_SIZE (sizeof(struct avtp_stream_pdu) + DATA_LEN)
+#define NSEC_PER_SEC 1000000000ULL
+
+struct sample_entry {
+ STAILQ_ENTRY(sample_entry) entries;
+
+ struct timespec tspec;
+ uint8_t pcm_sample[DATA_LEN];
+};
+
+static STAILQ_HEAD(sample_queue, sample_entry) samples;
+static char ifname[IFNAMSIZ];
+static uint8_t macaddr[ETH_ALEN];
+static uint8_t expected_seq;
+
+static struct argp_option options[] = {
+ {"dst-addr", 'd', "MACADDR", 0, "Stream Destination MAC address" },
+ {"ifname", 'i', "IFNAME", 0, "Network Interface" },
+ { 0 }
+};
+
+static error_t parser(int key, char *arg, struct argp_state *state)
+{
+ int res;
+
+ switch (key) {
+ case 'd':
+ res = sscanf(arg, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ &macaddr[0], &macaddr[1], &macaddr[2],
+ &macaddr[3], &macaddr[4], &macaddr[5]);
+ if (res != 6) {
+ fprintf(stderr, "Invalid address\n");
+ exit(EXIT_FAILURE);
+ }
+
+ break;
+ case 'i':
+ strncpy(ifname, arg, sizeof(ifname) - 1);
+ break;
+ }
+
+ return 0;
+}
+
+static struct argp argp = { options, parser };
+
+static int arm_timer(int fd, struct timespec *tspec)
+{
+ int res;
+ struct itimerspec timer_spec = { 0 };
+
+ timer_spec.it_value.tv_sec = tspec->tv_sec;
+ timer_spec.it_value.tv_nsec = tspec->tv_nsec;
+
+ res = timerfd_settime(fd, TFD_TIMER_ABSTIME, &timer_spec, NULL);
+ if (res < 0) {
+ perror("Failed to set timer");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Schedule 'pcm_sample' to be presented at time specified by 'tspec'. */
+static int schedule_sample(int fd, struct timespec *tspec, uint8_t *pcm_sample)
+{
+ struct sample_entry *entry;
+
+ entry = malloc(sizeof(*entry));
+ if (!entry) {
+ fprintf(stderr, "Failed to allocate memory\n");
+ return -1;
+ }
+
+ entry->tspec.tv_sec = tspec->tv_sec;
+ entry->tspec.tv_nsec = tspec->tv_nsec;
+ memcpy(entry->pcm_sample, pcm_sample, DATA_LEN);
+
+ STAILQ_INSERT_TAIL(&samples, entry, entries);
+
+ /* If this was the first entry inserted onto the queue, we need to arm
+ * the timer.
+ */
+ if (STAILQ_FIRST(&samples) == entry) {
+ int res;
+
+ res = arm_timer(fd, tspec);
+ if (res < 0) {
+ STAILQ_REMOVE(&samples, entry, sample_entry, entries);
+ free(entry);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int present_sample(uint8_t *pcm_sample)
+{
+ ssize_t n;
+
+ n = write(STDOUT_FILENO, pcm_sample, DATA_LEN);
+ if (n < 0 || n != DATA_LEN) {
+ perror("Failed to write()");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int setup_socket(void)
+{
+ int fd, res;
+ struct ifreq req;
+ struct packet_mreq mreq;
+
+ struct sockaddr_ll sk_addr = {
+ .sll_family = AF_PACKET,
+ .sll_protocol = htons(ETH_P_TSN),
+ };
+
+ fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_TSN));
+ if (fd < 0) {
+ perror("Failed to open socket");
+ return -1;
+ }
+
+ snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", ifname);
+ res = ioctl(fd, SIOCGIFINDEX, &req);
+ if (res < 0) {
+ perror("Failed to get interface index");
+ goto err;
+ }
+
+ sk_addr.sll_ifindex = req.ifr_ifindex;
+
+ res = bind(fd, (struct sockaddr *) &sk_addr, sizeof(sk_addr));
+ if (res < 0) {
+ perror("Couldn't bind() to interface");
+ goto err;
+ }
+
+ mreq.mr_ifindex = sk_addr.sll_ifindex;
+ mreq.mr_type = PACKET_MR_MULTICAST;
+ mreq.mr_alen = ETH_ALEN;
+ memcpy(&mreq.mr_address, macaddr, ETH_ALEN);
+
+ res = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
+ &mreq, sizeof(struct packet_mreq));
+ if (res < 0) {
+ perror("Couldn't set PACKET_ADD_MEMBERSHIP");
+ goto err;
+ }
+
+ return fd;
+
+err:
+ close(fd);
+ return -1;
+}
+
+static bool is_valid_packet(struct avtp_stream_pdu *pdu)
+{
+ struct avtp_common_pdu *common = (struct avtp_common_pdu *) pdu;
+ uint64_t val64;
+ uint32_t val32;
+ int res;
+
+ res = avtp_pdu_get(common, AVTP_FIELD_SUBTYPE, &val32);
+ if (res < 0) {
+ fprintf(stderr, "Failed to get subtype field: %d\n", res);
+ return false;
+ }
+ if (val32 != AVTP_SUBTYPE_AAF) {
+ fprintf(stderr, "Subtype mismatch: expected %u, got %u\n",
+ AVTP_FIELD_SUBTYPE, val32);
+ return false;
+ }
+
+ res = avtp_pdu_get(common, AVTP_FIELD_VERSION, &val32);
+ if (res < 0) {
+ fprintf(stderr, "Failed to get version field: %d\n", res);
+ return false;
+ }
+ if (val32 != 0) {
+ fprintf(stderr, "Version mismatch: expected %u, got %u\n",
+ 0, val32);
+ return false;
+ }
+
+ res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_TV, &val64);
+ if (res < 0) {
+ fprintf(stderr, "Failed to get tv field: %d\n", res);
+ return false;
+ }
+ if (val64 != 1) {
+ fprintf(stderr, "tv mismatch: expected %u, got %lu\n",
+ 1, val64);
+ return false;
+ }
+
+ res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_SP, &val64);
+ if (res < 0) {
+ fprintf(stderr, "Failed to get sp field: %d\n", res);
+ return false;
+ }
+ if (val64 != AVTP_AAF_PCM_SP_NORMAL) {
+ fprintf(stderr, "tv mismatch: expected %u, got %lu\n",
+ 1, val64);
+ return false;
+ }
+
+ res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_STREAM_ID, &val64);
+ if (res < 0) {
+ fprintf(stderr, "Failed to get stream ID field: %d\n", res);
+ return false;
+ }
+ if (val64 != STREAM_ID) {
+ fprintf(stderr, "Stream ID mismatch: expected %lu, got %lu\n",
+ STREAM_ID, val64);
+ return false;
+ }
+
+ res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_SEQ_NUM, &val64);
+ if (res < 0) {
+ fprintf(stderr, "Failed to get sequence num field: %d\n", res);
+ return false;
+ }
+
+ if (val64 != expected_seq) {
+ /* If we have a sequence number mismatch, we simply log the
+ * issue and continue to process the packet. We don't want to
+ * invalidate it since it is a valid packet after all.
+ */
+ fprintf(stderr, "Sequence number mismatch: expected %u, got %lu\n",
+ expected_seq, val64);
+ expected_seq = val64;
+ }
+
+ expected_seq++;
+
+ res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_FORMAT, &val64);
+ if (res < 0) {
+ fprintf(stderr, "Failed to get format field: %d\n", res);
+ return false;
+ }
+ if (val64 != AVTP_AAF_FORMAT_INT_16BIT) {
+ fprintf(stderr, "Format mismatch: expected %u, got %lu\n",
+ AVTP_AAF_FORMAT_INT_16BIT, val64);
+ return false;
+ }
+
+ res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_NSR, &val64);
+ if (res < 0) {
+ fprintf(stderr, "Failed to get sample rate field: %d\n", res);
+ return false;
+ }
+ if (val64 != AVTP_AAF_PCM_NSR_48KHZ) {
+ fprintf(stderr, "Sample rate mismatch: expected %u, got %lu",
+ AVTP_AAF_PCM_NSR_48KHZ, val64);
+ return false;
+ }
+
+ res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_CHAN_PER_FRAME, &val64);
+ if (res < 0) {
+ fprintf(stderr, "Failed to get channels field: %d\n", res);
+ return false;
+ }
+ if (val64 != NUM_CHANNELS) {
+ fprintf(stderr, "Channels mismatch: expected %u, got %lu\n",
+ NUM_CHANNELS, val64);
+ return false;
+ }
+
+ res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_BIT_DEPTH, &val64);
+ if (res < 0) {
+ fprintf(stderr, "Failed to get depth field: %d\n", res);
+ return false;
+ }
+ if (val64 != 16) {
+ fprintf(stderr, "Depth mismatch: expected %u, got %lu\n",
+ 16, val64);
+ return false;
+ }
+
+ res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_STREAM_DATA_LEN, &val64);
+ if (res < 0) {
+ fprintf(stderr, "Failed to get data_len field: %d\n", res);
+ return false;
+ }
+ if (val64 != DATA_LEN) {
+ fprintf(stderr, "Data len mismatch: expected %u, got %lu\n",
+ DATA_LEN, val64);
+ return false;
+ }
+
+ return true;
+}
+
+static int get_presentation_time(struct avtp_stream_pdu *pdu,
+ struct timespec *tspec)
+{
+ int res;
+ uint64_t avtp_time, ptime, now;
+
+ res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_TIMESTAMP, &avtp_time);
+ if (res < 0) {
+ fprintf(stderr, "Failed to get AVTP time from PDU\n");
+ return -1;
+ }
+
+ res = clock_gettime(CLOCK_REALTIME, tspec);
+ if (res < 0) {
+ perror("Failed to get time from PHC");
+ return -1;
+ }
+
+ now = (tspec->tv_sec * NSEC_PER_SEC) + tspec->tv_nsec;
+
+ /* The avtp_timestamp within AAF packet is the lower part (32
+ * less-significant bits) from presentation time calculated by the
+ * talker.
+ */
+ ptime = (now & 0xFFFFFFFF00000000ULL) | avtp_time;
+
+ /* If 'ptime' is less than the 'now', it means the higher part
+ * from 'ptime' needs to be incremented by 1 in order to recover the
+ * presentation time set by the talker.
+ */
+ if (ptime < now)
+ ptime += (1ULL << 32);
+
+ tspec->tv_sec = ptime / NSEC_PER_SEC;
+ tspec->tv_nsec = ptime % NSEC_PER_SEC;
+
+ return 0;
+}
+
+static int new_packet(int sk_fd, int timer_fd)
+{
+ int res;
+ ssize_t n;
+ struct timespec tspec;
+ struct avtp_stream_pdu *pdu = alloca(PDU_SIZE);
+
+ memset(pdu, 0, PDU_SIZE);
+
+ n = recv(sk_fd, pdu, PDU_SIZE, 0);
+ if (n < 0 || n != PDU_SIZE) {
+ perror("Failed to receive data");
+ return -1;
+ }
+
+ if (!is_valid_packet(pdu)) {
+ fprintf(stderr, "Dropping packet\n");
+ return 0;
+ }
+
+ res = get_presentation_time(pdu, &tspec);
+ if (res < 0)
+ return -1;
+
+ res = schedule_sample(timer_fd, &tspec, pdu->avtp_payload);
+ if (res < 0)
+ return -1;
+
+ return 0;
+}
+
+static int timeout(int fd)
+{
+ int res;
+ ssize_t n;
+ uint64_t expirations;
+ struct sample_entry *entry;
+
+ n = read(fd, &expirations, sizeof(uint64_t));
+ if (n < 0) {
+ perror("Failed to read timerfd");
+ return -1;
+ }
+
+ assert(expirations == 1);
+
+ entry = STAILQ_FIRST(&samples);
+ assert(entry != NULL);
+
+ res = present_sample(entry->pcm_sample);
+ if (res < 0)
+ return -1;
+
+ STAILQ_REMOVE_HEAD(&samples, entries);
+ free(entry);
+
+ if (!STAILQ_EMPTY(&samples)) {
+ entry = STAILQ_FIRST(&samples);
+
+ res = arm_timer(fd, &entry->tspec);
+ if (res < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int sk_fd, timer_fd, res;
+ struct pollfd fds[2];
+
+ argp_parse(&argp, argc, argv, 0, NULL, NULL);
+
+ STAILQ_INIT(&samples);
+
+ sk_fd = setup_socket();
+ if (sk_fd < 0)
+ return 1;
+
+ timer_fd = timerfd_create(CLOCK_REALTIME, 0);
+ if (timer_fd < 0) {
+ close(sk_fd);
+ return 1;
+ }
+
+ fds[0].fd = sk_fd;
+ fds[0].events = POLLIN;
+ fds[1].fd = timer_fd;
+ fds[1].events = POLLIN;
+
+ while (1) {
+ res = poll(fds, 2, -1);
+ if (res < 0) {
+ perror("Failed to poll() fds");
+ goto err;
+ }
+
+ if (fds[0].revents & POLLIN) {
+ res = new_packet(sk_fd, timer_fd);
+ if (res < 0)
+ goto err;
+ }
+
+ if (fds[1].revents & POLLIN) {
+ res = timeout(timer_fd);
+ if (res < 0)
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ close(sk_fd);
+ close(timer_fd);
+ return 1;
+}
diff --git a/lib/libavtp/examples/aaf-talker.c b/lib/libavtp/examples/aaf-talker.c
new file mode 100644
index 00000000..37ae1dfb
--- /dev/null
+++ b/lib/libavtp/examples/aaf-talker.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2017, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of 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.
+ */
+
+/* AAF Talker example.
+ *
+ * This example implements a very simple AAF talker application which reads
+ * a PCM stream from stdin, creates AAF packets and transmit them via the
+ * network.
+ *
+ * For simplicity, the example supports only one set of PCM parameters:
+ * - Sample format: 16-bit little endian
+ * - Sample rate: 48 kHz
+ * - Number of channels: 2 (stereo)
+ *
+ * TSN stream parameters (e.g. destination mac address, traffic priority) are
+ * passed via command-line arguments. Run 'aaf-talker --help' for more
+ * information.
+ *
+ * In order to have this example working properly, make sure you have
+ * configured FQTSS feature from your NIC according (for further information
+ * see tc-cbs(8)). Also, this example relies on system clock to set the AVTP
+ * timestamp so make sure it is synchronized with the PTP Hardware Clock (PHC)
+ * from your NIC and that the PHC is synchronized with the network clock. For
+ * further information see ptp4l(8) and phc2sys(8).
+ *
+ * The easiest way to use this example is combining it with 'arecord' tool
+ * provided by alsa-utils. 'arecord' reads the PCM stream from a capture ALSA
+ * device (e.g. your microphone) and writes it to stdout. So to stream Audio
+ * captured from your mic to a TSN network you should do something like this:
+ *
+ * $ arecord -f dat -t raw -D <capture-device> | aaf-talker <args>
+ */
+
+#include <alloca.h>
+#include <argp.h>
+#include <arpa/inet.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "avtp.h"
+#include "avtp_aaf.h"
+
+#define STREAM_ID 0xAABBCCDDEEFF0001
+#define SAMPLE_SIZE 2 /* Sample size in bytes. */
+#define NUM_CHANNELS 2
+#define DATA_LEN (SAMPLE_SIZE * NUM_CHANNELS)
+#define PDU_SIZE (sizeof(struct avtp_stream_pdu) + DATA_LEN)
+#define NSEC_PER_SEC 1000000000ULL
+#define NSEC_PER_MSEC 1000000ULL
+
+static char ifname[IFNAMSIZ];
+static uint8_t macaddr[ETH_ALEN];
+static int priority = -1;
+static int max_transit_time;
+
+static struct argp_option options[] = {
+ {"dst-addr", 'd', "MACADDR", 0, "Stream Destination MAC address" },
+ {"ifname", 'i', "IFNAME", 0, "Network Interface" },
+ {"max-transit-time", 'm', "MSEC", 0, "Maximum Transit Time in ms" },
+ {"prio", 'p', "NUM", 0, "SO_PRIORITY to be set in socket" },
+ { 0 }
+};
+
+static error_t parser(int key, char *arg, struct argp_state *state)
+{
+ int res;
+
+ switch (key) {
+ case 'd':
+ res = sscanf(arg, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ &macaddr[0], &macaddr[1], &macaddr[2],
+ &macaddr[3], &macaddr[4], &macaddr[5]);
+ if (res != 6) {
+ fprintf(stderr, "Invalid address\n");
+ exit(EXIT_FAILURE);
+ }
+
+ break;
+ case 'i':
+ strncpy(ifname, arg, sizeof(ifname) - 1);
+ break;
+ case 'm':
+ max_transit_time = atoi(arg);
+ break;
+ case 'p':
+ priority = atoi(arg);
+ break;
+ }
+
+ return 0;
+}
+
+static struct argp argp = { options, parser };
+
+static int setup_socket(void)
+{
+ int fd, res;
+
+ fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_TSN));
+ if (fd < 0) {
+ perror("Failed to open socket");
+ return -1;
+ }
+
+ if (priority != -1) {
+ res = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority,
+ sizeof(priority));
+ if (res < 0) {
+ perror("Failed to set priority");
+ goto err;
+ }
+
+ }
+
+ return fd;
+
+err:
+ close(fd);
+ return -1;
+}
+
+static int calculate_avtp_time(uint32_t *avtp_time)
+{
+ int res;
+ struct timespec tspec;
+ uint64_t ptime;
+
+ res = clock_gettime(CLOCK_REALTIME, &tspec);
+ if (res < 0) {
+ perror("Failed to get time");
+ return -1;
+ }
+
+ ptime = (tspec.tv_sec * NSEC_PER_SEC) +
+ (max_transit_time * NSEC_PER_MSEC) + tspec.tv_nsec;
+
+ *avtp_time = ptime % (1ULL << 32);
+
+ return 0;
+}
+
+static int init_pdu(struct avtp_stream_pdu *pdu)
+{
+ int res;
+
+ res = avtp_aaf_pdu_init(pdu);
+ if (res < 0)
+ return -1;
+
+ res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_TV, 1);
+ if (res < 0)
+ return -1;
+
+ res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_STREAM_ID, STREAM_ID);
+ if (res < 0)
+ return -1;
+
+ res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_FORMAT,
+ AVTP_AAF_FORMAT_INT_16BIT);
+ if (res < 0)
+ return -1;
+
+ res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_NSR,
+ AVTP_AAF_PCM_NSR_48KHZ);
+ if (res < 0)
+ return -1;
+
+ res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_CHAN_PER_FRAME,
+ NUM_CHANNELS);
+ if (res < 0)
+ return -1;
+
+ res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_BIT_DEPTH, 16);
+ if (res < 0)
+ return -1;
+
+ res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_STREAM_DATA_LEN, DATA_LEN);
+ if (res < 0)
+ return -1;
+
+ res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_SP, AVTP_AAF_PCM_SP_NORMAL);
+ if (res < 0)
+ return -1;
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int fd, res;
+ struct ifreq req;
+ struct sockaddr_ll sk_addr;
+ struct avtp_stream_pdu *pdu = alloca(PDU_SIZE);
+ uint8_t seq_num = 0;
+
+ argp_parse(&argp, argc, argv, 0, NULL, NULL);
+
+ fd = setup_socket();
+ if (fd < 0)
+ return 1;
+
+ snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", ifname);
+ res = ioctl(fd, SIOCGIFINDEX, &req);
+ if (res < 0) {
+ perror("Failed to get interface index");
+ goto err;
+ }
+
+ sk_addr.sll_family = AF_PACKET;
+ sk_addr.sll_protocol = htons(ETH_P_TSN);
+ sk_addr.sll_halen = ETH_ALEN;
+ sk_addr.sll_ifindex = req.ifr_ifindex;
+ memcpy(&sk_addr.sll_addr, macaddr, ETH_ALEN);
+
+ res = init_pdu(pdu);
+ if (res < 0)
+ goto err;
+
+ while (1) {
+ ssize_t n;
+ uint32_t avtp_time;
+
+ memset(pdu->avtp_payload, 0, DATA_LEN);
+
+ n = read(STDIN_FILENO, pdu->avtp_payload, DATA_LEN);
+ if (n == 0)
+ break;
+
+ if (n != DATA_LEN) {
+ fprintf(stderr, "read %zd bytes, expected %d\n",
+ n, DATA_LEN);
+ }
+
+ res = calculate_avtp_time(&avtp_time);
+ if (res < 0) {
+ fprintf(stderr, "Failed to calculate avtp time\n");
+ goto err;
+ }
+
+ res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_TIMESTAMP,
+ avtp_time);
+ if (res < 0)
+ goto err;
+
+ res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_SEQ_NUM, seq_num++);
+ if (res < 0)
+ goto err;
+
+ n = sendto(fd, pdu, PDU_SIZE, 0,
+ (struct sockaddr *) &sk_addr, sizeof(sk_addr));
+ if (n < 0) {
+ perror("Failed to send data");
+ goto err;
+ }
+
+ if (n != PDU_SIZE) {
+ fprintf(stderr, "wrote %zd bytes, expected %zd\n",
+ n, PDU_SIZE);
+ }
+ }
+
+ close(fd);
+ return 0;
+
+err:
+ close(fd);
+ return 1;
+}
diff --git a/lib/libavtp/include/avtp.h b/lib/libavtp/include/avtp.h
new file mode 100644
index 00000000..c999c1eb
--- /dev/null
+++ b/lib/libavtp/include/avtp.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2017, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of 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.
+ */
+
+#pragma once
+
+#include <errno.h>
+#include <stdint.h>
+
+/* AVTP subtypes values. For further information refer to section 4.4.3.2 from
+ * IEEE 1722-2016 spec.
+ */
+#define AVTP_SUBTYPE_61883_IIDC 0x00
+#define AVTP_SUBTYPE_MMA_STREAM 0x01
+#define AVTP_SUBTYPE_AAF 0x02
+#define AVTP_SUBTYPE_CVF 0x03
+#define AVTP_SUBTYPE_CRF 0x04
+#define AVTP_SUBTYPE_TSCF 0x05
+#define AVTP_SUBTYPE_SVF 0x06
+#define AVTP_SUBTYPE_RVF 0x07
+#define AVTP_SUBTYPE_AEF_CONTINUOUS 0x6E
+#define AVTP_SUBTYPE_VSF_STREAM 0x6F
+#define AVTP_SUBTYPE_EF_STREAM 0x7F
+#define AVTP_SUBTYPE_NTSCF 0x82
+#define AVTP_SUBTYPE_ESCF 0xEC
+#define AVTP_SUBTYPE_EECF 0xED
+#define AVTP_SUBTYPE_AEF_DISCRETE 0xEE
+#define AVTP_SUBTYPE_ADP 0xFA
+#define AVTP_SUBTYPE_AECP 0xFB
+#define AVTP_SUBTYPE_ACMP 0xFC
+#define AVTP_SUBTYPE_MAAP 0xFE
+#define AVTP_SUBTYPE_EF_CONTROL 0xFF
+
+/* XXX: Fields from PDU structs should not be read or written directly since
+ * they are encoded in Network order which may be different from the Host
+ * order (see section 3.4.1 from IEEE 1722-2016 spec for further information).
+ *
+ * Any read or write operation with PDU structs should be done via getter and
+ * setter APIs which handle byte order conversion.
+ */
+struct avtp_common_pdu {
+ uint32_t subtype_data;
+ uint8_t pdu_specific[0];
+} __attribute__ ((__packed__));
+
+struct avtp_stream_pdu {
+ uint32_t subtype_data;
+ uint64_t stream_id;
+ uint32_t avtp_time;
+ uint32_t format_specific;
+ uint32_t packet_info;
+ uint8_t avtp_payload[0];
+} __attribute__ ((__packed__));
+
+enum avtp_field {
+ AVTP_FIELD_SUBTYPE,
+ AVTP_FIELD_VERSION,
+ AVTP_FIELD_MAX,
+};
+
+/* Get value from Common AVTPDU field.
+ * @pdu: Pointer to PDU struct.
+ * @field: PDU field to be retrieved.
+ * @val: Pointer to variable which the retrieved value should be saved.
+ *
+ * Returns:
+ * 0: Success.
+ * -EINVAL: If any argument is invalid.
+ */
+int avtp_pdu_get(const struct avtp_common_pdu *pdu, enum avtp_field field,
+ uint32_t *val);
+
+/* Set value from Common AVTPDU field.
+ * @pdu: Pointer to PDU struct.
+ * @field: PDU field to be set.
+ * @val: Value to be set.
+ *
+ * Returns:
+ * 0: Success.
+ * -EINVAL: If any argument is invalid.
+ */
+int avtp_pdu_set(struct avtp_common_pdu *pdu, enum avtp_field field,
+ uint32_t val);
diff --git a/lib/libavtp/include/avtp_aaf.h b/lib/libavtp/include/avtp_aaf.h
new file mode 100644
index 00000000..80b7be14
--- /dev/null
+++ b/lib/libavtp/include/avtp_aaf.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2017, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of 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.
+ */
+
+#pragma once
+
+#include <errno.h>
+#include <stdint.h>
+
+/* AAF PCM 'format' field values. */
+#define AVTP_AAF_FORMAT_USER 0x00
+#define AVTP_AAF_FORMAT_FLOAT_32BIT 0x01
+#define AVTP_AAF_FORMAT_INT_32BIT 0x02
+#define AVTP_AAF_FORMAT_INT_24BIT 0x03
+#define AVTP_AAF_FORMAT_INT_16BIT 0x04
+#define AVTP_AAF_FORMAT_AES3_32BIT 0x05
+
+/* AAF PCM 'nsr' (nominal sample rate) field values. */
+#define AVTP_AAF_PCM_NSR_USER 0x00
+#define AVTP_AAF_PCM_NSR_8KHZ 0x01
+#define AVTP_AAF_PCM_NSR_16KHZ 0x02
+#define AVTP_AAF_PCM_NSR_32KHZ 0x03
+#define AVTP_AAF_PCM_NSR_44_1KHZ 0x04
+#define AVTP_AAF_PCM_NSR_48KHZ 0x05
+#define AVTP_AAF_PCM_NSR_88_2KHZ 0x06
+#define AVTP_AAF_PCM_NSR_96KHZ 0x07
+#define AVTP_AAF_PCM_NSR_176_4KHZ 0x08
+#define AVTP_AAF_PCM_NSR_192KHZ 0x09
+#define AVTP_AAF_PCM_NSR_24KHZ 0x0A
+
+/* AAF PCM 'sp' (sparse timestamp) field values. */
+#define AVTP_AAF_PCM_SP_NORMAL 0x00
+#define AVTP_AAF_PCM_SP_SPARSE 0x01
+
+enum avtp_aaf_field {
+ AVTP_AAF_FIELD_SV,
+ AVTP_AAF_FIELD_MR,
+ AVTP_AAF_FIELD_TV,
+ AVTP_AAF_FIELD_SEQ_NUM,
+ AVTP_AAF_FIELD_TU,
+ AVTP_AAF_FIELD_STREAM_ID,
+ AVTP_AAF_FIELD_TIMESTAMP,
+ AVTP_AAF_FIELD_FORMAT,
+ AVTP_AAF_FIELD_NSR,
+ AVTP_AAF_FIELD_CHAN_PER_FRAME,
+ AVTP_AAF_FIELD_BIT_DEPTH,
+ AVTP_AAF_FIELD_STREAM_DATA_LEN,
+ AVTP_AAF_FIELD_SP,
+ AVTP_AAF_FIELD_EVT,
+ AVTP_AAF_FIELD_MAX,
+};
+
+/* Get value from AAF AVTPDU field.
+ * @pdu: Pointer to PDU struct.
+ * @field: PDU field to be retrieved.
+ * @val: Pointer to variable which the retrieved value should be saved.
+ *
+ * Returns:
+ * 0: Success.
+ * -EINVAL: If any argument is invalid.
+ */
+int avtp_aaf_pdu_get(const struct avtp_stream_pdu *pdu,
+ enum avtp_aaf_field field, uint64_t *val);
+
+/* Set value from AAF AVTPDU field.
+ * @pdu: Pointer to PDU struct.
+ * @field: PDU field to be set.
+ * @val: Value to be set.
+ *
+ * Returns:
+ * 0: Success.
+ * -EINVAL: If any argument is invalid.
+ */
+int avtp_aaf_pdu_set(struct avtp_stream_pdu *pdu, enum avtp_aaf_field field,
+ uint64_t val);
+
+/* Initialize AAF AVTPDU. All AVTPDU fields are initialized with zero except
+ * 'subtype' (which is set to AVTP_SUBTYPE_AAF) and 'sv' (which is set to 1).
+ * @pdu: Pointer to PDU struct.
+ *
+ * Return values:
+ * 0: Success.
+ * -EINVAL: If any argument is invalid.
+ */
+int avtp_aaf_pdu_init(struct avtp_stream_pdu *pdu);
diff --git a/lib/libavtp/meson.build b/lib/libavtp/meson.build
new file mode 100644
index 00000000..e12b56b5
--- /dev/null
+++ b/lib/libavtp/meson.build
@@ -0,0 +1,67 @@
+project(
+ 'libavtp',
+ 'c',
+ version: '0.1',
+ license: 'BSD-3-Clause',
+)
+
+avtp_lib = library(
+ 'avtp',
+ [
+ 'src/avtp.c',
+ 'src/avtp_aaf.c',
+ ],
+ include_directories: include_directories('include'),
+ install: true,
+)
+
+install_headers(
+ 'include/avtp.h',
+ 'include/avtp_aaf.h',
+)
+
+pkg = import('pkgconfig')
+pkg.generate(
+ name: 'avtp',
+ description: 'AVTP packetization library',
+ version: '0.1',
+ url: 'github.com/AVnu/OpenAvnu',
+ libraries: avtp_lib,
+)
+
+test_avtp = executable(
+ 'test-avtp',
+ 'unit/test-avtp.c',
+ include_directories: include_directories('include'),
+ link_with: avtp_lib,
+ dependencies: dependency('cmocka'),
+ build_by_default: false,
+)
+
+test_aaf = executable(
+ 'test-aaf',
+ 'unit/test-aaf.c',
+ include_directories: include_directories('include'),
+ link_with: avtp_lib,
+ dependencies: dependency('cmocka'),
+ build_by_default: false,
+)
+
+test('AVTP API', test_avtp)
+test('AAF API', test_aaf)
+
+executable(
+ 'aaf-talker',
+ 'examples/aaf-talker.c',
+ include_directories: include_directories('include'),
+ link_with: avtp_lib,
+ build_by_default: false,
+)
+
+executable(
+ 'aaf-listener',
+ 'examples/aaf-listener.c',
+ include_directories: include_directories('include'),
+ link_with: avtp_lib,
+ build_by_default: false,
+)
diff --git a/lib/libavtp/src/avtp.c b/lib/libavtp/src/avtp.c
new file mode 100644
index 00000000..e3132457
--- /dev/null
+++ b/lib/libavtp/src/avtp.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2017, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of 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 <arpa/inet.h>
+#include <stddef.h>
+
+#include "avtp.h"
+#include "util.h"
+
+#define SHIFT_SUBTYPE (31 - 7)
+#define SHIFT_VERSION (31 - 11)
+
+#define MASK_SUBTYPE (BITMASK(8) << SHIFT_SUBTYPE)
+#define MASK_VERSION (BITMASK(3) << SHIFT_VERSION)
+
+int avtp_pdu_get(const struct avtp_common_pdu *pdu, enum avtp_field field,
+ uint32_t *val)
+{
+ uint32_t bitmap, mask;
+ uint8_t shift;
+
+ if (!pdu || !val)
+ return -EINVAL;
+
+ switch (field) {
+ case AVTP_FIELD_SUBTYPE:
+ mask = MASK_SUBTYPE;
+ shift = SHIFT_SUBTYPE;
+ break;
+ case AVTP_FIELD_VERSION:
+ mask = MASK_VERSION;
+ shift = SHIFT_VERSION;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ bitmap = ntohl(pdu->subtype_data);
+
+ *val = BITMAP_GET_VALUE(bitmap, mask, shift);
+
+ return 0;
+}
+
+int avtp_pdu_set(struct avtp_common_pdu *pdu, enum avtp_field field,
+ uint32_t value)
+{
+ uint32_t bitmap, mask;
+ uint8_t shift;
+
+ if (!pdu)
+ return -EINVAL;
+
+ switch (field) {
+ case AVTP_FIELD_SUBTYPE:
+ mask = MASK_SUBTYPE;
+ shift = SHIFT_SUBTYPE;
+ break;
+ case AVTP_FIELD_VERSION:
+ mask = MASK_VERSION;
+ shift = SHIFT_VERSION;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ bitmap = ntohl(pdu->subtype_data);
+
+ BITMAP_SET_VALUE(bitmap, value, mask, shift);
+
+ pdu->subtype_data = htonl(bitmap);
+
+ return 0;
+}
diff --git a/lib/libavtp/src/avtp_aaf.c b/lib/libavtp/src/avtp_aaf.c
new file mode 100644
index 00000000..ce84a2c7
--- /dev/null
+++ b/lib/libavtp/src/avtp_aaf.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2017, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of 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 <arpa/inet.h>
+#include <endian.h>
+#include <string.h>
+
+#include "avtp.h"
+#include "avtp_aaf.h"
+#include "util.h"
+
+#define SHIFT_SV (31 - 8)
+#define SHIFT_MR (31 - 12)
+#define SHIFT_TV (31 - 15)
+#define SHIFT_SEQ_NUM (31 - 23)
+#define SHIFT_FORMAT (31 - 7)
+#define SHIFT_NSR (31 - 11)
+#define SHIFT_CHAN_PER_FRAME (31 - 23)
+#define SHIFT_STREAM_DATA_LEN (31 - 15)
+#define SHIFT_SP (31 - 19)
+#define SHIFT_EVT (31 - 23)
+
+#define MASK_SV (BITMASK(1) << SHIFT_SV)
+#define MASK_MR (BITMASK(1) << SHIFT_MR)
+#define MASK_TV (BITMASK(1) << SHIFT_TV)
+#define MASK_SEQ_NUM (BITMASK(8) << SHIFT_SEQ_NUM)
+#define MASK_TU (BITMASK(1))
+#define MASK_FORMAT (BITMASK(8) << SHIFT_FORMAT)
+#define MASK_NSR (BITMASK(4) << SHIFT_NSR)
+#define MASK_CHAN_PER_FRAME (BITMASK(10) << SHIFT_CHAN_PER_FRAME)
+#define MASK_BIT_DEPTH (BITMASK(8))
+#define MASK_STREAM_DATA_LEN (BITMASK(16) << SHIFT_STREAM_DATA_LEN)
+#define MASK_SP (BITMASK(1) << SHIFT_SP)
+#define MASK_EVT (BITMASK(4) << SHIFT_EVT)
+
+static int get_field_value(const struct avtp_stream_pdu *pdu,
+ enum avtp_aaf_field field, uint64_t *val)
+{
+ uint32_t bitmap, mask;
+ uint8_t shift;
+
+ switch (field) {
+ case AVTP_AAF_FIELD_SV:
+ mask = MASK_SV;
+ shift = SHIFT_SV;
+ bitmap = ntohl(pdu->subtype_data);
+ break;
+ case AVTP_AAF_FIELD_MR:
+ mask = MASK_MR;
+ shift = SHIFT_MR;
+ bitmap = ntohl(pdu->subtype_data);
+ break;
+ case AVTP_AAF_FIELD_TV:
+ mask = MASK_TV;
+ shift = SHIFT_TV;
+ bitmap = ntohl(pdu->subtype_data);
+ break;
+ case AVTP_AAF_FIELD_SEQ_NUM:
+ mask = MASK_SEQ_NUM;
+ shift = SHIFT_SEQ_NUM;
+ bitmap = ntohl(pdu->subtype_data);
+ break;
+ case AVTP_AAF_FIELD_TU:
+ mask = MASK_TU;
+ shift = 0;
+ bitmap = ntohl(pdu->subtype_data);
+ break;
+ case AVTP_AAF_FIELD_FORMAT:
+ mask = MASK_FORMAT;
+ shift = SHIFT_FORMAT;
+ bitmap = ntohl(pdu->format_specific);
+ break;
+ case AVTP_AAF_FIELD_NSR:
+ mask = MASK_NSR;
+ shift = SHIFT_NSR;
+ bitmap = ntohl(pdu->format_specific);
+ break;
+ case AVTP_AAF_FIELD_CHAN_PER_FRAME:
+ mask = MASK_CHAN_PER_FRAME;
+ shift = SHIFT_CHAN_PER_FRAME;
+ bitmap = ntohl(pdu->format_specific);
+ break;
+ case AVTP_AAF_FIELD_BIT_DEPTH:
+ mask = MASK_BIT_DEPTH;
+ shift = 0;
+ bitmap = ntohl(pdu->format_specific);
+ break;
+ case AVTP_AAF_FIELD_STREAM_DATA_LEN:
+ mask = MASK_STREAM_DATA_LEN;
+ shift = SHIFT_STREAM_DATA_LEN;
+ bitmap = ntohl(pdu->packet_info);
+ break;
+ case AVTP_AAF_FIELD_SP:
+ mask = MASK_SP;
+ shift = SHIFT_SP;
+ bitmap = ntohl(pdu->packet_info);
+ break;
+ case AVTP_AAF_FIELD_EVT:
+ mask = MASK_EVT;
+ shift = SHIFT_EVT;
+ bitmap = ntohl(pdu->packet_info);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *val = BITMAP_GET_VALUE(bitmap, mask, shift);
+
+ return 0;
+}
+
+int avtp_aaf_pdu_get(const struct avtp_stream_pdu *pdu,
+ enum avtp_aaf_field field, uint64_t *val)
+{
+ int res;
+
+ if (!pdu || !val)
+ return -EINVAL;
+
+ switch (field) {
+ case AVTP_AAF_FIELD_SV:
+ case AVTP_AAF_FIELD_MR:
+ case AVTP_AAF_FIELD_TV:
+ case AVTP_AAF_FIELD_SEQ_NUM:
+ case AVTP_AAF_FIELD_TU:
+ case AVTP_AAF_FIELD_FORMAT:
+ case AVTP_AAF_FIELD_NSR:
+ case AVTP_AAF_FIELD_CHAN_PER_FRAME:
+ case AVTP_AAF_FIELD_BIT_DEPTH:
+ case AVTP_AAF_FIELD_STREAM_DATA_LEN:
+ case AVTP_AAF_FIELD_SP:
+ case AVTP_AAF_FIELD_EVT:
+ res = get_field_value(pdu, field, val);
+ break;
+ case AVTP_AAF_FIELD_TIMESTAMP:
+ *val = ntohl(pdu->avtp_time);
+ res = 0;
+ break;
+ case AVTP_AAF_FIELD_STREAM_ID:
+ *val = be64toh(pdu->stream_id);
+ res = 0;
+ break;
+ default:
+ res = -EINVAL;
+ break;
+ }
+
+ return res;
+}
+
+static int set_field_value(struct avtp_stream_pdu *pdu,
+ enum avtp_aaf_field field, uint32_t val)
+{
+ uint32_t *ptr, bitmap, mask;
+ uint8_t shift;
+
+ switch (field) {
+ case AVTP_AAF_FIELD_SV:
+ mask = MASK_SV;
+ shift = SHIFT_SV;
+ ptr = &pdu->subtype_data;
+ break;
+ case AVTP_AAF_FIELD_MR:
+ mask = MASK_MR;
+ shift = SHIFT_MR;
+ ptr = &pdu->subtype_data;
+ break;
+ case AVTP_AAF_FIELD_TV:
+ mask = MASK_TV;
+ shift = SHIFT_TV;
+ ptr = &pdu->subtype_data;
+ break;
+ case AVTP_AAF_FIELD_SEQ_NUM:
+ mask = MASK_SEQ_NUM;
+ shift = SHIFT_SEQ_NUM;
+ ptr = &pdu->subtype_data;
+ break;
+ case AVTP_AAF_FIELD_TU:
+ mask = MASK_TU;
+ shift = 0;
+ ptr = &pdu->subtype_data;
+ break;
+ case AVTP_AAF_FIELD_FORMAT:
+ mask = MASK_FORMAT;
+ shift = SHIFT_FORMAT;
+ ptr = &pdu->format_specific;
+ break;
+ case AVTP_AAF_FIELD_NSR:
+ mask = MASK_NSR;
+ shift = SHIFT_NSR;
+ ptr = &pdu->format_specific;
+ break;
+ case AVTP_AAF_FIELD_CHAN_PER_FRAME:
+ mask = MASK_CHAN_PER_FRAME;
+ shift = SHIFT_CHAN_PER_FRAME;
+ ptr = &pdu->format_specific;
+ break;
+ case AVTP_AAF_FIELD_BIT_DEPTH:
+ mask = MASK_BIT_DEPTH;
+ shift = 0;
+ ptr = &pdu->format_specific;
+ break;
+ case AVTP_AAF_FIELD_STREAM_DATA_LEN:
+ mask = MASK_STREAM_DATA_LEN;
+ shift = SHIFT_STREAM_DATA_LEN;
+ ptr = &pdu->packet_info;
+ break;
+ case AVTP_AAF_FIELD_SP:
+ mask = MASK_SP;
+ shift = SHIFT_SP;
+ ptr = &pdu->packet_info;
+ break;
+ case AVTP_AAF_FIELD_EVT:
+ mask = MASK_EVT;
+ shift = SHIFT_EVT;
+ ptr = &pdu->packet_info;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ bitmap = ntohl(*ptr);
+
+ BITMAP_SET_VALUE(bitmap, val, mask, shift);
+
+ *ptr = htonl(bitmap);
+
+ return 0;
+}
+
+int avtp_aaf_pdu_set(struct avtp_stream_pdu *pdu, enum avtp_aaf_field field,
+ uint64_t val)
+{
+ int res;
+
+ if (!pdu)
+ return -EINVAL;
+
+ switch (field) {
+ case AVTP_AAF_FIELD_SV:
+ case AVTP_AAF_FIELD_MR:
+ case AVTP_AAF_FIELD_TV:
+ case AVTP_AAF_FIELD_SEQ_NUM:
+ case AVTP_AAF_FIELD_TU:
+ case AVTP_AAF_FIELD_FORMAT:
+ case AVTP_AAF_FIELD_NSR:
+ case AVTP_AAF_FIELD_CHAN_PER_FRAME:
+ case AVTP_AAF_FIELD_BIT_DEPTH:
+ case AVTP_AAF_FIELD_STREAM_DATA_LEN:
+ case AVTP_AAF_FIELD_SP:
+ case AVTP_AAF_FIELD_EVT:
+ res = set_field_value(pdu, field, val);
+ break;
+ case AVTP_AAF_FIELD_TIMESTAMP:
+ pdu->avtp_time = htonl(val);
+ res = 0;
+ break;
+ case AVTP_AAF_FIELD_STREAM_ID:
+ pdu->stream_id = htobe64(val);
+ res = 0;
+ break;
+ default:
+ res = -EINVAL;
+ }
+
+ return res;
+}
+
+int avtp_aaf_pdu_init(struct avtp_stream_pdu *pdu)
+{
+ int res;
+
+ if (!pdu)
+ return -EINVAL;
+
+ memset(pdu, 0, sizeof(struct avtp_stream_pdu));
+
+ res = avtp_pdu_set((struct avtp_common_pdu *) pdu, AVTP_FIELD_SUBTYPE,
+ AVTP_SUBTYPE_AAF);
+ if (res < 0)
+ return res;
+
+ res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_SV, 1);
+ if (res < 0)
+ return res;
+
+ return 0;
+};
diff --git a/lib/libavtp/src/util.h b/lib/libavtp/src/util.h
new file mode 100644
index 00000000..2a6ec3b0
--- /dev/null
+++ b/lib/libavtp/src/util.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of 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.
+ */
+
+#pragma once
+
+#define BIT(n) (1UL << n)
+
+#define BITMASK(len) (BIT(len) - 1)
+
+/* Get value from the bits within 'bitmap' represented by 'mask'. The 'mask'
+ * parameter must be a continuous bit mask (e.g. 0b00111000). This macro
+ * doesn't work with non-continuous bit masks (e.g. 0b00101001).
+ */
+#define BITMAP_GET_VALUE(bitmap, mask, shift) \
+ ((bitmap & mask) >> shift)
+
+/* Set the value 'val' in the 'bitmap' variable at the position represented by
+ * 'mask'.
+ */
+#define BITMAP_SET_VALUE(bitmap, val, mask, shift) \
+ (bitmap = (bitmap & ~mask) | ((val << shift) & mask))
diff --git a/lib/libavtp/unit/test-aaf.c b/lib/libavtp/unit/test-aaf.c
new file mode 100644
index 00000000..20a79cb9
--- /dev/null
+++ b/lib/libavtp/unit/test-aaf.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright (c) 2017, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of 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 <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <arpa/inet.h>
+
+#include "avtp.h"
+#include "avtp_aaf.h"
+
+static void aaf_get_field_null_pdu(void **state)
+{
+ int res;
+ uint64_t val = 1;
+
+ res = avtp_aaf_pdu_get(NULL, AVTP_AAF_FIELD_SV, &val);
+
+ assert_int_equal(res, -EINVAL);
+}
+
+static void aaf_get_field_null_val(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_SV, NULL);
+
+ assert_int_equal(res, -EINVAL);
+}
+
+static void aaf_get_field_invalid_field(void **state)
+{
+ int res;
+ uint64_t val = 1;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_MAX, &val);
+
+ assert_int_equal(res, -EINVAL);
+}
+
+static void aaf_get_field_sv(void **state)
+{
+ int res;
+ uint64_t val;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ /* Set 'sv' field to 1. */
+ pdu.subtype_data = htonl(0x00800000);
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_SV, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == 1);
+}
+
+static void aaf_get_field_mr(void **state)
+{
+ int res;
+ uint64_t val;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ /* Set 'mr' field to 1. */
+ pdu.subtype_data = htonl(0x00080000);
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_MR, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == 1);
+}
+
+static void aaf_get_field_tv(void **state)
+{
+ int res;
+ uint64_t val;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ /* Set 'tv' field to 1. */
+ pdu.subtype_data = htonl(0x00010000);
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_TV, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == 1);
+}
+
+static void aaf_get_field_seq_num(void **state)
+{
+ int res;
+ uint64_t val;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ /* Set 'sequence_num' field to 0x55. */
+ pdu.subtype_data = htonl(0x00005500);
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_SEQ_NUM, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == 0x55);
+}
+
+static void aaf_get_field_tu(void **state)
+{
+ int res;
+ uint64_t val;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ /* Set 'tu' field to 1. */
+ pdu.subtype_data = htonl(0x00000001);
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_TU, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == 1);
+}
+
+static void aaf_get_field_stream_id(void **state)
+{
+ int res;
+ uint64_t val;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ /* Set 'stream_id' field to 0xAABBCCDDEEFF0001. */
+ pdu.stream_id = htobe64(0xAABBCCDDEEFF0001);
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_STREAM_ID, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == 0xAABBCCDDEEFF0001);
+}
+
+static void aaf_get_field_timestamp(void **state)
+{
+ int res;
+ uint64_t val;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ /* Set 'avtp_timestamp' field to 0x80C0FFEE. */
+ pdu.avtp_time = htonl(0x80C0FFEE);
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_TIMESTAMP, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == 0x80C0FFEE);
+}
+
+static void aaf_get_field_format(void **state)
+{
+ int res;
+ uint64_t val;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ /* Set 'format' field to AVTP_AAF_FORMAT_INT_16BIT. */
+ pdu.format_specific = htonl(0x04000000);
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_FORMAT, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == AVTP_AAF_FORMAT_INT_16BIT);
+}
+
+static void aaf_get_field_nsr(void **state)
+{
+ int res;
+ uint64_t val;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ /* Set 'nsr' field to AVTP_AAF_PCM_NSR_48KHZ. */
+ pdu.format_specific = htonl(0x00500000);
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_NSR, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == AVTP_AAF_PCM_NSR_48KHZ);
+}
+
+static void aaf_get_field_chan(void **state)
+{
+ int res;
+ uint64_t val;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ /* Set 'channels_per_frame' field to 0x2AA. */
+ pdu.format_specific = htonl(0x0002AA00);
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_CHAN_PER_FRAME, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == 0x2AA);
+}
+
+static void aaf_get_field_depth(void **state)
+{
+ int res;
+ uint64_t val;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ /* Set 'bit_depth' field to 0xA5. */
+ pdu.format_specific = htonl(0x000000A5);
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_BIT_DEPTH, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == 0xA5);
+}
+
+static void aaf_get_field_data_len(void **state)
+{
+ int res;
+ uint64_t val;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ /* Set 'stream_data_length' field to 0xAAAA. */
+ pdu.packet_info = htonl(0xAAAA0000);
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_STREAM_DATA_LEN, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == 0xAAAA);
+}
+
+static void aaf_get_field_sp(void **state)
+{
+ int res;
+ uint64_t val;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ /* Set 'sp' field to AVTP_AAF_PCM_SP_SPARSE. */
+ pdu.packet_info = htonl(0x00001000);
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_SP, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == AVTP_AAF_PCM_SP_SPARSE);
+}
+
+static void aaf_get_field_evt(void **state)
+{
+ int res;
+ uint64_t val;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ /* Set 'evt' field to 0xA. */
+ pdu.packet_info = htonl(0x00000A00);
+
+ res = avtp_aaf_pdu_get(&pdu, AVTP_AAF_FIELD_EVT, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == 0xA);
+}
+
+static void aaf_set_field_null_pdu(void **state)
+{
+ int res;
+
+ res = avtp_aaf_pdu_set(NULL, AVTP_AAF_FIELD_SV, 1);
+
+ assert_int_equal(res, -EINVAL);
+}
+
+static void aaf_set_field_invalid_field(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_MAX, 1);
+
+ assert_int_equal(res, -EINVAL);
+}
+
+static void aaf_set_field_sv(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_SV, 1);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.subtype_data) == 0x00800000);
+ assert_true(ntohl(pdu.stream_id) == 0);
+ assert_true(ntohl(pdu.avtp_time) == 0);
+ assert_true(ntohl(pdu.format_specific) == 0);
+ assert_true(ntohl(pdu.packet_info) == 0);
+}
+
+static void aaf_set_field_mr(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_MR, 1);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.subtype_data) == 0x00080000);
+ assert_true(ntohl(pdu.stream_id) == 0);
+ assert_true(ntohl(pdu.avtp_time) == 0);
+ assert_true(ntohl(pdu.format_specific) == 0);
+ assert_true(ntohl(pdu.packet_info) == 0);
+}
+
+static void aaf_set_field_tv(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_TV, 1);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.subtype_data) == 0x00010000);
+ assert_true(ntohl(pdu.stream_id) == 0);
+ assert_true(ntohl(pdu.avtp_time) == 0);
+ assert_true(ntohl(pdu.format_specific) == 0);
+ assert_true(ntohl(pdu.packet_info) == 0);
+}
+
+static void aaf_set_field_seq_num(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_SEQ_NUM, 0x55);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.subtype_data) == 0x00005500);
+ assert_true(ntohl(pdu.stream_id) == 0);
+ assert_true(ntohl(pdu.avtp_time) == 0);
+ assert_true(ntohl(pdu.format_specific) == 0);
+ assert_true(ntohl(pdu.packet_info) == 0);
+}
+
+static void aaf_set_field_tu(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_TU, 1);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.subtype_data) == 0x00000001);
+ assert_true(ntohl(pdu.stream_id) == 0);
+ assert_true(ntohl(pdu.avtp_time) == 0);
+ assert_true(ntohl(pdu.format_specific) == 0);
+ assert_true(ntohl(pdu.packet_info) == 0);
+}
+
+static void aaf_set_field_stream_id(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_STREAM_ID,
+ 0xAABBCCDDEEFF0001);
+
+ assert_int_equal(res, 0);
+ assert_true(be64toh(pdu.stream_id) == 0xAABBCCDDEEFF0001);
+ assert_true(ntohl(pdu.subtype_data) == 0);
+ assert_true(ntohl(pdu.avtp_time) == 0);
+ assert_true(ntohl(pdu.format_specific) == 0);
+ assert_true(ntohl(pdu.packet_info) == 0);
+}
+
+static void aaf_set_field_timestamp(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_TIMESTAMP, 0x80C0FFEE);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.avtp_time) == 0x80C0FFEE);
+ assert_true(ntohl(pdu.subtype_data) == 0);
+ assert_true(ntohl(pdu.stream_id) == 0);
+ assert_true(ntohl(pdu.format_specific) == 0);
+ assert_true(ntohl(pdu.packet_info) == 0);
+}
+
+static void aaf_set_field_format(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_FORMAT,
+ AVTP_AAF_FORMAT_INT_16BIT);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.format_specific) == 0x04000000);
+ assert_true(ntohl(pdu.subtype_data) == 0);
+ assert_true(ntohl(pdu.stream_id) == 0);
+ assert_true(ntohl(pdu.avtp_time) == 0);
+ assert_true(ntohl(pdu.packet_info) == 0);
+}
+
+static void aaf_set_field_nsr(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_NSR,
+ AVTP_AAF_PCM_NSR_48KHZ);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.format_specific) == 0x00500000);
+ assert_true(ntohl(pdu.subtype_data) == 0);
+ assert_true(ntohl(pdu.stream_id) == 0);
+ assert_true(ntohl(pdu.avtp_time) == 0);
+ assert_true(ntohl(pdu.packet_info) == 0);
+}
+
+static void aaf_set_field_chan(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_CHAN_PER_FRAME, 0x2AA);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.format_specific) == 0x0002AA00);
+ assert_true(ntohl(pdu.subtype_data) == 0);
+ assert_true(ntohl(pdu.stream_id) == 0);
+ assert_true(ntohl(pdu.avtp_time) == 0);
+ assert_true(ntohl(pdu.packet_info) == 0);
+}
+
+static void aaf_set_field_depth(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_BIT_DEPTH, 0xA5);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.format_specific) == 0x000000A5);
+ assert_true(ntohl(pdu.subtype_data) == 0);
+ assert_true(ntohl(pdu.stream_id) == 0);
+ assert_true(ntohl(pdu.avtp_time) == 0);
+ assert_true(ntohl(pdu.packet_info) == 0);
+}
+
+static void aaf_set_field_data_len(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_STREAM_DATA_LEN, 0xAAAA);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.packet_info) == 0xAAAA0000);
+ assert_true(ntohl(pdu.subtype_data) == 0);
+ assert_true(ntohl(pdu.stream_id) == 0);
+ assert_true(ntohl(pdu.avtp_time) == 0);
+ assert_true(ntohl(pdu.format_specific) == 0);
+}
+
+static void aaf_set_field_sp(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_SP, 1);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.packet_info) == 0x00001000);
+ assert_true(ntohl(pdu.subtype_data) == 0);
+ assert_true(ntohl(pdu.stream_id) == 0);
+ assert_true(ntohl(pdu.avtp_time) == 0);
+ assert_true(ntohl(pdu.format_specific) == 0);
+}
+
+static void aaf_set_field_evt(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu = { 0 };
+
+ res = avtp_aaf_pdu_set(&pdu, AVTP_AAF_FIELD_EVT, 0xA);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.packet_info) == 0x00000A00);
+ assert_true(ntohl(pdu.subtype_data) == 0);
+ assert_true(ntohl(pdu.stream_id) == 0);
+ assert_true(ntohl(pdu.avtp_time) == 0);
+ assert_true(ntohl(pdu.format_specific) == 0);
+}
+
+static void aaf_pdu_init_null_pdu(void **state)
+{
+ int res;
+
+ res = avtp_aaf_pdu_init(NULL);
+
+ assert_int_equal(res, -EINVAL);
+}
+
+static void aaf_pdu_init(void **state)
+{
+ int res;
+ struct avtp_stream_pdu pdu;
+
+ res = avtp_aaf_pdu_init(&pdu);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.subtype_data) == 0x02800000);
+ assert_true(pdu.stream_id == 0);
+ assert_true(pdu.avtp_time == 0);
+ assert_true(pdu.format_specific == 0);
+ assert_true(pdu.packet_info == 0);
+}
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(aaf_get_field_null_pdu),
+ cmocka_unit_test(aaf_get_field_null_val),
+ cmocka_unit_test(aaf_get_field_invalid_field),
+ cmocka_unit_test(aaf_get_field_sv),
+ cmocka_unit_test(aaf_get_field_mr),
+ cmocka_unit_test(aaf_get_field_tv),
+ cmocka_unit_test(aaf_get_field_seq_num),
+ cmocka_unit_test(aaf_get_field_tu),
+ cmocka_unit_test(aaf_get_field_stream_id),
+ cmocka_unit_test(aaf_get_field_timestamp),
+ cmocka_unit_test(aaf_get_field_format),
+ cmocka_unit_test(aaf_get_field_nsr),
+ cmocka_unit_test(aaf_get_field_chan),
+ cmocka_unit_test(aaf_get_field_depth),
+ cmocka_unit_test(aaf_get_field_data_len),
+ cmocka_unit_test(aaf_get_field_sp),
+ cmocka_unit_test(aaf_get_field_evt),
+ cmocka_unit_test(aaf_set_field_null_pdu),
+ cmocka_unit_test(aaf_set_field_invalid_field),
+ cmocka_unit_test(aaf_set_field_sv),
+ cmocka_unit_test(aaf_set_field_mr),
+ cmocka_unit_test(aaf_set_field_tv),
+ cmocka_unit_test(aaf_set_field_seq_num),
+ cmocka_unit_test(aaf_set_field_tu),
+ cmocka_unit_test(aaf_set_field_stream_id),
+ cmocka_unit_test(aaf_set_field_timestamp),
+ cmocka_unit_test(aaf_set_field_format),
+ cmocka_unit_test(aaf_set_field_nsr),
+ cmocka_unit_test(aaf_set_field_chan),
+ cmocka_unit_test(aaf_set_field_depth),
+ cmocka_unit_test(aaf_set_field_data_len),
+ cmocka_unit_test(aaf_set_field_sp),
+ cmocka_unit_test(aaf_set_field_evt),
+ cmocka_unit_test(aaf_pdu_init_null_pdu),
+ cmocka_unit_test(aaf_pdu_init),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/lib/libavtp/unit/test-avtp.c b/lib/libavtp/unit/test-avtp.c
new file mode 100644
index 00000000..ee056ee7
--- /dev/null
+++ b/lib/libavtp/unit/test-avtp.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2017, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of 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 <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <arpa/inet.h>
+
+#include "avtp.h"
+
+static void get_field_null_pdu(void **state)
+{
+ int res;
+ uint32_t val = AVTP_SUBTYPE_MAAP;
+
+ res = avtp_pdu_get(NULL, AVTP_FIELD_SUBTYPE, &val);
+
+ assert_int_equal(res, -EINVAL);
+}
+
+static void get_field_null_val(void **state)
+{
+ int res;
+ struct avtp_common_pdu pdu = { 0 };
+
+ res = avtp_pdu_get(&pdu, AVTP_FIELD_SUBTYPE, NULL);
+
+ assert_int_equal(res, -EINVAL);
+}
+
+static void get_field_invalid_field(void **state)
+{
+ int res;
+ uint32_t val = AVTP_SUBTYPE_MAAP;
+ struct avtp_common_pdu pdu = { 0 };
+
+ res = avtp_pdu_get(&pdu, AVTP_FIELD_MAX, &val);
+
+ assert_int_equal(res, -EINVAL);
+}
+
+static void get_field_subtype(void **state)
+{
+ int res;
+ uint32_t val;
+ struct avtp_common_pdu pdu = { 0 };
+
+ /* Set 'subtype' field to 0xFE (AVTP_SUBTYPE_MAAP). */
+ pdu.subtype_data = htonl(0xFE000000);
+
+ res = avtp_pdu_get(&pdu, AVTP_FIELD_SUBTYPE, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == AVTP_SUBTYPE_MAAP);
+}
+
+static void get_field_version(void **state)
+{
+ int res;
+ uint32_t val;
+ struct avtp_common_pdu pdu = { 0 };
+
+ /* Set 'version' field to 5. */
+ pdu.subtype_data = htonl(0x00500000);
+
+ res = avtp_pdu_get(&pdu, AVTP_FIELD_VERSION, &val);
+
+ assert_int_equal(res, 0);
+ assert_true(val == 5);
+}
+
+static void set_field_null_pdu(void **state)
+{
+ int res;
+
+ res = avtp_pdu_set(NULL, AVTP_FIELD_SUBTYPE, AVTP_SUBTYPE_MAAP);
+
+ assert_int_equal(res, -EINVAL);
+}
+
+static void set_field_invalid_field(void **state)
+{
+ int res;
+ struct avtp_common_pdu pdu = { 0 };
+
+ res = avtp_pdu_set(&pdu, AVTP_FIELD_MAX, 1);
+
+ assert_int_equal(res, -EINVAL);
+}
+
+static void set_field_subtype(void **state)
+{
+ int res;
+ struct avtp_common_pdu pdu = { 0 };
+
+ res = avtp_pdu_set(&pdu, AVTP_FIELD_SUBTYPE, AVTP_SUBTYPE_MAAP);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.subtype_data) == 0xFE000000);
+}
+
+static void set_field_version(void **state)
+{
+ int res;
+ struct avtp_common_pdu pdu = { 0 };
+
+ res = avtp_pdu_set(&pdu, AVTP_FIELD_VERSION, 5);
+
+ assert_int_equal(res, 0);
+ assert_true(ntohl(pdu.subtype_data) == 0x00500000);
+}
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(get_field_null_pdu),
+ cmocka_unit_test(get_field_null_val),
+ cmocka_unit_test(get_field_invalid_field),
+ cmocka_unit_test(get_field_subtype),
+ cmocka_unit_test(get_field_version),
+ cmocka_unit_test(set_field_null_pdu),
+ cmocka_unit_test(set_field_invalid_field),
+ cmocka_unit_test(set_field_subtype),
+ cmocka_unit_test(set_field_version),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/travis.sh b/travis.sh
index b10cc42c..f143de5c 100755
--- a/travis.sh
+++ b/travis.sh
@@ -1,5 +1,8 @@
#!/bin/bash
set -ev
+
+ROOT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
+
make igb
make lib
make daemons_all
@@ -17,3 +20,6 @@ mkdir build
cd build
cmake ..
make doc
+cd $ROOT_DIR
+CFLAGS=-Wno-missing-braces meson lib/libavtp/ lib/libavtp/build
+ninja -C lib/libavtp/build/ test aaf-talker aaf-listener