summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Fornal <jakubfornal@gklab-20-101.igk.intel.com>2018-03-16 13:12:49 +0100
committerJakub Fornal <jakubfornal@gklab-20-101.igk.intel.com>2018-03-16 13:12:49 +0100
commitd17f251c749ff2111c40ca36f3ba9695b7b6552d (patch)
tree39dd7365a2d81e74a4d913c6f3dc57ec623a44b8
parentb64faedfbb26c6c846f3bd6e6051f862bf49abf5 (diff)
parentcc209ac3f0775432213ee896466d2ce574817503 (diff)
downloadOpen-AVB-d17f251c749ff2111c40ca36f3ba9695b7b6552d.tar.gz
Merge branch 'new_dev_id_only' of https://github.com/jfornal/Open-AVB into new_dev_id_only
-rw-r--r--.travis.yml15
-rw-r--r--kmod/igb/e1000_82575.c14
-rw-r--r--kmod/igb/e1000_osdep.h2
-rw-r--r--kmod/igb/igb.h5
-rw-r--r--kmod/igb/igb_ethtool.c8
-rw-r--r--kmod/igb/igb_main.c87
-rw-r--r--kmod/igb/igb_param.c2
-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
21 files changed, 2554 insertions, 29 deletions
diff --git a/.travis.yml b/.travis.yml
index ecc2b9d6..43b4a521 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
+ - 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/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_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..c6d5c57d 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 *);
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 51ff75fe..3ae792c0 100644
--- a/kmod/igb/igb_main.c
+++ b/kmod/igb/igb_main.c
@@ -122,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,
@@ -215,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);
@@ -459,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))
@@ -1091,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;
}
@@ -1811,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;
@@ -1855,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)
@@ -2878,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)
@@ -2885,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);
@@ -4674,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);
}
@@ -4726,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);
}
@@ -4984,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);
}
@@ -9607,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);
@@ -10219,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) {
@@ -10702,8 +10760,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/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