diff options
-rw-r--r-- | .travis.yml | 15 | ||||
-rw-r--r-- | kmod/igb/e1000_82575.c | 14 | ||||
-rw-r--r-- | kmod/igb/e1000_osdep.h | 2 | ||||
-rw-r--r-- | kmod/igb/igb.h | 5 | ||||
-rw-r--r-- | kmod/igb/igb_ethtool.c | 8 | ||||
-rw-r--r-- | kmod/igb/igb_main.c | 87 | ||||
-rw-r--r-- | kmod/igb/igb_param.c | 2 | ||||
-rw-r--r-- | lib/libavtp/HACKING.md | 73 | ||||
-rw-r--r-- | lib/libavtp/LICENSE | 24 | ||||
-rw-r--r-- | lib/libavtp/README.md | 48 | ||||
-rw-r--r-- | lib/libavtp/examples/aaf-listener.c | 536 | ||||
-rw-r--r-- | lib/libavtp/examples/aaf-talker.c | 299 | ||||
-rw-r--r-- | lib/libavtp/include/avtp.h | 106 | ||||
-rw-r--r-- | lib/libavtp/include/avtp_aaf.h | 108 | ||||
-rw-r--r-- | lib/libavtp/meson.build | 67 | ||||
-rw-r--r-- | lib/libavtp/src/avtp.c | 98 | ||||
-rw-r--r-- | lib/libavtp/src/avtp_aaf.c | 312 | ||||
-rw-r--r-- | lib/libavtp/src/util.h | 45 | ||||
-rw-r--r-- | lib/libavtp/unit/test-aaf.c | 575 | ||||
-rw-r--r-- | lib/libavtp/unit/test-avtp.c | 153 | ||||
-rwxr-xr-x | travis.sh | 6 |
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 c8a4a7e3..95286e49 100644 --- a/kmod/igb/igb_main.c +++ b/kmod/igb/igb_main.c @@ -121,11 +121,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 +220,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 +468,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 +1100,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 +1825,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 +1869,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 +2891,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 +2905,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 +4695,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 +4757,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 +5026,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 +9660,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 +10269,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) { @@ -10701,8 +10759,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); +} @@ -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 |