diff options
author | Ken Carlino <Kenneth.Carlino@Symphonyteleca.com> | 2015-07-17 12:56:00 -0500 |
---|---|---|
committer | Ken Carlino <Kenneth.Carlino@Symphonyteleca.com> | 2015-07-17 12:56:00 -0500 |
commit | b8512cf1d4a2675cd941fd2ed723cc25afba0de7 (patch) | |
tree | bcfaecf5b7488897275857a35dd9e84fd1b0c4c1 | |
parent | 365cf04f73715780e123312ddadd83d202aa1bdd (diff) | |
download | Open-AVB-b8512cf1d4a2675cd941fd2ed723cc25afba0de7.tar.gz |
STC AVTP Pipeline : Work in progress 1
267 files changed, 40758 insertions, 0 deletions
diff --git a/lib/avtp_pipeline/CMakeLists.txt b/lib/avtp_pipeline/CMakeLists.txt new file mode 100644 index 00000000..c558ffbf --- /dev/null +++ b/lib/avtp_pipeline/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required ( VERSION 2.6 ) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") +project ( AVB ) + +# point to AVB SRC directory +set ( AVB_SRC_DIR ${CMAKE_SOURCE_DIR} ) + +# point to HAL directory +set ( AVB_HAL_DIR ${AVB_SRC_DIR}/platform/${OPENAVB_HAL} ) + +# point to OSAL directory +set ( AVB_OSAL_DIR ${AVB_SRC_DIR}/platform/${OPENAVB_OSAL} ) + +# point to TCAL directory +set ( AVB_TCAL_DIR ${AVB_SRC_DIR}/platform/platTCAL/${OPENAVB_TCAL} ) + +# Directory to install binaries to +set ( AVB_INSTALL_DIR ${CMAKE_BINARY_DIR}/bin ) + +# CORE_TODO: There may be additional common CMakeLists.txt functionality that can be migrated out of the platform specific area. + +# CMake is happier when the CMakeLists.txt is in a top level directory working down. Therefore the platform specific CMakeLists.txt +# is included here. The common CMake command uage will take this form below when run from a build directory that is at the same +# directory level as the repo: +# cmake -DCMAKE_TOOLCHAIN_FILE=../<avb_repo>/platform/<platform name>/<target>.cmake -DCMAKE_BUILD_TYPE=Release ../<avb_repo> +# for example: +# cmake -DCMAKE_TOOLCHAIN_FILE=../avbrepo/platform/Linux/x86_i210_linux.cmake -DCMAKE_BUILD_TYPE=Release ../avbrepo + +# Used to hold lists of source files from common code subdirectories +SET (SRC_FILES "") + +# Suppress the policy warning when including a CMakeLists.txt file +if(POLICY CMP0011) + cmake_policy(SET CMP0011 OLD) +endif(POLICY CMP0011) + +include(${AVB_OSAL_DIR}/CMakeLists.txt) + + + diff --git a/lib/avtp_pipeline/LICENSE b/lib/avtp_pipeline/LICENSE new file mode 100644 index 00000000..7f46621a --- /dev/null +++ b/lib/avtp_pipeline/LICENSE @@ -0,0 +1,30 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + diff --git a/lib/avtp_pipeline/README.md b/lib/avtp_pipeline/README.md new file mode 100644 index 00000000..5eab7b04 --- /dev/null +++ b/lib/avtp_pipeline/README.md @@ -0,0 +1,72 @@ +# STC AVTP Pipeline Contribution Notes + +## General Status +- Consider the AVTP Pipeline a work in progress. +- Integrated with OpenAVB: + - gPTP +- Not yet integreated with OpenAVB: + - MSRP + - igb direct for packet TX. Current linux raw sockets are used for both TX and RX. + - igb credit based shaper + - Build system +- Very limited tested as been down. Primarily just the Echo talker which is a simple test stream. +- Documentation and doc generation has not been fully updated. + +## Building Current OpenAVB +### Tool chain and libraries +- Ubuntu 14.04 +- Build OpenAVB Next branch (OpenAVB Master doesn't igb doesn't build properly) +- Install ($ sudo apt-get install ...) + - $ sudo apt-get install build-essential + - $ sudo apt-get install libpcap-dev + - $ sudo apt-get install libpci-dev + - $ sudo apt-get install libsndfile1-dev + - $ sudo apt-get install libjack-dev + - $ sudo apt-get install linux-headers-generic + - linux-headers (same version as the kernel you're building for) + +### Building +- Building from the repo root +- $ make igb +- $ make lib +- $ ARCH=I210 make gptp +- $ make mrpd +- $ make maap +- $ make examples_all + +## Building STC AVTP Pipeline +### Get packages +- $ sudo apt-get install libglib2.0-dev +- $ sudo apt-get install libgstreamer0.10-dev +- $ sudo apt-get install libgstreamer-plugins-base0.10-dev +- $ sudo apt-get install libasound2-dev + +### Setup AVTP Pipeline build directory +- Building from the repo root +- $ cd .. +- $ mkdir build +- $ cd build +- `$ cmake -DCMAKE_TOOLCHAIN_FILE=repo/lib/avtp_pipeline/platform/Linux/x86_i210_linux.cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ../repo/lib/avtp_pipeline` + +### Build AVTP Pipeline +- $ make all install + +## Running OpenAVB +- Helper scripts in the repo root. +- `$ sudo ./run_igb.sh eth1` + - Load the igb driver. Supply the interface name (ethx) as parameter. +- `$ sudo ./run_gptp.sh eth1` + - Start gptp daemon. Supply the interface name (ethx) as parameter. +- `$ sudo ./run_srp.sh eth1` + - Start msrp daemon. Supply the interface name (ethx) as parameter. +- `$ sudo ./run_simple_talker.sh eth1` + - Run the current OpenAVB simple talker example. Supply the interface name (ethx) as parameter. + +## Running OpenAVB with STC Echo Talker (without SRP currently) +- `$ sudo ./run_igb.sh eth1` + - Load the igb driver. Supply the interface name (ethx) as parameter. +- `$ sudo ./run_gptp.sh eth1` + - Start gptp daemon. Supply the interface name (ethx) as parameter. +- `$ sudo ./run_echo_talker.sh eth1` + - Run the AVTP Echo talker test stream. Supply the interface name (ethx) as parameter. + diff --git a/lib/avtp_pipeline/avtp/CMakeLists.txt b/lib/avtp_pipeline/avtp/CMakeLists.txt new file mode 100644 index 00000000..7f43c471 --- /dev/null +++ b/lib/avtp_pipeline/avtp/CMakeLists.txt @@ -0,0 +1,6 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/avtp/openavb_avtp.c + ${AVB_SRC_DIR}/avtp/openavb_avtp_time.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/avtp/openavb_avtp.c b/lib/avtp_pipeline/avtp/openavb_avtp.c new file mode 100644 index 00000000..6b646d08 --- /dev/null +++ b/lib/avtp_pipeline/avtp/openavb_avtp.c @@ -0,0 +1,701 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Implements main functions for AVTP. Includes +* functions to create/destroy and AVTP stream, and to send or receive +* data from that AVTP stream. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdint.h> +#include <errno.h> +#include "openavb_platform.h" +#include "openavb_types.h" +#include "openavb_trace.h" +#include "openavb_avtp.h" +#include "openavb_rawsock.h" +#include "openavb_mediaq.h" + +#define AVB_LOG_COMPONENT "AVTP" +#include "openavb_log.h" + +// Maximum time that AVTP RX/TX calls should block before returning +#define AVTP_MAX_BLOCK_USEC (1 * MICROSECONDS_PER_SECOND) + +/* + * This is broken out into a function, so that we can close and reopen + * the socket if we detect a problem receiving frames. + */ +static openavbRC openAvtpSock(avtp_stream_t *pStream) +{ + if (pStream->tx) { + pStream->rawsock = openavbRawsockOpen(pStream->ifname, FALSE, TRUE, ETHERTYPE_AVTP, pStream->frameLen, pStream->nbuffers); + } + else { +#ifndef UBUNTU + // This is the normal case for most of our supported platforms + pStream->rawsock = openavbRawsockOpen(pStream->ifname, TRUE, FALSE, ETHERTYPE_8021Q, pStream->frameLen, pStream->nbuffers); +#else + pStream->rawsock = openavbRawsockOpen(pStream->ifname, TRUE, FALSE, ETHERTYPE_AVTP, pStream->frameLen, pStream->nbuffers); +#endif + } + + if (pStream->rawsock != NULL) { + // Get the socket, so we can poll on it + pStream->sock = openavbRawsockGetSocket(pStream->rawsock); + + openavbSetRxSignalMode(pStream->rawsock, pStream->bRxSignalMode); + + if (!pStream->tx) { + // Set the multicast address that we want to receive + openavbRawsockRxMulticast(pStream->rawsock, TRUE, pStream->dest_addr.ether_addr_octet); + } + AVB_RC_RET(OPENAVB_AVTP_SUCCESS); + } + + AVB_RC_LOG_RET(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVB_RC_RAWSOCK_OPEN)); +} + + +// Evaluate the AVTP timestamp. Only valid for common AVTP stream subtypes +#define HIDX_AVTP_HIDE7_TV1 1 +#define HIDX_AVTP_HIDE7_TU1 3 +#define HIDX_AVTP_TIMESPAMP32 12 +static void processTimestampEval(avtp_stream_t *pStream, U8 *pHdr) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVTP_DETAIL); + + if (pStream->tsEval) { + bool tsValid = (pHdr[HIDX_AVTP_HIDE7_TV1] & 0x01) ? TRUE : FALSE; + bool tsUncertain = (pHdr[HIDX_AVTP_HIDE7_TU1] & 0x01) ? TRUE : FALSE; + + if (tsValid && !tsUncertain) { + U32 ts = ntohl(*(U32 *)(&pHdr[HIDX_AVTP_TIMESPAMP32])); + U32 tsSmoothed = openavbTimestampEvalTimestamp(pStream->tsEval, ts); + if (tsSmoothed != ts) { + *(U32 *)(&pHdr[HIDX_AVTP_TIMESPAMP32]) = htonl(tsSmoothed); + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_AVTP_DETAIL); +} + + +/* Initialize AVTP for talking + */ +openavbRC openavbAvtpTxInit( + media_q_t *pMediaQ, + openavb_map_cb_t *pMapCB, + openavb_intf_cb_t *pIntfCB, + char *ifname, + AVBStreamID_t *streamID, + U8 *destAddr, + U32 max_transit_usec, + U32 fwmark, + U16 vlanID, + U8 vlanPCP, + U16 nbuffers, + void **pStream_out) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVTP); + AVB_LOG_DEBUG("Initialize"); + + *pStream_out = NULL; + + // Malloc the structure to hold state information + avtp_stream_t *pStream = calloc(1, sizeof(avtp_stream_t)); + if (!pStream) { + AVB_RC_LOG_TRACE_RET(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVB_RC_OUT_OF_MEMORY), AVB_TRACE_AVTP); + } + pStream->tx = TRUE; + + pStream->pMediaQ = pMediaQ; + pStream->pMapCB = pMapCB; + pStream->pIntfCB = pIntfCB; + + pStream->pMapCB->map_tx_init_cb(pStream->pMediaQ); + pStream->pIntfCB->intf_tx_init_cb(pStream->pMediaQ); + + // Set the frame length + pStream->frameLen = pStream->pMapCB->map_max_data_size_cb(pStream->pMediaQ) + ETH_HDR_LEN_VLAN; + + // and the latency + pStream->max_transit_usec = max_transit_usec; + + // and save other stuff needed to (re)open the socket + pStream->ifname = strdup(ifname); + pStream->nbuffers = nbuffers; + + // Open a raw socket + openavbRC rc = openAvtpSock(pStream); + if (IS_OPENAVB_FAILURE(rc)) { + free(pStream); + AVB_RC_LOG_TRACE_RET(rc, AVB_TRACE_AVTP); + } + + // Create the AVTP frame header for the frames we'll send + hdr_info_t hdrInfo; + + U8 srcAddr[ETH_ALEN]; + if (openavbRawsockGetAddr(pStream->rawsock, srcAddr)) { + hdrInfo.shost = srcAddr; + } + else { + openavbRawsockClose(pStream->rawsock); + free(pStream); + AVB_LOG_ERROR("Failed to get source MAC address"); + AVB_RC_TRACE_RET(OPENAVB_AVTP_FAILURE, AVB_TRACE_AVTP); + } + + hdrInfo.dhost = destAddr; + if (vlanPCP != 0 || vlanID != 0) { + hdrInfo.vlan = TRUE; + hdrInfo.vlan_pcp = vlanPCP; + hdrInfo.vlan_vid = vlanID; + AVB_LOGF_DEBUG("VLAN pcp=%d vid=%d", hdrInfo.vlan_pcp, hdrInfo.vlan_vid); + } + else { + hdrInfo.vlan = FALSE; + } + openavbRawsockTxSetHdr(pStream->rawsock, &hdrInfo); + + // Remember the AVTP subtype and streamID + pStream->subtype = pStream->pMapCB->map_subtype_cb(); + + memcpy(pStream->streamIDnet, streamID->addr, ETH_ALEN); + U16 *pStreamUID = (U16 *)((U8 *)(pStream->streamIDnet) + ETH_ALEN); + *pStreamUID = htons(streamID->uniqueID); + + // Set the fwmark - used to steer packets into the right traffic control queue + openavbRawsockTxSetMark(pStream->rawsock, fwmark); + + *pStream_out = (void *)pStream; + AVB_RC_TRACE_RET(OPENAVB_AVTP_SUCCESS, AVB_TRACE_AVTP); +} + +#ifdef OPENAVB_AVTP_REPORT_RX_STATS +static void inline rxDeliveryStats(avtp_rx_info_t *rxInfo, + struct timespec *tmNow, + U32 early, U32 late) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVTP); + + rxInfo->rxCnt++; + + if (late > 0) { + rxInfo->lateCnt++; + if (late > rxInfo->maxLate) + rxInfo->maxLate = late; + } + if (early > 0) { + rxInfo->earlyCnt++; + if (early > rxInfo->maxEarly) + rxInfo->maxEarly = early; + } + + if (rxInfo->lastTime.tv_sec == 0) { + rxInfo->lastTime.tv_sec = tmNow->tv_sec; + rxInfo->lastTime.tv_nsec = tmNow->tv_nsec; + } + else if ((tmNow->tv_sec > (rxInfo->lastTime.tv_sec + OPENAVB_AVTP_REPORT_INTERVAL)) + || ((tmNow->tv_sec == (rxInfo->lastTime.tv_sec + OPENAVB_AVTP_REPORT_INTERVAL)) + && (tmNow->tv_nsec > rxInfo->lastTime.tv_nsec))) { + AVB_LOGF_INFO("Stream %d seconds, %lu samples: %lu late, max=%lums, %lu early, max=%lums", + OPENAVB_AVTP_REPORT_INTERVAL, (unsigned long)rxInfo->rxCnt, + (unsigned long)rxInfo->lateCnt, (unsigned long)rxInfo->maxLate / NANOSECONDS_PER_MSEC, + (unsigned long)rxInfo->earlyCnt, (unsigned long)rxInfo->maxEarly / NANOSECONDS_PER_MSEC); + rxInfo->maxLate = 0; + rxInfo->lateCnt = 0; + rxInfo->maxEarly = 0; + rxInfo->earlyCnt = 0; + rxInfo->rxCnt = 0; + rxInfo->lastTime.tv_sec = tmNow->tv_sec; + rxInfo->lastTime.tv_nsec = tmNow->tv_nsec; + } + +#if 0 + if (++txCnt >= 1000) {} +#endif + AVB_TRACE_EXIT(AVB_TRACE_AVTP); +} +#endif + +static openavbRC fillAvtpHdr(avtp_stream_t *pStream, U8 *pFill) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVTP_DETAIL); + + switch (pStream->pMapCB->map_avtp_version_cb()) { + default: + AVB_RC_LOG_RET(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVBAVTP_RC_INVALID_AVTP_VERSION)); + case 0: + // + // - 1 bit cd (control/data indicator) = 0 (stream data) + // - 7 bits subtype = as configured + *pFill++ = pStream->subtype & 0x7F; + // - 1 bit sv (stream valid) = 1 + // - 3 bits AVTP version = binary 000 + // - 1 bit mr (media restart) = toggled when clock changes + // - 1 bit r (reserved) = 0 + // - 1 bit gv (gateway valid) = 0 + // - 1 bit tv (timestamp valid) = 1 + // TODO: set mr correctly + *pFill++ = 0x81; + // - 8 bits sequence num = increments with each frame + *pFill++ = pStream->avtp_sequence_num; + // - 7 bits reserved = 0; + // - 1 bit tu (timestamp uncertain) = 1 when no PTP sync + // TODO: set tu correctly + *pFill++ = 0; + // - 8 bytes stream_id + memcpy(pFill, (U8 *)&pStream->streamIDnet, 8); + break; + } + AVB_RC_TRACE_RET(OPENAVB_AVTP_SUCCESS, AVB_TRACE_AVTP_DETAIL); +} + +/* Send a frame + */ +openavbRC openavbAvtpTx(void *pv, bool bSend, bool txBlockingInIntf) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVTP_DETAIL); + + avtp_stream_t *pStream = (avtp_stream_t *)pv; + if (!pStream) { + AVB_RC_LOG_TRACE_RET(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVB_RC_INVALID_ARGUMENT), AVB_TRACE_AVTP_DETAIL); + } + + U8 * pAvtpFrame,*pFill; + U32 avtpFrameLen, frameLen; + tx_cb_ret_t txCBResult = TX_CB_RET_PACKET_NOT_READY; + + // Get a TX buf if we don't already have one. + // (We keep the TX buf in our stream data, so that if we don't + // get data from the mapping module, we can use the buf next time.) + if (!pStream->pBuf) { + + pStream->pBuf = (U8 *)openavbRawsockGetTxFrame(pStream->rawsock, TRUE, &frameLen); + if (!pStream->pBuf) { + txCBResult = TX_CB_RET_PACKET_NOT_READY; + } + else { + assert(frameLen >= pStream->frameLen); + // Fill in the Ethernet header + openavbRawsockTxFillHdr(pStream->rawsock, pStream->pBuf, &pStream->ethHdrLen); + } + } + + if (pStream->pBuf) { + // AVTP frame starts right after the Ethernet header + pAvtpFrame = pFill = pStream->pBuf + pStream->ethHdrLen; + avtpFrameLen = pStream->frameLen - pStream->ethHdrLen; + + // Fill the AVTP Header. This must be done before calling the interface and mapping modules. + openavbRC rc = fillAvtpHdr(pStream, pFill); + if (IS_OPENAVB_FAILURE(rc)) { + AVB_RC_LOG_TRACE_RET(rc, AVB_TRACE_AVTP_DETAIL); + } + + if (!txBlockingInIntf) { + // Call interface module to read data + pStream->pIntfCB->intf_tx_cb(pStream->pMediaQ); + // Call mapping module to move data into AVTP frame + txCBResult = pStream->pMapCB->map_tx_cb(pStream->pMediaQ, pAvtpFrame, &avtpFrameLen); + + pStream->bytes += avtpFrameLen; + } + else { + // Blocking in interface mode. Pull from media queue for tx first + if ((txCBResult = pStream->pMapCB->map_tx_cb(pStream->pMediaQ, pAvtpFrame, &avtpFrameLen)) == TX_CB_RET_PACKET_NOT_READY) { + // Call interface module to read data + pStream->pIntfCB->intf_tx_cb(pStream->pMediaQ); + } + else { + pStream->bytes += avtpFrameLen; + } + } + + // If we got data from the mapping module, notifiy the raw sockets. + if (txCBResult != TX_CB_RET_PACKET_NOT_READY) { + + if (pStream->tsEval) { + processTimestampEval(pStream, pAvtpFrame); + } + + // Increment the sequence number now that we are sure this is a good packet. + pStream->avtp_sequence_num++; + // Mark the frame "ready to send". + openavbRawsockTxFrameReady(pStream->rawsock, pStream->pBuf, avtpFrameLen + pStream->ethHdrLen); + // Send if requested + if (bSend) + openavbRawsockSend(pStream->rawsock); + // Drop our reference to it + pStream->pBuf = NULL; + } + else { + AVB_RC_TRACE_RET(OPENAVB_AVTP_FAILURE, AVB_TRACE_AVTP_DETAIL); + } + } + else { + AVB_RC_TRACE_RET(OPENAVB_AVTP_FAILURE, AVB_TRACE_AVTP_DETAIL); + } + + AVB_RC_TRACE_RET(OPENAVB_AVTP_SUCCESS, AVB_TRACE_AVTP_DETAIL); +} + +openavbRC openavbAvtpRxInit( + media_q_t *pMediaQ, + openavb_map_cb_t *pMapCB, + openavb_intf_cb_t *pIntfCB, + char *ifname, + AVBStreamID_t *streamID, + U8 *daddr, + U16 nbuffers, + bool rxSignalMode, + void **pStream_out) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVTP); + AVB_LOG_DEBUG("Initialize"); + + *pStream_out = NULL; + + if (!pMapCB) { + AVB_RC_LOG_TRACE_RET(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVBAVTP_RC_MAPPING_CB_NOT_SET), AVB_TRACE_AVTP); + } + if (!pIntfCB) { + AVB_RC_LOG_TRACE_RET(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVBAVTP_RC_INTERFACE_CB_NOT_SET), AVB_TRACE_AVTP); + } + if (!daddr) { + AVB_RC_LOG_TRACE_RET(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVB_RC_INVALID_ARGUMENT), AVB_TRACE_AVTP); + } + + avtp_stream_t *pStream = calloc(1, sizeof(avtp_stream_t)); + if (!pStream) { + AVB_RC_LOG_TRACE_RET(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVB_RC_OUT_OF_MEMORY), AVB_TRACE_AVTP); + } + pStream->tx = FALSE; + pStream->nLost = -1; + + pStream->pMediaQ = pMediaQ; + pStream->pMapCB = pMapCB; + pStream->pIntfCB = pIntfCB; + + pStream->pMapCB->map_rx_init_cb(pStream->pMediaQ); + pStream->pIntfCB->intf_rx_init_cb(pStream->pMediaQ); + + // Set the frame length + pStream->frameLen = pStream->pMapCB->map_max_data_size_cb(pStream->pMediaQ) + ETH_HDR_LEN_VLAN; + + // Save the streamID + memcpy(pStream->streamIDnet, streamID->addr, ETH_ALEN); + U16 *pStreamUID = (U16 *)((U8 *)(pStream->streamIDnet) + ETH_ALEN); + *pStreamUID = htons(streamID->uniqueID); + + // and the destination MAC address + memcpy(pStream->dest_addr.ether_addr_octet, daddr, ETH_ALEN); + + // and other stuff needed to (re)open the socket + pStream->ifname = strdup(ifname); + pStream->nbuffers = nbuffers; + pStream->bRxSignalMode = rxSignalMode; + + openavbRC rc = openAvtpSock(pStream); + if (IS_OPENAVB_FAILURE(rc)) { + free(pStream); + AVB_RC_LOG_TRACE_RET(rc, AVB_TRACE_AVTP); + } + + // Save the AVTP subtype + pStream->subtype = pStream->pMapCB->map_subtype_cb(); + + *pStream_out = (void *)pStream; + AVB_RC_TRACE_RET(OPENAVB_AVTP_SUCCESS, AVB_TRACE_AVTP); +} + +static void x_avtpRxFrame(avtp_stream_t *pStream, U8 *pFrame, U32 frameLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVTP_DETAIL); + AVB_LOGF_DEBUG("pFrame=%8.8p, len=%u", pFrame, frameLen); + U8 subtype, flags, flags2, rxSeq, nLost, avtpVersion; + U8 *pRead = pFrame; + + // AVTP Header + // + // Check control/data bit. We only expect data packets. + if (0 == (*pRead & 0x80)) { + // - 7 bits subtype + subtype = *pRead++ & 0x7F; + flags = *pRead++; + avtpVersion = (flags >> 4) & 0x07; + + // Check AVTPDU version, BZ 106 + if (0 == avtpVersion) { + + rxSeq = *pRead++; + + if (pStream->nLost == -1) { + // first frame received, don't check for mismatch + pStream->nLost = 0; + } + else if (pStream->avtp_sequence_num != rxSeq) { + nLost = (rxSeq - pStream->avtp_sequence_num) + + (rxSeq < pStream->avtp_sequence_num ? 256 : 0); + AVB_LOGF_DEBUG("AVTP sequence mismatch: expected: %u,\tgot: %u,\tlost %d", + pStream->avtp_sequence_num, rxSeq, nLost); + pStream->nLost += nLost; + } + pStream->avtp_sequence_num = rxSeq + 1; + + pStream->bytes += frameLen; + + flags2 = *pRead++; + + AVB_LOGF_DEBUG("subtype=%u, sv=%u, ver=%u, mr=%u, tv=%u tu=%u", + subtype, flags & 0x80, avtpVersion, + flags & 0x08, flags & 0x01, flags2 & 0x01); + + pRead += 8; + + if (pStream->tsEval) { + processTimestampEval(pStream, pFrame); + } + + pStream->pMapCB->map_rx_cb(pStream->pMediaQ, pFrame, frameLen); + + // NOTE : This is a redundant call. It is handled in avtpTryRx() + // pStream->pIntfCB->intf_rx_cb(pStream->pMediaQ); + + pStream->info.rx.bComplete = TRUE; + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVBAVTP_RC_INVALID_AVTP_VERSION)); + } + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVBAVTP_RC_IGNORING_CONTROL_PACKET)); + } + + AVB_TRACE_EXIT(AVB_TRACE_AVTP_DETAIL); +} + +/* + * Try to receive some data. + * + * Keeps state information in pStream. + * Look at pStream->info for the received data. + */ +static void avtpTryRx(avtp_stream_t *pStream) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVTP_DETAIL); + + U8 *pBuf = NULL; // pointer to buffer containing rcvd frame, if any + U8 *pAvtpPdu; // pointer to AVTP PDU within Ethernet frame + U32 offsetToFrame; // offset into pBuf where Ethernet frame begins (bytes) + U32 frameLen; // length of the Ethernet frame (bytes) + int hdrLen; // length of the Ethernet frame header (bytes) + U32 avtpPduLen; // length of the AVTP PDU (bytes) + hdr_info_t hdrInfo; // Ethernet header contents + U32 timeout; + + while (!pBuf) { + if (!openavbMediaQUsecTillTail(pStream->pMediaQ, &timeout)) { + // No mediaQ item available therefore wait for a new packet + timeout = AVTP_MAX_BLOCK_USEC; + pBuf = (U8 *)openavbRawsockGetRxFrame(pStream->rawsock, timeout, &offsetToFrame, &frameLen); + if (!pBuf) { + AVB_TRACE_EXIT(AVB_TRACE_AVTP_DETAIL); + return; + } + } + else if (timeout == 0) { + // Process the pending media queue item and after check for available incoming packets + pStream->pIntfCB->intf_rx_cb(pStream->pMediaQ); + + // Previously would check for new packets but disabled to favor presentation times. + // pBuf = (U8 *)openavbRawsockGetRxFrame(pStream->rawsock, OPENAVB_RAWSOCK_NONBLOCK, &offsetToFrame, &frameLen); + } + else { + if (timeout > AVTP_MAX_BLOCK_USEC) + timeout = AVTP_MAX_BLOCK_USEC; + if (timeout < RAWSOCK_MIN_TIMEOUT_USEC) + timeout = RAWSOCK_MIN_TIMEOUT_USEC; + + pBuf = (U8 *)openavbRawsockGetRxFrame(pStream->rawsock, timeout, &offsetToFrame, &frameLen); + if (!pBuf) + pStream->pIntfCB->intf_rx_cb(pStream->pMediaQ); + } + } + + hdrLen = openavbRawsockRxParseHdr(pStream->rawsock, pBuf, &hdrInfo); + if (hdrLen < 0) { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVBAVTP_RC_PARSING_FRAME_HEADER)); + } + else { + pAvtpPdu = pBuf + offsetToFrame + hdrLen; + avtpPduLen = frameLen - hdrLen; + x_avtpRxFrame(pStream, pAvtpPdu, avtpPduLen); + } + openavbRawsockRelRxFrame(pStream->rawsock, pBuf); + + AVB_TRACE_EXIT(AVB_TRACE_AVTP_DETAIL); +} + +int openavbAvtpTxBufferLevel(void *pv) +{ + avtp_stream_t *pStream = (avtp_stream_t *)pv; + if (!pStream) { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVB_RC_INVALID_ARGUMENT)); + return 0; + } + return openavbRawsockTxBufLevel(pStream->rawsock); +} + +int openavbAvtpRxBufferLevel(void *pv) +{ + avtp_stream_t *pStream = (avtp_stream_t *)pv; + if (!pStream) { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVB_RC_INVALID_ARGUMENT)); + return 0; + } + return openavbRawsockRxBufLevel(pStream->rawsock); +} + +int openavbAvtpLost(void *pv) +{ + avtp_stream_t *pStream = (avtp_stream_t *)pv; + if (!pStream) { + // Quietly return. Since this can be called before a stream is available. + return 0; + } + int count = pStream->nLost; + pStream->nLost = 0; + return count; +} + +U64 openavbAvtpBytes(void *pv) +{ + avtp_stream_t *pStream = (avtp_stream_t *)pv; + if (!pStream) { + // Quietly return. Since this can be called before a stream is available. + return 0; + } + + U64 bytes = pStream->bytes; + pStream->bytes = 0; + return bytes; +} + +openavbRC openavbAvtpRx(void *pv) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVTP_DETAIL); + + avtp_stream_t *pStream = (avtp_stream_t *)pv; + if (!pStream) { + AVB_RC_LOG_TRACE_RET(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVB_RC_INVALID_ARGUMENT), AVB_TRACE_AVTP_DETAIL); + } + + // Check our socket, and potentially receive some data. + avtpTryRx(pStream); + + // See if there's a complete (re-assembled) data sample. + if (pStream->info.rx.bComplete) { + pStream->info.rx.bComplete = FALSE; + AVB_RC_TRACE_RET(OPENAVB_AVTP_SUCCESS, AVB_TRACE_AVTP_DETAIL); + } + + AVB_RC_TRACE_RET(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVBAVTP_RC_NO_FRAMES_PROCESSED), AVB_TRACE_AVTP_DETAIL); +} + +void openavbAvtpConfigTimsstampEval(void *handle, U32 tsInterval, U32 reportInterval, bool smoothing, U32 tsMaxJitter, U32 tsMaxDrift) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVTP); + + avtp_stream_t *pStream = (avtp_stream_t *)handle; + if (!pStream) { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVB_RC_INVALID_ARGUMENT)); + AVB_TRACE_EXIT(AVB_TRACE_AVTP); + return; + } + + pStream->tsEval = openavbTimestampEvalNew(); + openavbTimestampEvalInitialize(pStream->tsEval, tsInterval); + openavbTimestampEvalSetReport(pStream->tsEval, reportInterval); + if (smoothing) { + openavbTimestampEvalSetSmoothing(pStream->tsEval, tsMaxJitter, tsMaxDrift); + } + + AVB_TRACE_EXIT(AVB_TRACE_AVTP); +} + +void openavbAvtpPause(void *handle, bool bPause) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVTP); + + avtp_stream_t *pStream = (avtp_stream_t *)handle; + if (!pStream) { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVB_RC_INVALID_ARGUMENT)); + AVB_TRACE_EXIT(AVB_TRACE_AVTP); + return; + } + + pStream->bPause = bPause; + + AVB_TRACE_EXIT(AVB_TRACE_AVTP); +} + +void openavbAvtpShutdown(void *pv) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVTP); + AVB_LOG_DEBUG("Shutdown"); + + avtp_stream_t *pStream = (avtp_stream_t *)pv; + if (pStream) { + pStream->pIntfCB->intf_end_cb(pStream->pMediaQ); + pStream->pMapCB->map_end_cb(pStream->pMediaQ); + + // close the rawsock + if (pStream->rawsock) { + openavbRawsockClose(pStream->rawsock); + pStream->rawsock = NULL; + } + + if (pStream->ifname) + free(pStream->ifname); + + // free the malloc'd stream info + free(pStream); + } + AVB_TRACE_EXIT(AVB_TRACE_AVTP); + return; +} diff --git a/lib/avtp_pipeline/avtp/openavb_avtp.h b/lib/avtp_pipeline/avtp/openavb_avtp.h new file mode 100644 index 00000000..aa506ff7 --- /dev/null +++ b/lib/avtp_pipeline/avtp/openavb_avtp.h @@ -0,0 +1,192 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Declare the main functions for AVTP. Includes +* functions to create/destroy and AVTP stream, and to send or receive +* data from that AVTP stream. +*/ + +#ifndef AVB_AVTP_H +#define AVB_AVTP_H 1 + +#include "openavb_platform.h" +#include "openavb_intf_pub.h" +#include "openavb_map_pub.h" +#include "openavb_rawsock.h" +#include "openavb_timestamp.h" + +#define ETHERTYPE_AVTP 0x22F0 +#define ETHERTYPE_8021Q 0x8100 +#define ETHERNET_8021Q_OCTETS 4 +#define TS_PACKET_LEN 188 +#define SRC_PACKET_LEN 192 +#define CIP_HEADER_LEN 8 + +// Length of Ethernet frame header, with and without 802.1Q tag +#define ETH_HDR_LEN 14 +#define ETH_HDR_LEN_VLAN 18 + +// AVTP Headers +#define AVTP_COMMON_STREAM_DATA_HDR_LEN 24 + +//#define OPENAVB_AVTP_REPORT_RX_STATS 1 +#define OPENAVB_AVTP_REPORT_INTERVAL 100 + +typedef struct { + // These are significant only for RX data + U32 timestamp; // delivery timestamp + bool bComplete; // not waiting for more data +#ifdef OPENAVB_AVTP_REPORT_RX_STATS + U32 rxCnt, lateCnt, earlyCnt; + U32 maxLate, maxEarly; + struct timespec lastTime; +#endif +} avtp_rx_info_t; + +typedef struct { + U8 *data; // pointer to data + avtp_rx_info_t rx; // re-assembly info +} avtp_info_t; + +typedef struct { + media_q_t mediaq; +} avtp_state_t; + + +/* Info associated with an AVTP stream (RX or TX). + * + * The void* handle that is returned to the client + * really is a pointer to an avtp_stream_t. + * + * TODO: This passed around as void * handle can be typed since the avtp_stream_t is + * now seen by the talker / listern module. + * + */ +typedef struct +{ + // TX socket? + bool tx; + // Interface name + char* ifname; + // Number of rawsock buffers + U16 nbuffers; + // The rawsock library handle. Used to send or receive frames. + void *rawsock; + // The actual socket used by the rawsock library. Used for poll(). + int sock; + // The streamID - in network form + U8 streamIDnet[8]; + // The destination address for stream + struct ether_addr dest_addr; + // The AVTP subtype; it determines the encapsulation + U8 subtype; + // Max Transit - value added to current time to get play time + U64 max_transit_usec; + // Max frame size + U16 frameLen; + // AVTP sequence number + U8 avtp_sequence_num; + // Paused state of the stream + bool bPause; + // Encapsulation-specific state information + avtp_state_t state; + // RX info for data sample currently being received + avtp_info_t info; + // Mapping callbacks + openavb_map_cb_t *pMapCB; + // Interface callbacks + openavb_intf_cb_t *pIntfCB; + // MediaQ + media_q_t *pMediaQ; + bool bRxSignalMode; + + // TX frame buffer + U8* pBuf; + // Ethernet header length + U32 ethHdrLen; + + // Timestamp evaluation related + openavb_timestamp_eval_t tsEval; + + // Stat related + // RX frames lost + int nLost; + // Bytes sent or recieved + U64 bytes; + +} avtp_stream_t; + + +typedef void (*avtp_listener_callback_fn)(void *pv, avtp_info_t *data); + +// tx/rx +openavbRC openavbAvtpTxInit(media_q_t *pMediaQ, + openavb_map_cb_t *pMapCB, + openavb_intf_cb_t *pIntfCB, + char* ifname, + AVBStreamID_t *streamID, + U8* destAddr, + U32 max_transit_usec, + U32 fwmark, + U16 vlanID, + U8 vlanPCP, + U16 nbuffers, + void **pStream_out); + +openavbRC openavbAvtpTx(void *pv, bool bSend, bool txBlockingInIntf); + +openavbRC openavbAvtpRxInit(media_q_t *pMediaQ, + openavb_map_cb_t *pMapCB, + openavb_intf_cb_t *pIntfCB, + char* ifname, + AVBStreamID_t *streamID, + U8* destAddr, + U16 nbuffers, + bool rxSignalMode, + void **pStream_out); + +openavbRC openavbAvtpRx(void *handle); + +void openavbAvtpConfigTimsstampEval(void *handle, U32 tsInterval, U32 reportInterval, bool smoothing, U32 tsMaxJitter, U32 tsMaxDrift); + +void openavbAvtpPause(void *handle, bool bPause); + +void openavbAvtpShutdown(void *handle); + +int openavbAvtpTxBufferLevel(void *handle); + +int openavbAvtpRxBufferLevel(void *handle); + +int openavbAvtpLost(void *handle); + +U64 openavbAvtpBytes(void *handle); + +#endif //AVB_AVTP_H diff --git a/lib/avtp_pipeline/avtp/openavb_avtp_time.c b/lib/avtp_pipeline/avtp/openavb_avtp_time.c new file mode 100644 index 00000000..39140c87 --- /dev/null +++ b/lib/avtp_pipeline/avtp/openavb_avtp_time.c @@ -0,0 +1,438 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Avtp Time implementation +*/ + +#include "openavb_platform.h" +#include <stdlib.h> + +#include "openavb_types.h" +#include "openavb_trace.h" +#include "openavb_avtp_time_pub.h" + +#define AVB_LOG_COMPONENT "AVTP" +#include "openavb_log.h" + +#define OPENAVB_AVTP_TIME_MAX_TS_DIFF (0x7FFFFFFF) + +#include "openavb_avtp_time_osal.c" + +static U64 x_getNsTime(timespec_t *tmTime) +{ + return ((U64)tmTime->tv_sec * (U64)NANOSECONDS_PER_SECOND) + (U64)tmTime->tv_nsec; +} + +static U32 x_getTimestamp(U64 timeNsec) +{ + return (U32)(timeNsec & 0x00000000FFFFFFFFL); +} + +avtp_time_t* openavbAvtpTimeCreate(U32 maxLatencyUsec) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVTP_TIME); + + avtp_time_t *pAvtpTime = calloc(1, sizeof(avtp_time_t)); + + if (pAvtpTime) { + pAvtpTime->maxLatencyNsec = maxLatencyUsec * NANOSECONDS_PER_USEC; + } + + AVB_TRACE_EXIT(AVB_TRACE_AVTP_TIME); + return pAvtpTime; +} + +void openavbAvtpTimeDelete(avtp_time_t *pAvtpTime) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVTP_TIME); + + if (pAvtpTime) { + free(pAvtpTime); + pAvtpTime = NULL; + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + } + + AVB_TRACE_EXIT(AVB_TRACE_AVTP_TIME); +} + +void openavbAvtpTimeAddUSec(avtp_time_t *pAvtpTime, long uSec) +{ + if (pAvtpTime) { + pAvtpTime->timeNsec += uSec * NANOSECONDS_PER_USEC; + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + } +} + +void openavbAvtpTimeAddNSec(avtp_time_t *pAvtpTime, long nSec) +{ + if (pAvtpTime) { + pAvtpTime->timeNsec += nSec; + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + } +} + +void openavbAvtpTimeSubUSec(avtp_time_t *pAvtpTime, long uSec) +{ + if (pAvtpTime) { + pAvtpTime->timeNsec -= uSec * NANOSECONDS_PER_USEC; + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + } +} + +void openavbAvtpTimeSubNSec(avtp_time_t *pAvtpTime, long nSec) +{ + if (pAvtpTime) { + pAvtpTime->timeNsec -= nSec; + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + } +} + + +void openavbAvtpTimeSetToWallTime(avtp_time_t *pAvtpTime) +{ + if (pAvtpTime) { + timespec_t tmNow; + if (CLOCK_GETTIME(OPENAVB_CLOCK_WALLTIME, &tmNow)) { + pAvtpTime->timeNsec = x_getNsTime(&tmNow); + pAvtpTime->bTimestampValid = TRUE; + pAvtpTime->bTimestampUncertain = FALSE; + } + else { + pAvtpTime->bTimestampValid = FALSE; + pAvtpTime->bTimestampUncertain = FALSE; + } + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + } +} + +void openavbAvtpTimeSetToSystemTime(avtp_time_t *pAvtpTime) +{ + if (pAvtpTime) { + timespec_t tmNow = {0, 0}; + CLOCK_GETTIME(OPENAVB_CLOCK_REALTIME, &tmNow); + pAvtpTime->timeNsec = x_getNsTime(&tmNow); + pAvtpTime->bTimestampValid = tmNow.tv_sec || tmNow.tv_nsec; + pAvtpTime->bTimestampUncertain = FALSE; + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + } +} + +// This function will take a AVTP timestamp and set the local AvtpTime values for it. +// Commonly this is used in the RX mapping callbacks, to take the AVTP timestamp received +// on the listener and place it into the AvtpTime so that the media queue and determine +// the correct time to release the media queue item for presentation. +void openavbAvtpTimeSetToTimestamp(avtp_time_t *pAvtpTime, U32 timestamp) +{ + if (pAvtpTime) { + timespec_t tmNow; + if (CLOCK_GETTIME(OPENAVB_CLOCK_WALLTIME, &tmNow)) { + U64 nsNow = x_getNsTime(&tmNow); + U32 tsNow = x_getTimestamp(nsNow); + + U32 delta; + if (tsNow < timestamp) { + delta = timestamp - tsNow; + } + else if (tsNow > timestamp) { + delta = timestamp + (0x100000000ULL - tsNow); + } + else { + delta = 0; + } + + if (delta < OPENAVB_AVTP_TIME_MAX_TS_DIFF) { + // Normal case, timestamp is upcoming + pAvtpTime->timeNsec = nsNow + delta; + } + else { + // Timestamp is past + pAvtpTime->timeNsec = nsNow - (0x100000000ULL - delta); + } + + pAvtpTime->bTimestampValid = TRUE; + pAvtpTime->bTimestampUncertain = FALSE; + } + else { + pAvtpTime->bTimestampValid = FALSE; + pAvtpTime->bTimestampUncertain = TRUE; + } + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + } +} + +void openavbAvtpTimeSetToTimestampNS(avtp_time_t *pAvtpTime, U64 timeNS) +{ + if (pAvtpTime) { + pAvtpTime->timeNsec = timeNS; + pAvtpTime->bTimestampValid = TRUE; + pAvtpTime->bTimestampUncertain = FALSE; + } +} + +void openavbAvtpTimeSetToTimespec(avtp_time_t *pAvtpTime, timespec_t* timestamp) +{ + if (pAvtpTime) + { + if ((timestamp->tv_sec == 0) && (timestamp->tv_nsec == 0)) + { + pAvtpTime->bTimestampValid = FALSE; + pAvtpTime->bTimestampUncertain = TRUE; + } + else + { + U64 nsec = x_getNsTime(timestamp); + pAvtpTime->timeNsec = nsec; + pAvtpTime->bTimestampValid = TRUE; + pAvtpTime->bTimestampUncertain = FALSE; + } + } + else + { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + } +} + +void openavbAvtpTimePushMCR(avtp_time_t *pAvtpTime, U32 timestamp) +{ + if (pAvtpTime) + { + if ( HAL_PUSH_MCR(×tamp) == FALSE) { + AVB_LOG_ERROR("Pushing MCR timestamp"); + } + } +} + + +void openavbAvtpTimeSetTimestampValid(avtp_time_t *pAvtpTime, bool validFlag) +{ + if (pAvtpTime) { + pAvtpTime->bTimestampValid = validFlag; + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + } +} + +void openavbAvtpTimeSetTimestampUncertain(avtp_time_t *pAvtpTime, bool uncertainFlag) +{ + if (pAvtpTime) { + pAvtpTime->bTimestampUncertain = uncertainFlag; + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + } +} + +U32 openavbAvtpTimeGetAvtpTimestamp(avtp_time_t *pAvtpTime) +{ + if (pAvtpTime) { + return x_getTimestamp(pAvtpTime->timeNsec); + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + return 0; + } +} + +U64 openavbAvtpTimeGetAvtpTimeNS(avtp_time_t *pAvtpTime) +{ + if (pAvtpTime) { + return pAvtpTime->timeNsec; + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + return 0; + } +} + +bool openavbAvtpTimeTimestampIsValid(avtp_time_t *pAvtpTime) +{ + if (pAvtpTime) { + return pAvtpTime->bTimestampValid; + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + return FALSE; + } +} + +bool openavbAvtpTimeTimestampIsUncertain(avtp_time_t *pAvtpTime) +{ + if (pAvtpTime) { + return pAvtpTime->bTimestampUncertain; + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + return TRUE; + } +} + +// Check if the AvtpTime is past now. If something goes wrong return true to allow +// data to continue to flow. +bool openavbAvtpTimeIsPast(avtp_time_t *pAvtpTime) +{ + if (pAvtpTime) { + timespec_t tmNow; + + if (!pAvtpTime->bTimestampValid || pAvtpTime->bTimestampUncertain) { + return TRUE; // If timestamp can't be trusted assume time is past. + } + + if (CLOCK_GETTIME(OPENAVB_CLOCK_WALLTIME, &tmNow)) { + U64 nsNow = x_getNsTime(&tmNow); + + if (nsNow >= pAvtpTime->timeNsec) { + return TRUE; // Normal timestamp time reached. + } + + if (nsNow + pAvtpTime->maxLatencyNsec < pAvtpTime->timeNsec) { + IF_LOG_INTERVAL(100) { + AVB_LOGF_INFO("Timestamp out of range: Now:%llu TSTime%llu MaxLatency:%lluns Delta:%lluns", nsNow, pAvtpTime->timeNsec, pAvtpTime->maxLatencyNsec, pAvtpTime->timeNsec - nsNow); + } + return TRUE; + } + + return FALSE; + } + else { + return TRUE; // If timestamp can't be retrieved assume time is past to keep data flowing. + } + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + return TRUE; + } +} + +bool openavbAvtpTimeIsPastTime(avtp_time_t *pAvtpTime, U64 nSecTime) +{ + if (pAvtpTime) { + + if (!pAvtpTime->bTimestampValid || pAvtpTime->bTimestampUncertain) { + return TRUE; // If timestamp can't be trusted assume time is past. + } + + if (nSecTime >= pAvtpTime->timeNsec) { + return TRUE; // Normal timestamp time reached. + } + + if (nSecTime + pAvtpTime->maxLatencyNsec < pAvtpTime->timeNsec) { + IF_LOG_INTERVAL(100) { + AVB_LOGF_INFO("Timestamp out of range: Now:%llu TSTime%llu MaxLatency:%lluns Delta:%lluns", nSecTime, pAvtpTime->timeNsec, pAvtpTime->maxLatencyNsec, pAvtpTime->timeNsec - nSecTime); + } + return TRUE; + } + + return FALSE; +} + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + return TRUE; + } +} + + + +bool openavbAvtpTimeUsecTill(avtp_time_t *pAvtpTime, U32 *pUsecTill) +{ + if (pAvtpTime) { + if (pAvtpTime->bTimestampValid && !pAvtpTime->bTimestampUncertain) { + + timespec_t tmNow; + + if (CLOCK_GETTIME(OPENAVB_CLOCK_WALLTIME, &tmNow)) { + U64 nsNow = x_getNsTime(&tmNow); + + if (pAvtpTime->timeNsec >= nsNow) { + U32 usecTill = (pAvtpTime->timeNsec - nsNow) / NANOSECONDS_PER_USEC; + + if (usecTill <= MICROSECONDS_PER_SECOND * 5) { + *pUsecTill = usecTill; + return TRUE; + } + else { + return FALSE; + } + } + else { + *pUsecTill = 0; + return TRUE; + } + } + } + + // When the timestamp is invalid or uncertain assume timestamp is now. + *pUsecTill = 0; + return TRUE; + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + return FALSE; + } +} + +S32 openavbAvtpTimeUsecDelta(avtp_time_t *pAvtpTime) +{ + S32 delta = 0; + if (pAvtpTime) { + if (pAvtpTime->bTimestampValid && !pAvtpTime->bTimestampUncertain) { + timespec_t tmNow; + + if (CLOCK_GETTIME(OPENAVB_CLOCK_WALLTIME, &tmNow)) { + U64 nsNow = x_getNsTime(&tmNow); + + delta = (S64)(pAvtpTime->timeNsec - nsNow) / NANOSECONDS_PER_USEC; + } + } + } + else { + AVB_RC_LOG(AVB_RC(OPENAVB_AVTP_TIME_FAILURE | OPENAVBAVTPTIME_RC_INVALID_PTP_TIME)); + } + return delta; +} + + diff --git a/lib/avtp_pipeline/avtp/openavb_avtp_time_pub.h b/lib/avtp_pipeline/avtp/openavb_avtp_time_pub.h new file mode 100644 index 00000000..c91ee104 --- /dev/null +++ b/lib/avtp_pipeline/avtp/openavb_avtp_time_pub.h @@ -0,0 +1,275 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : AVTP Time public interface +*/ + +#ifndef OPENAVB_AVTP_TIME_PUB_H +#define OPENAVB_AVTP_TIME_PUB_H 1 + +#include "openavb_platform_pub.h" +#include "openavb_types_pub.h" + +/** \file + * AVTP Time public interface. + */ + +/// standard timespec type. +typedef struct timespec timespec_t; + +/** AVTP time structure. + */ +typedef struct { + /// Time in nanoseconds. + U64 timeNsec; + + /// Maximum latency. Timestamps greater than now + max latency will be considered uncertain. + U64 maxLatencyNsec; + + /// Timestamp valid. + bool bTimestampValid; + + /// Timestamp uncertain. + bool bTimestampUncertain; +} avtp_time_t; + + +/** Create a avtp_time_t structure. + * + * Allocate storage for a avtp_time_t structure. When a media queue items are + * created an avtp_time_t structure is allocated for each item. Interface + * modules do not need to be concerned with doing this. + * + * \param maxLatencyUsec Maximum Latency (in usec) for the avtp_time_t + * structure. Timestamps greater than now + maximum latency are + * considered uncertain. + * \return A pointer to the avtp_time_t structure. Returns NULL if the memory + * could not be allocated. + */ +avtp_time_t * openavbAvtpTimeCreate(U32 maxLatencyUsec); + +/** Delete the time struct. + * + * Delete the avtp_time_t structure and any additional allocations it owns. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + */ +void openavbAvtpTimeDelete(avtp_time_t *pAvtpTime); + +/** Set to wall time (gPTP time). + * + * Set the time in the avtp_time_t structure to that of the synchronized PTP + * time. An interface module will normally use this function to set the time + * that media data was placed into the media queue. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + */ +void openavbAvtpTimeSetToWallTime(avtp_time_t *pAvtpTime); + +/** Set to system time. + * + * Set the time in the avtp_time_t structure to that of the system time on the + * device. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + */ +void openavbAvtpTimeSetToSystemTime(avtp_time_t *pAvtpTime); + +/** Set to timestamp. + * + * Set the time in the avtp_time_t structure to the value of the timestamp + * parameter which is in the same format as an AVTP timestamp. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \param timestamp A timestamp in the same format as the 1722 AVTP timestamp. + */ +void openavbAvtpTimeSetToTimestamp(avtp_time_t *pAvtpTime, U32 timestamp); + +/** Set to timestamp. + * + * Set the time in the avtp_time_t structure to the value of timespec_t + * *timestamp. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \param timestamp A timestamp in the timespec_t format. + */ +void openavbAvtpTimeSetToTimespec(avtp_time_t *pAvtpTime, timespec_t* timestamp); + +/** Push a timestamp, for use in Media Clock Recovery (MCR). + * \note Not available in all platforms. + * + * Push a timestamp, for use in Media Clock Recover (MCR). *pAvtpTime is not + * modified. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \param timestamp A timestamp in the same format as the 1722 AVTP timestamp. + */ +void openavbAvtpTimePushMCR(avtp_time_t *pAvtpTime, U32 timestamp); + +/** Set the AVTP timestamp valid indicator. + * + * Sets the indicator for AVTP timestamp is valid or not. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \param validFlag Flag indicating is timestamp is valid. + */ +void openavbAvtpTimeSetTimestampValid(avtp_time_t *pAvtpTime, bool validFlag); + +/** Set the AVTP timestamp uncertain indicator. + * + * Sets the indicator for AVTP timestamp is uncertain or not. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \param uncertainFlag Flag indicating is timestamp is uncertain. + */ +void openavbAvtpTimeSetTimestampUncertain(avtp_time_t *pAvtpTime, bool uncertainFlag); + +/** Add microseconds to the time. + * + * Add the number of microseconds passed in to the time stored in avtp_time_t. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \param uSec Number of microseconds to add to the stored time. + */ +void openavbAvtpTimeAddUSec(avtp_time_t *pAvtpTime, long uSec); + +/** Add nanoseconds to the time. + * + * Add the number of nanoseconds passed in to the time stored in avtp_time_t. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \param nSec Number of nanoseconds to add to the stored time. + */ +void openavbAvtpTimeAddNSec(avtp_time_t *pAvtpTime, long nSec); + +/** Subtract microseconds from the time + * + * Subtract the number of microseconds passed in from the time + * stored in avtp_time_t. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \param uSec Number of microseconds to subtract from the + * stored time. + */ +void openavbAvtpTimeSubUSec(avtp_time_t *pAvtpTime, long uSec); + +/** Subtract nanoseconds from the time + * + * Subtract the number of nanoseconds passed in from the time + * stored in avtp_time_t. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \param nSec Number of nanoseconds to subtract from the stored + * time. + */ +void openavbAvtpTimeSubNSec(avtp_time_t *pAvtpTime, long nSec); + +/** Get AVTP timestamp + * + * Get the time stored in avtp_time_t and return it in an AVTP timestamp format + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \return Returns an integer (U32) in the same format as a 1722 AVTP Timestamp. + */ +U32 openavbAvtpTimeGetAvtpTimestamp(avtp_time_t *pAvtpTime); + +/** Get AVTP timestamp in nanoseconds + * + * Get the time stored in avtp_time_t and return it as a full time value in nanoseconds + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \return Returns an integer (U64) of the full walltime in + * nanoseconds. + */ +U64 openavbAvtpTimeGetAvtpTimeNS(avtp_time_t *pAvtpTime); + +/** Get the AVTP timestamp valid indicator. + * + * Gets the indicator for AVTP timestamp is valid or not. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \return Returns TRUE if the AVTP timestamp valid indicator is set otherwise + * FALSE. + */ +bool openavbAvtpTimeTimestampIsValid(avtp_time_t *pAvtpTime); + +/** Get the AVTP timestamp uncertain indicator. + * + * Gets the indicator for AVTP timestamp is uncertain or not. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \return Returns TRUE if the AVTP timestamp uncertain indicator is set + * otherwise FALSE. + */ +bool openavbAvtpTimeTimestampIsUncertain(avtp_time_t *pAvtpTime); + +/** Check if time is in the past. + * + * Checks if the time stored in avtp_time_t is past the PTP wall time. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \return Returns TRUE if time is in the past otherwise FALSE. + */ +bool openavbAvtpTimeIsPast(avtp_time_t *pAvtpTime); + +/** Check if time is in the past a specific time (PTP time) + * + * Checks if the time stored in avtp_time_t is past the time + * passed in. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \param nSecTime Time in nanoseconds to compare against. + * \return Returns TRUE if time is in the past otherwise FALSE. + */ +bool openavbAvtpTimeIsPastTime(avtp_time_t *pAvtpTime, U64 nSecTime); + +/** Determines microseconds until PTP time. + * + * Returns the number of microseconds until the time stored in avtp_time_t + * reaches the PTP time. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \param pUsecTill An output parameter that is set with the number of + * microseconds until the time is reached. + * \return Return FALSE if greater than 5 second otherwise TRUE. + */ +bool openavbAvtpTimeUsecTill(avtp_time_t *pAvtpTime, U32 *pUsecTill); + +/** Returns delta from timestamp and now. + * + * Returns difference between timestamp and current time. + * + * \param pAvtpTime A pointer to the avtp_time_t structure. + * \return Difference in microseconds between timestamp and now. + */ +S32 openavbAvtpTimeUsecDelta(avtp_time_t *pAvtpTime); + +#endif // OPENAVB_AVTP_TIME_PUB_H diff --git a/lib/avtp_pipeline/cmake/FindALSA.cmake b/lib/avtp_pipeline/cmake/FindALSA.cmake new file mode 100644 index 00000000..8bdf01aa --- /dev/null +++ b/lib/avtp_pipeline/cmake/FindALSA.cmake @@ -0,0 +1,36 @@ +# - Try to find ALSA +# Once done, this will define +# +# ALSA_FOUND - system has ALSA (GL and GLU) +# ALSA_INCLUDE_DIRS - the ALSA include directories +# ALSA_LIBRARIES - link these to use ALSA +# ALSA_GL_LIBRARY - only GL +# ALSA_GLU_LIBRARY - only GLU +# +# See documentation on how to write CMake scripts at +# http://www.cmake.org/Wiki/CMake:How_To_Find_Libraries + +include(LibFindMacros) + +libfind_pkg_check_modules(ALSA alsa) + +find_path(ALSA_INCLUDE_DIR + NAMES alsa/version.h + PATHS ${ALSA_INCLUDE_DIRS} +) + +find_library(ALSA_LIBRARY + NAMES asound + PATHS ${ALSA_LIBRARY_DIRS} +) + +# Extract the version number +IF(ALSA_INCLUDE_DIR) +file(READ "${ALSA_INCLUDE_DIR}/alsa/version.h" _ALSA_VERSION_H_CONTENTS) +string(REGEX REPLACE ".*#define SND_LIB_VERSION_STR[ \t]*\"([^\n]*)\".*" "\\1" ALSA_VERSION "${_ALSA_VERSION_H_CONTENTS}") +ENDIF(ALSA_INCLUDE_DIR) + +set(ALSA_PROCESS_INCLUDES ALSA_INCLUDE_DIR) +set(ALSA_PROCESS_LIBS ALSA_LIBRARY) +libfind_process(ALSA) + diff --git a/lib/avtp_pipeline/cmake/LibFindMacros.cmake b/lib/avtp_pipeline/cmake/LibFindMacros.cmake new file mode 100644 index 00000000..69975c51 --- /dev/null +++ b/lib/avtp_pipeline/cmake/LibFindMacros.cmake @@ -0,0 +1,99 @@ +# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments +# used for the current package. For this to work, the first parameter must be the +# prefix of the current package, then the prefix of the new package etc, which are +# passed to find_package. +macro (libfind_package PREFIX) + set (LIBFIND_PACKAGE_ARGS ${ARGN}) + if (${PREFIX}_FIND_QUIETLY) + set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) + endif (${PREFIX}_FIND_QUIETLY) + if (${PREFIX}_FIND_REQUIRED) + set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) + endif (${PREFIX}_FIND_REQUIRED) + find_package(${LIBFIND_PACKAGE_ARGS}) +endmacro (libfind_package) + +# CMake developers made the UsePkgConfig system deprecated in the same release (2.6) +# where they added pkg_check_modules. Consequently I need to support both in my scripts +# to avoid those deprecated warnings. Here's a helper that does just that. +# Works identically to pkg_check_modules, except that no checks are needed prior to use. +macro (libfind_pkg_check_modules PREFIX PKGNAME) + if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + include(UsePkgConfig) + pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) + else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(${PREFIX} ${PKGNAME}) + endif (PKG_CONFIG_FOUND) + endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) +endmacro (libfind_pkg_check_modules) + +# Do the final processing once the paths have been detected. +# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain +# all the variables, each of which contain one include directory. +# Ditto for ${PREFIX}_PROCESS_LIBS and library files. +# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. +# Also handles errors in case library detection was required, etc. +macro (libfind_process PREFIX) + # Skip processing if already processed during this run + if (NOT ${PREFIX}_FOUND) + # Start with the assumption that the library was found + set (${PREFIX}_FOUND TRUE) + + # Process all includes and set _FOUND to false if any are missing + foreach (i ${${PREFIX}_PROCESS_INCLUDES}) + if (${i}) + set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) + mark_as_advanced(${i}) + else (${i}) + set (${PREFIX}_FOUND FALSE) + endif (${i}) + endforeach (i) + + # Process all libraries and set _FOUND to false if any are missing + foreach (i ${${PREFIX}_PROCESS_LIBS}) + if (${i}) + set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) + mark_as_advanced(${i}) + else (${i}) + set (${PREFIX}_FOUND FALSE) + endif (${i}) + endforeach (i) + + # Print message and/or exit on fatal error + if (${PREFIX}_FOUND) + if (NOT ${PREFIX}_FIND_QUIETLY) + message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") + endif (NOT ${PREFIX}_FIND_QUIETLY) + else (${PREFIX}_FOUND) + if (${PREFIX}_FIND_REQUIRED) + foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) + message("${i}=${${i}}") + endforeach (i) + message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") + endif (${PREFIX}_FIND_REQUIRED) + endif (${PREFIX}_FOUND) + endif (NOT ${PREFIX}_FOUND) +endmacro (libfind_process) + +macro(libfind_library PREFIX basename) + set(TMP "") + if(MSVC80) + set(TMP -vc80) + endif(MSVC80) + if(MSVC90) + set(TMP -vc90) + endif(MSVC90) + set(${PREFIX}_LIBNAMES ${basename}${TMP}) + if(${ARGC} GREATER 2) + set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) + string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) + set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) + endif(${ARGC} GREATER 2) + find_library(${PREFIX}_LIBRARY + NAMES ${${PREFIX}_LIBNAMES} + PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} + ) +endmacro(libfind_library) + diff --git a/lib/avtp_pipeline/documents/CMakeLists.txt b/lib/avtp_pipeline/documents/CMakeLists.txt new file mode 100644 index 00000000..464532cc --- /dev/null +++ b/lib/avtp_pipeline/documents/CMakeLists.txt @@ -0,0 +1,42 @@ +# add a target to generate API documentation with Doxygen +find_package(Doxygen) + +if(DOXYGEN_FOUND) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in + ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/DoxygenLayout.xml + ${CMAKE_CURRENT_BINARY_DIR}/DoxygenLayout.xml @ONLY) + +add_custom_target(doc + ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" VERBATIM +) + +add_custom_target(pdfdoc + make 1>/dev/null 2>/dev/null + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/latex + DEPENDS doc + COMMENT "Generating PDF document" +) + +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/api_docs + DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} OPTIONAL) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/latex/refman.pdf + DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} + RENAME "OPENAVB EAVB SDK.pdf" + OPTIONAL) + +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/api_docs + DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} OPTIONAL) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/latex/refman.pdf + DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} + RENAME "OPENAVB EAVB SDK.pdf" + OPTIONAL) + + +endif(DOXYGEN_FOUND) diff --git a/lib/avtp_pipeline/documents/Doxyfile.in b/lib/avtp_pipeline/documents/Doxyfile.in new file mode 100644 index 00000000..77df2da4 --- /dev/null +++ b/lib/avtp_pipeline/documents/Doxyfile.in @@ -0,0 +1,355 @@ +# Doxyfile 1.8.6 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "OPENAVB EAVB SDK" +PROJECT_NUMBER = 1.4 +PROJECT_BRIEF = +PROJECT_LOGO = @CMAKE_CURRENT_SOURCE_DIR@/images/openavb_logo_small.png +OUTPUT_DIRECTORY = +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 4 +ALIASES = +TCL_SUBST = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = YES +LOOKUP_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = YES +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = NO +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/index.md \ + @CMAKE_CURRENT_SOURCE_DIR@/sdk_overview.md \ + @CMAKE_CURRENT_SOURCE_DIR@/sdk_eavb_integration.md \ + @CMAKE_CURRENT_SOURCE_DIR@/sdk_avtp_stream_cfg.md \ + @CMAKE_CURRENT_SOURCE_DIR@/sdk_avtp_interface_module_dev.md \ + @CMAKE_CURRENT_SOURCE_DIR@/sdk_notes.md \ + @CMAKE_CURRENT_SOURCE_DIR@/sdk_notes_media_queue_usage.md \ + @CMAKE_CURRENT_SOURCE_DIR@/../map_aaf_audio \ + @CMAKE_CURRENT_SOURCE_DIR@/../map_ctrl \ + @CMAKE_CURRENT_SOURCE_DIR@/../map_mjpeg \ + @CMAKE_CURRENT_SOURCE_DIR@/../map_mpeg2ts \ + @CMAKE_CURRENT_SOURCE_DIR@/../map_null \ + @CMAKE_CURRENT_SOURCE_DIR@/../map_pipe \ + @CMAKE_CURRENT_SOURCE_DIR@/../map_uncmp_audio \ + @CMAKE_CURRENT_SOURCE_DIR@/../platform/Linux/intf_alsa \ + @CMAKE_CURRENT_SOURCE_DIR@/../intf_ctrl \ + @CMAKE_CURRENT_SOURCE_DIR@/../intf_echo \ + @CMAKE_CURRENT_SOURCE_DIR@/../intf_logger \ + @CMAKE_CURRENT_SOURCE_DIR@/../intf_null \ + @CMAKE_CURRENT_SOURCE_DIR@/../intf_tonegen \ + @CMAKE_CURRENT_SOURCE_DIR@/../intf_viewer \ + @CMAKE_CURRENT_SOURCE_DIR@/../platform/Linux/intf_mjpeg_camera \ + @CMAKE_CURRENT_SOURCE_DIR@/../platform/Linux/intf_mjpeg_gst \ + @CMAKE_CURRENT_SOURCE_DIR@/../platform/Linux/intf_mpeg2ts_file \ + @CMAKE_CURRENT_SOURCE_DIR@/../platform/Linux/intf_mpeg2ts_gst \ + @CMAKE_CURRENT_SOURCE_DIR@/../platform/Linux/intf_pipe_gst \ + @CMAKE_CURRENT_SOURCE_DIR@/../platform/Linux/intf_wav_file \ + @CMAKE_CURRENT_SOURCE_DIR@/../include \ + @CMAKE_CURRENT_SOURCE_DIR@/../avtp \ + @CMAKE_CURRENT_SOURCE_DIR@/../mediaq \ + @CMAKE_CURRENT_SOURCE_DIR@/../tl \ +# @CMAKE_CURRENT_SOURCE_DIR@/../adp \ +# @CMAKE_CURRENT_SOURCE_DIR@/../aecp \ +# @CMAKE_CURRENT_SOURCE_DIR@/../aem \ +# @CMAKE_CURRENT_SOURCE_DIR@/../avdecc \ +# @CMAKE_CURRENT_SOURCE_DIR@/../intf_ctrl \ +# @CMAKE_CURRENT_SOURCE_DIR@/../map_ctrl \ +# @CMAKE_CURRENT_SOURCE_DIR@/../map_h264 \ +# @CMAKE_CURRENT_SOURCE_DIR@/../map_mjpeg \ +# @CMAKE_CURRENT_SOURCE_DIR@/../map_mpeg2ts \ +# @CMAKE_CURRENT_SOURCE_DIR@/../map_null \ +# @CMAKE_CURRENT_SOURCE_DIR@/../map_pipe \ +# @CMAKE_CURRENT_SOURCE_DIR@/../map_saf_audio \ +# @CMAKE_CURRENT_SOURCE_DIR@/../map_uncmp_audio \ + +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *_pub.h *.md +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = @CMAKE_CURRENT_SOURCE_DIR@/../intf_echo \ + @CMAKE_CURRENT_SOURCE_DIR@/../platform/Linux/avb_host \ + +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = @CMAKE_CURRENT_SOURCE_DIR@/images +INPUT_FILTER = +# In markdown files join lines ended with '\' +FILTER_PATTERNS = "*.md=\"perl -p -e 's/\\\n//'\"" +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = api_docs +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = YES +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = YES +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = YES +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = YES +RTF_OUTPUT = rtf +COMPACT_RTF = YES +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = __GNUC__=4 +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +DIA_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DIAFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = YES +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/lib/avtp_pipeline/documents/DoxygenLayout.xml b/lib/avtp_pipeline/documents/DoxygenLayout.xml new file mode 100644 index 00000000..5b5f92b7 --- /dev/null +++ b/lib/avtp_pipeline/documents/DoxygenLayout.xml @@ -0,0 +1,194 @@ +<doxygenlayout version="1.0"> + <!-- Generated by doxygen 1.8.6 --> + <!-- Navigation index tabs for HTML output --> + <navindex> + <tab type="mainpage" visible="yes" title="Table of Contents"/> + <tab type="pages" visible="yes" title="Topics" intro=""/> + <tab type="modules" visible="yes" title="" intro=""/> + <tab type="namespaces" visible="yes" title=""> + <tab type="namespacelist" visible="yes" title="" intro=""/> + <tab type="namespacemembers" visible="yes" title="" intro=""/> + </tab> + <tab type="classes" visible="yes" title=""> + <tab type="classlist" visible="yes" title="" intro=""/> + <tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/> + <tab type="hierarchy" visible="yes" title="" intro=""/> + <tab type="classmembers" visible="yes" title="" intro=""/> + </tab> + <tab type="files" visible="yes" title=""> + <tab type="filelist" visible="yes" title="" intro=""/> + <tab type="globals" visible="yes" title="" intro=""/> + </tab> + <tab type="examples" visible="yes" title="" intro=""/> + </navindex> + + <!-- Layout definition for a class page --> + <class> + <briefdescription visible="yes"/> + <includes visible="$SHOW_INCLUDE_FILES"/> + <inheritancegraph visible="$CLASS_GRAPH"/> + <collaborationgraph visible="$COLLABORATION_GRAPH"/> + <memberdecl> + <nestedclasses visible="yes" title=""/> + <publictypes title=""/> + <services title=""/> + <interfaces title=""/> + <publicslots title=""/> + <signals title=""/> + <publicmethods title=""/> + <publicstaticmethods title=""/> + <publicattributes title=""/> + <publicstaticattributes title=""/> + <protectedtypes title=""/> + <protectedslots title=""/> + <protectedmethods title=""/> + <protectedstaticmethods title=""/> + <protectedattributes title=""/> + <protectedstaticattributes title=""/> + <packagetypes title=""/> + <packagemethods title=""/> + <packagestaticmethods title=""/> + <packageattributes title=""/> + <packagestaticattributes title=""/> + <properties title=""/> + <events title=""/> + <privatetypes title=""/> + <privateslots title=""/> + <privatemethods title=""/> + <privatestaticmethods title=""/> + <privateattributes title=""/> + <privatestaticattributes title=""/> + <friends title=""/> + <related title="" subtitle=""/> + <membergroups visible="yes"/> + </memberdecl> + <detaileddescription title=""/> + <memberdef> + <inlineclasses title=""/> + <typedefs title=""/> + <enums title=""/> + <services title=""/> + <interfaces title=""/> + <constructors title=""/> + <functions title=""/> + <related title=""/> + <variables title=""/> + <properties title=""/> + <events title=""/> + </memberdef> + <allmemberslink visible="yes"/> + <usedfiles visible="$SHOW_USED_FILES"/> + <authorsection visible="yes"/> + </class> + + <!-- Layout definition for a namespace page --> + <namespace> + <briefdescription visible="yes"/> + <memberdecl> + <nestednamespaces visible="yes" title=""/> + <constantgroups visible="yes" title=""/> + <classes visible="yes" title=""/> + <typedefs title=""/> + <enums title=""/> + <functions title=""/> + <variables title=""/> + <membergroups visible="yes"/> + </memberdecl> + <detaileddescription title=""/> + <memberdef> + <inlineclasses title=""/> + <typedefs title=""/> + <enums title=""/> + <functions title=""/> + <variables title=""/> + </memberdef> + <authorsection visible="yes"/> + </namespace> + + <!-- Layout definition for a file page --> + <file> + <briefdescription visible="yes"/> + <includes visible="$SHOW_INCLUDE_FILES"/> + <includegraph visible="$INCLUDE_GRAPH"/> + <includedbygraph visible="$INCLUDED_BY_GRAPH"/> + <sourcelink visible="yes"/> + <memberdecl> + <classes visible="yes" title=""/> + <namespaces visible="yes" title=""/> + <constantgroups visible="yes" title=""/> + <defines title=""/> + <typedefs title=""/> + <enums title=""/> + <functions title=""/> + <variables title=""/> + <membergroups visible="yes"/> + </memberdecl> + <detaileddescription title=""/> + <memberdef> + <inlineclasses title=""/> + <defines title=""/> + <typedefs title=""/> + <enums title=""/> + <functions title=""/> + <variables title=""/> + </memberdef> + <authorsection/> + </file> + + <!-- Layout definition for a group page --> + <group> + <briefdescription visible="yes"/> + <groupgraph visible="$GROUP_GRAPHS"/> + <memberdecl> + <nestedgroups visible="yes" title=""/> + <dirs visible="yes" title=""/> + <files visible="yes" title=""/> + <namespaces visible="yes" title=""/> + <classes visible="yes" title=""/> + <defines title=""/> + <typedefs title=""/> + <enums title=""/> + <enumvalues title=""/> + <functions title=""/> + <variables title=""/> + <signals title=""/> + <publicslots title=""/> + <protectedslots title=""/> + <privateslots title=""/> + <events title=""/> + <properties title=""/> + <friends title=""/> + <membergroups visible="yes"/> + </memberdecl> + <detaileddescription title=""/> + <memberdef> + <pagedocs/> + <inlineclasses title=""/> + <defines title=""/> + <typedefs title=""/> + <enums title=""/> + <enumvalues title=""/> + <functions title=""/> + <variables title=""/> + <signals title=""/> + <publicslots title=""/> + <protectedslots title=""/> + <privateslots title=""/> + <events title=""/> + <properties title=""/> + <friends title=""/> + </memberdef> + <authorsection visible="yes"/> + </group> + + <!-- Layout definition for a directory page --> + <directory> + <briefdescription visible="yes"/> + <directorygraph visible="yes"/> + <memberdecl> + <dirs visible="yes"/> + <files visible="yes"/> + </memberdecl> + <detaileddescription title=""/> + </directory> +</doxygenlayout> diff --git a/lib/avtp_pipeline/documents/Release Notes.docx b/lib/avtp_pipeline/documents/Release Notes.docx Binary files differnew file mode 100644 index 00000000..0edfb800 --- /dev/null +++ b/lib/avtp_pipeline/documents/Release Notes.docx diff --git a/lib/avtp_pipeline/documents/images/AVTP_Data_Flow.png b/lib/avtp_pipeline/documents/images/AVTP_Data_Flow.png Binary files differnew file mode 100644 index 00000000..2b19d9e0 --- /dev/null +++ b/lib/avtp_pipeline/documents/images/AVTP_Data_Flow.png diff --git a/lib/avtp_pipeline/documents/images/Core_AVB.png b/lib/avtp_pipeline/documents/images/Core_AVB.png Binary files differnew file mode 100644 index 00000000..cfb6f96c --- /dev/null +++ b/lib/avtp_pipeline/documents/images/Core_AVB.png diff --git a/lib/avtp_pipeline/documents/images/Listener_Stream_Data_Flow.png b/lib/avtp_pipeline/documents/images/Listener_Stream_Data_Flow.png Binary files differnew file mode 100644 index 00000000..7594107b --- /dev/null +++ b/lib/avtp_pipeline/documents/images/Listener_Stream_Data_Flow.png diff --git a/lib/avtp_pipeline/documents/images/Stream_Initialize.png b/lib/avtp_pipeline/documents/images/Stream_Initialize.png Binary files differnew file mode 100644 index 00000000..3b6c80f8 --- /dev/null +++ b/lib/avtp_pipeline/documents/images/Stream_Initialize.png diff --git a/lib/avtp_pipeline/documents/images/Talker_Stream_Data_Flow.png b/lib/avtp_pipeline/documents/images/Talker_Stream_Data_Flow.png Binary files differnew file mode 100644 index 00000000..e1a9cc5c --- /dev/null +++ b/lib/avtp_pipeline/documents/images/Talker_Stream_Data_Flow.png diff --git a/lib/avtp_pipeline/documents/images/fig1.png b/lib/avtp_pipeline/documents/images/fig1.png Binary files differnew file mode 100644 index 00000000..33ae6d37 --- /dev/null +++ b/lib/avtp_pipeline/documents/images/fig1.png diff --git a/lib/avtp_pipeline/documents/images/fig2.png b/lib/avtp_pipeline/documents/images/fig2.png Binary files differnew file mode 100644 index 00000000..1aecb34d --- /dev/null +++ b/lib/avtp_pipeline/documents/images/fig2.png diff --git a/lib/avtp_pipeline/documents/images/stc_logo_small.png b/lib/avtp_pipeline/documents/images/stc_logo_small.png Binary files differnew file mode 100644 index 00000000..7a316664 --- /dev/null +++ b/lib/avtp_pipeline/documents/images/stc_logo_small.png diff --git a/lib/avtp_pipeline/documents/index.md b/lib/avtp_pipeline/documents/index.md new file mode 100644 index 00000000..edf2d08e --- /dev/null +++ b/lib/avtp_pipeline/documents/index.md @@ -0,0 +1,52 @@ +Contents {#mainpage} +======== + +- [EAVB SDK Overview](@ref sdk_overview) + - [Introduction](@ref sdk_overview_introduction) + - [Glossary](@ref sdk_overview_glossary) + - [Architecture](@ref sdk_overview_architecture) + - [Integration, Extension and Configuration](@ref sdk_overview_integration) + - [Platform Specific Considerations](@ref sdk_overview_platform) +- [EAVB Integration](@ref sdk_integration) + - [Introduction] (@ref sdk_integration_introduction) + - [AVTP Control] (@ref sdk_integration_avtp_control) + - [Host Application Integration] (@ref sdk_integration_host_app) +- [AVTP Interface Module Development](@ref sdk_avtp_interface_module_dev) + - [Introduction] (@ref sdk_avtp_intf_module_introduction) + - [The Plug-in Architecture] (@ref sdk_avtp_intf_module_plugin) + - [Task / Thread Model] (@ref sdk_avtp_intf_module_task) + - [Building an Interface Module] (@ref sdk_avtp_intf_module_building) + - [Interface Module in a Talker] (@ref sdk_avtp_intf_module_talker) + - [Interface Module in a Listener] (@ref sdk_avtp_intf_module_listener) + - [Working With the Media Queue] (@ref working_with_mediaq) + - [Timestamps] (@ref sdk_avtp_intf_module_timestamps) +- [AVTP Stream (Talker/Listener) Configuration](@ref sdk_avtp_stream_cfg) + - [Overview](@ref sdk_avtp_stream_cfg_overview) + - [Example Interface Mapping Combinations](@ref sdk_avtp_stream_cfg_combinations) + - [Common Stream Configuration](@ref sdk_avtp_stream_cfg_common) + - [Interface and Mapping Module Configuration] (@ref sdk_avtp_stream_cfg_intf_map) + - Reference: AVTP Mapping Modules + - [1722 AAF (aaf_audio)](@ref aaf_audio_map) + - [Control (ctrl)](@ref ctrl_map) + - [Motion JPEG (mjpeg)](@ref mjpeg_map) + - [MPEG2 TS (mpeg2ts)](@ref mpeg2ts_map) + - [NULL (null)](@ref null_map) + - [PIPE (pipe)](@ref pipe_map) + - [61883-6 (uncmp_audio)](@ref uncmp_audio_map) + - Reference: AVTP Interface Module + - [Control (ctrl)](@ref ctrl_intf) + - [Echo (echo)](@ref echo_host_intf) + - [Logger (logger)](@ref logger_intf) + - [Null (null)](@ref null_host_intf) + - [Tone Generator (tonegen)](@ref tonegen_intf) + - [Viewer (viewer)](@ref viewer_intf) + - Reference: AVTP Interface Module Linux Specific + - [ALSA (alsa)](@ref alsa_intf) + - [MJPEG Camera (mjpeg_camera)](@ref mjpeg_camera_intf) + - [MJPEG GST (mjpeg_gstreamer)](@ref mjpeg_gst_intf) + - [MPEG2 TS File (mpeg2ts_file)](@ref mpeg2ts_file_intf) + - [MPEG2 TS GST (mpeg2ts_gstreamer)](@ref mpeg2ts_gst_intf) + - [WAV File (wav_file)](@ref wav_file_intf) +- [Developer Notes](@ref sdk_notes) + + diff --git a/lib/avtp_pipeline/documents/sdk_avtp_interface_module_dev.md b/lib/avtp_pipeline/documents/sdk_avtp_interface_module_dev.md new file mode 100644 index 00000000..4e6fb69f --- /dev/null +++ b/lib/avtp_pipeline/documents/sdk_avtp_interface_module_dev.md @@ -0,0 +1,165 @@ +AVTP Interface Module Development {#sdk_avtp_interface_module_dev} +================================= + +Introduction {#sdk_avtp_intf_module_introduction} +============ +Interface modules are the components that sit between the core AVB stack and the +platform device drivers that supply access to media hardware. + +<br> +The Plug-in Architecture {#sdk_avtp_intf_module_plugin} +======================== +The OPENAVB AVB stack has a plug-in architecture for the AVTP implementation in the +form of interface modules. This allows for easily hooking into the AVB stack to +extend it for interfacing to media specific hardware. + +Mapping modules, which are mentioned through-out these guides, are also +plug-ins. These implement the various AVTP encapsulations and require a deep +understanding of AVTP and therefore are developed internally by OPENAVB. + +Interface modules are discovered at runtime using configuration information +that is passed to the openavbTLOpen() function. Below is an example of a section of +the configuration information with interface module values. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// pIntfInitFn : Interface module initialization function. +// openavb_intf_initialize_fn_t) +cfg->osalCfg.pIntfInitFn = openavbIntfJ6Video; + +/////////////////// +// Interface module +/////////////////// + +// intf_nv_blocking_tx_callback : A talker only option. When set packet pacing +// is expected to be handled in the interface module. +// Commonly the TL configuration option tx_blocking_in_intf needs to be set +// to match this. +// cfg->libCfgNames[cfgIdx] = "intf_nv_blocking_tx_callback"; +// cfg->libCfgValues[cfgIdx++] = "1"; + +// intf_nv_ignore_timestamp : If set the listener will ignore the timestamp +// on media queue items. +cfg->libCfgNames[cfgIdx] = "intf_nv_ignore_timestamp"; +cfg->libCfgValues[cfgIdx++] = "0"; + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The function pointer variable `pIntfInitFn` points to the interface module +initialization function. + +When the talker/listener module calls the interface module initialization +function it passes in a callback structure as a parameter that must get filled +with the required callback function addresses by the interface module. + +After initialization the talker/listener module will call directly into the +interface module via these callbacks. See the API reference section for details +and examples of these callbacks. + +The callback functions in an interface module, aside from the initialization +call, receives a pointer to a media queue structure. This structure is +self-contained, similar to an object in C++, in that it includes a function +callback list for the interface module to call into the media queue to access +and work with the media queue. + +<br> +Task / Thread Model {#sdk_avtp_intf_module_task} +================= +In the general case the task / threading (hereafter referred to as task) model +for interface modules is very simple. All callbacks will occur on the same task +and therefore there are no synchronization concerns. However, access to media +sources and media sinks can have demanding timing and processing requirements +and it may be necessary for an interface module to have complete control over an +execution task to ensure timely pulling or pushing data into the media hardware +or driver. In those cases the interface module may find it a benefit to create +its own task or tasks. + +When an interface module has its own task the point of synchronization between +the callback thread and the interface module private thread would be the +sharing of a media queue item pointer. When a head or tail item is locked a +pointer to that media queue item is returned. At that point the data area for +that item remains locked and can be used by another task beyond the scope of +the callback into the interface module. The media queue item will remain locked +until unlocked (or pushed or pulled). The media queue itself can't be locked, +it can only be accessed within the scope of a callback into the interface +module. + +<br> +Building an Interface Module {#sdk_avtp_intf_module_building} +============================ +There should be minimal dependencies for creation of an interface module. The +interface module can be built as a static library and include all the required +callbacks as defined in the reference section of this document. + +Access to all functionality in the AVB stack from an interface module is also +handled via callbacks that are available to the interface module in the media +queue structure it receives as a parameter on its own callbacks. + +<br> +Interface Module in a Talker {#sdk_avtp_intf_module_talker} +============================ +An interface module when used in a talker pulls data from a media source and +pushes it onto the media queue in the format expected by the mapping module +configured for that stream. + +<br> +Interface Module in a Listener {#sdk_avtp_intf_module_listener} +============================== +When called from a listener it pulls data from the media queue and pushes it to +the media sink for presentation. + +<br> +Working With the Media Queue {#working_with_mediaq} +============================ +The media queue is the conduit between interface modules and mapping modules. +The media queue is the only entry point for interface modules to call into the +AVB stack and allows for pushing or pulling media data though it as well as +access to time functionality in the AVTP time structure. + +The media queue is internally implemented as a circular FIFO container. The +format of the data for items in the media queue is defined by the mapping +module being used in the talker/listener. For some mapping modules the data +format of an item will match the defined AVB encapsulation. For example the +MPEG2-TS (61883-4) mapping module data format of media queue items are simple +192 byte MPEG2-TS source packets. Each media queue item contains one or more +MPEG2-TS source packet. The actual MPEG2-TS mapping may combine multiple source +packets into one AVTP packet but that is hidden from the interface module. + +The media queue functions are accessed as API calls. + +For example, the openavbMediaQHeadLock() function is called as: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + openavbMediaQHeadLock(pMediaQ); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The pMediaQ pointer is passed to all of the interface module callbacks. + +Access to the queue items is accomplished with the Head functions for pushing +data into the media queue. These are used by interface modules running in a +talker. To read items from the media queue the Tail functions are used by +interface modules running in a listener. + +At any one time only 2 items are accessible in the media queue. These are the +head item and tail item. Care must be taken to always match up the +openavbMediaQHeadLock() with a openavbMediaQHeadUnlock() or openavbMediaQHeadPush() +functions. The same applies that the openavbMediaQTailLock() must be paired with +either a openavbMediaQTailPull() or openavbMediaQTailUnlock() functions. This means +a new item can not be added to the media queue until a openavbMediaQHeadPush() +is called and a new item can not be pulled from the media queue until +openavbMediaQTailPull() is called. + +For a detailed work flow please visit +[Media Queue Usage](@ref sdk_notes_media_queue_usage) + +<br> +Timestamps {#sdk_avtp_intf_module_timestamps} +========== +For interface modules often the only requirement for timestamps is to assign a +PTP time to the items added to the media queue when running as a talker. For +example: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In most cases the mapping modules and media queue take care of the rest of the +requirements for AVTP. + +[Source code](@ref openavb_intf_echo.c) diff --git a/lib/avtp_pipeline/documents/sdk_avtp_stream_cfg.md b/lib/avtp_pipeline/documents/sdk_avtp_stream_cfg.md new file mode 100644 index 00000000..10dc4d07 --- /dev/null +++ b/lib/avtp_pipeline/documents/sdk_avtp_stream_cfg.md @@ -0,0 +1,344 @@ +AVTP Stream (Talker/Listener) Configuration {#sdk_avtp_stream_cfg} +=========================================== + +Overview {#sdk_avtp_stream_cfg_overview} +======== + +The configuration of streams (talkers and listeners) is controlled via the +structure @ref openavb_tl_cfg_t that is passed to the @ref openavbTLConfigure function. +There are 3 major sections within the configuration structure. The general +talker / listener (AVTP stream) section, the mapping module section and the +interface module section. The general section has settings used by the +talker/listener module directly. The mapping module section has settings +specific to the mapping module being used for the stream and the interface +module section has settings specific to the interface module being used for the +stream. + +How the @ref openavb_tl_cfg_t structure gets set is platform dependent. For the +Linux reference implementation reading from .ini files is supported which in +turn fills this structure. For RTOSes the stream configuration structure is +usually set directly via code in the AVB host application module. Therefore the +use of .ini files is a layer above the what the core AVB stack uses. The Release +Notes for the AVB port should be referenced with regards to where the +configuration values can be set. + + +Here is a sample configuration structure initialization for a H.264 listener. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void h264_SampleListenerCfg(openavb_tl_cfg_t *cfg) +{ + // Clear our the configuration structure to ensure defaults (0) + memset(cfg, 0, sizeof(openavb_tl_cfg_t)); + + // This must be set to the multicast address that is used + U8 multicastStrmAddr[] = { 0x91, 0xE0, 0xF0, 0x00, 0xFE, 0x00 }; + + /////////////////// + // TL (Talker / Listener) Configuration + /////////////////// + + // Identify the role of the stream (talker or listener) + // must be set to AVB_ROLE_LISTENER or AVB_ROLE_TALKER + cfg->role = AVB_ROLE_LISTENER; + + memcpy(cfg->stream_addr.buffer.ether_addr_octet, multicastStrmAddr, ETH_ALEN); + cfg->stream_addr.mac = &cfg->stream_addr.buffer; + + // max_interval_frames: The maximum number of packets that will be sent during + // an observation interval. This is only used on the talker. + // cfg->max_interval_frames = 1; + + // max_transit_usec: Allows manually specifying a maximum transit time. + // On the talker this value is added to the PTP walltime to create the AVTP Timestamp. + // On the listener this value is used to validate an expected valid timestamp range. + // Note: For the listener the map_nv_item_count value must be set large enough to + // allow buffering at least as many AVTP packets that can be transmitted during this + // max transit time. + cfg->max_transit_usec = 2000; + + // max_transmit_deficit_usec: Allows setting the maximum packet transmit rate deficit that will + // be recovered when a talker falls behind. This is only used on a talker side. When a talker + // can not keep up with the specified transmit rate it builds up a deficit and will attempt to + // make up for this deficit by sending more packets. There is normally some variability in the + // transmit rate because of other demands on the system so this is expected. However, without this + // bounding value the deficit could grew too large in cases such where more streams are started + // than the system can support and when the number of streams is reduced the remaining streams + // will attempt to recover this deficit by sending packets at a higher rate. This can cause a problem + // at the listener side and significantly delay the recovery time before media playback will return + // to normal. Typically this value can be set to the expected buffer size (in usec) that listeners are + // expected to be buffering. For low latency solutions this is normally a small value. For non-live + // media playback such as video playback the listener side buffers can often be large enough to held many + // seconds of data. + // cfg->max_transmit_deficit_usec = 2000; + + // internal_latency: Allows mannually specifying an internal latency time. This is used + // only on the talker. + // cfg->internal_latency = 0; + + // The number of microseconds a media queue items can be passed the presentation time at which + // point it will be purged. + cfg->max_stale = 500; + + // number of intervals to handle at once (talker) + // cfg->batch_factor = 1; + + // report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. + cfg->report_seconds = 0; + + // sr_class: A talker only setting. Values are either SR_CLASS_A or SR_CLASS_B. + // cfg->sr_class = SR_CLASS_A; + + // raw_tx_buffers: The number of raw socket transmit buffers. + // cfg->raw_tx_buffers = 40; + + // raw_rx_buffers: The number of raw socket receive buffers. + cfg->raw_rx_buffers = 40; + + // tx_blocking_in_intf : A talker only option. When set packet pacing is expected to be handled in the interface module. + // Commonly there will be a matching interface module configuration item that needs to be set. + // cfg->tx_blocking_in_intf = FALSE; + + /////////////////// + // The remaining configuration items vary depending on the mapping module and interface module being used. + // These configuration values are populated as name value pairs. + /////////////////// + + // Configuration index counter. + int cfgIdx = 0; + + /////////////////// + // Mapping module + /////////////////// + // map_nv_item_count: The number of media queue elements to hold. + cfg->libCfgNames[cfgIdx] = "map_nv_item_count"; + cfg->libCfgValues[cfgIdx++] = "10"; + + // map_nv_tx_rate: Transmit rate. Typically this is set to match the SR Class. 8000 for A and 4000 for B + // cfg->libCfgNames[cfgIdx] = "map_nv_tx_rate"; + // cfg->libCfgValues[cfgIdx++] = "8000"; + + // map_nv_max_payload_size: This is the max RTP payload size. See RFC 6184 for details, + // 1412 is the default size + cfg->libCfgNames[cfgIdx] = "map_nv_max_payload_size"; + cfg->libCfgValues[cfgIdx++] = "1412"; + + /////////////////// + // Interface module + /////////////////// + + // intf_nv_blocking_tx_callback : A talker only option. When set packet pacing is expected to be handled in the interface module. + // Commonly the TL configuration option tx_blocking_in_intf needs to be set to match this. + // cfg->libCfgNames[cfgIdx] = "intf_nv_blocking_tx_callback"; + // cfg->libCfgValues[cfgIdx++] = "1"; + + // intf_nv_ignore_timestamp : If set the listener will ignore the timestamp on media queue items. + cfg->libCfgNames[cfgIdx] = "intf_nv_ignore_timestamp"; + cfg->libCfgValues[cfgIdx++] = "0"; + + cfg->nLibCfgItems = cfgIdx; + + /////////////////// + // Mapping and interface modules main initialization entry points + /////////////////// + + // pMapInitFn : Mapping module initialization function. (openavb_map_initialize_fn_t) + cfg->osalCfg.pMapInitFn = openavbMapH264Initialize; + + // pIntfInitFn : Interface module initialization function. (openavb_intf_initialize_fn_t) + cfg->osalCfg.pIntfInitFn = openavbIntfJ6Video; +} + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +<br> +Common Stream Configuration {#sdk_avtp_stream_cfg_common} +=========================== +These are common stream configuration values. + +Name | Description +--------------------|------------ +role |Sets the process as a talker or listener. Valid values are\ + *talker* or *listener*. +stream_addr |Used on the listener and should be set to the mac address \ + of the talker. +stream_uid |The unique stream ID. The talker and listener must both \ + have this set the same. +dest_addr |Destination multicast address for the stream.<br> \ + If using \ + <ul><li><b>with MAAP</b> - dynamic destination addresses \ + are generated automatically by the talker and passed to \ + the listener, and don't need to be configured.</li> \ + <li><b>without MAAP</b>, locally administered (static) \ + addresses must be configured. Those addresses are in the \ + range of: 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. Typically\ + use :00 for the first stream, :01 for the second, etc. \ + </li></ul> \ + If <b>SRP</b> \ + <ul><li><b>is being</b> used the static destination \ + address only needs to be set in the talker;</li> \ + <li><b>is not being</b> used the destination address need \ + to be set (to the same value) in both the talker and \ + listener.</li></ul> \ + The destination is a multicast address, not a real MAC \ + address, so it does not match the talker or listener's \ + interface MAC. There are several pools of those addresses \ + for use by AVTP defined in 1722. +max_interval_frames |The maximum number of packets that will be sent during an \ + observation interval. This is only used on the talker. +max_frame_size |Maximum size of the frame +sr_class |A talker only setting. Values are either A or B. If not \ + set an internal default is used. +sr_rank |A talker only setting. If not set an internal default is \ + used. +max_transit_usec |Allows manually specifying a maximum transit time. \ + <ul><li><b>On the talker</b> this value is added to the \ + PTP walltime to create the AVTP Timestamp.</li> \ + <li><b>On the listener</b> this value is used to validate \ + an expected valid timestamp range.</li></ul> \ + <b>Note:</b> For the listener the map_nv_item_count value \ + must be set large enough to allow buffering at least as \ + many AVTP packets that can be transmitted during this max \ + transit time. +max_transmit_deficit_usec |Allows setting the maximum packet transmit rate \ + deficit that will be recovered when a talker falls behind.\ + <p>When a talker can not keep up with the specified \ + transmit rate it builds up a deficit and will attempt to \ + make up for this deficit by sending more packets. There is\ + normally some variability in the transmit rate because of \ + other demands on the system so this is expected. However, \ + without this bounding value the deficit could grew too \ + large in cases such where more streams are started than \ + the system can support and when the number of streams is \ + reduced the remaining streams will attempt to recover this\ + deficit by sending packets at a higher rate. This can \ + cause a problem at the listener side and significantly \ + delay the recovery time before media playback will return \ + to normal.</p> \ + <p>Typically this value can be set to the expected buffer \ + size (in usec) that listeners are expected to be \ + buffering.<br> \ + For low latency solutions this is normally a small value. \ + For non-live media playback such as video playback the \ + listener side buffers can often be large enough to held \ + many seconds of data.</p> \ + <b>Note:</b> This is only used on a talker side. +internal_latency |Allows mannually specifying an internal latency time. This\ + is used only on the talker. +max_stale |The number of microseconds beyond the presentation time \ + that media queue items will be purged because they are too\ + old (past the presentation time).<br> \ + This is only used on listener end stations. \ + <p><b>Note:</b> needing to purge old media queue items is \ + often a sign of some other problem.<br> \ + For example: a delay at stream startup before incoming \ + packets are ready to be processed by the media sink.<br> \ + If this deficit in processing or purging the old (stale) \ + packets is not handled, syncing multiple listeners will be\ + problematic.</p> +raw_tx_buffers |The number of raw socket transmit buffers. Typically 4 - 8\ + are good values. This is only used by the talker. If not \ + set internal defaults are used. +raw_rx_buffers |The number of raw socket receive buffers. Typically 50 - \ + 100 are good values. This is only used by the listener. \ + If not set internal defaults are used. +report_seconds |How often to output stats. Defaults to 10 seconds. 0 turns\ + off the stats. +tx_blocking_in_intf |The interface module will block until data is available. \ + This is a talker only configuration value and not all interface modules support it. +pMapInitFn |Pointer to the mapping module initialization function. \ + Since this is a pointer to a function addresss is it not \ + directly set in platforms that use a .ini file. +IntfInitFn |Pointer to the interface module initialization function. \ + Since this is a pointer to a function addresss is it not \ + directly set in platforms that use a .ini file. + + +<br> +# Platform Specific Stream Configuration Values +Some platform ports have unique configuration values. + +## Linux + +Name | Description +--------------------------|--------------------------- +map_lib |The name of the library file (commonly a .so file) that \ + implements the Initialize function.<br> \ + Comment out the map_lib name and link in the .c file to \ + the openavb_tl executable to embed the mapper directly into \ + the executable unit. There is no need to change anything \ + else. The Initialize function will still be dynamically \ + linked in. +map_fn |The name of the initialize function in the mapper +intf_lib | The name of the library file (commonly a .so file) \ + that implements the Initialize function.<br> \ + Comment out the intf_lib name and link in the .c \ + file to the openavb_tl executable to embed the \ + interface directly into the executable unit.<br> \ + There is no need to change anything else. \ + The Initialize function will still be dynamically \ + linked in +intf_fn | The name of the initialize function in the interface + + +<br> +Example Interface / Mapping Combinations {#sdk_avtp_stream_cfg_combinations} +======================================== +Below are a few interface / mapping module combinations. Notice that a single +interface module may work with mutliple mapping modules. Additionally some +mappings may work with multiple interface modules. + +interface module | mapping module | description +----------------------------|-----------------------|------------ +[echo](@ref echo_host_intf) |[pipe](@ref pipe_map) |Demonstration interface \ + used mostly for \ + verification and testing \ + purposes. +[alsa](@ref alsa_intf) |[uncmp_audio](@ref uncmp_audio_map)|Audio \ + interface created for \ + demonstration on Linux. \ + Can be used to play \ + captured (line in, mic) \ + audio stream via EAVB +[alsa](@ref alsa_intf) |[aaf_audio](@ref aaf_audio_map)|Audio \ + interface created for \ + demonstration on Linux. \ + Can be used to play \ + captured (line in, mic) \ + audio stream via EAVB +[wav_file](@ref wav_file_intf)|[uncmp_audio](@ref uncmp_audio_map)| \ + Configuration for playing \ + wave file via EAVB + + +<br> +Interface and Mapping Module Configuration {#sdk_avtp_stream_cfg_intf_map} +========================================== + +Each interface module and mapping module has unique configuration values. +Details of these configuration values can be found in the reference pages for +each module. + +- Reference: AVTP Mapping Modules + - [1722 AAF (aaf_audio)](@ref aaf_audio_map) + - [Control (ctrl)](@ref ctrl_map) + - [Motion JPEG (mjpeg)](@ref mjpeg_map) + - [MPEG2 TS (mpeg2ts)](@ref mpeg2ts_map) + - [NULL (null)](@ref null_map) + - [PIPE (pipe)](@ref pipe_map) + - [61883-6 (uncmp_audio)](@ref uncmp_audio_map) +- Reference: AVTP Interface Module + - [Control (ctrl)](@ref ctrl_intf) + - [Echo (echo)](@ref echo_host_intf) + - [Null (null)](@ref null_host_intf) + - [Viewer (viewer)](@ref viewer_intf) +- Reference: AVTP Interface Module Linux Specific + - [ALSA (alsa)](@ref alsa_intf) + - [MJPEG Camera (mjpeg_camera)](@ref mjpeg_camera_intf) + - [MJPEG GST (mjpeg_gstreamer)](@ref mjpeg_gst_intf) + - [MPEG2 TS File (mpeg2ts_file)](@ref mpeg2ts_file_intf) + - [MPEG2 TS GST (mpeg2ts_gstreamer)](@ref mpeg2ts_gst_intf) + - [WAV File (wav_file)](@ref wav_file_intf) + + diff --git a/lib/avtp_pipeline/documents/sdk_eavb_integration.md b/lib/avtp_pipeline/documents/sdk_eavb_integration.md new file mode 100644 index 00000000..c1200834 --- /dev/null +++ b/lib/avtp_pipeline/documents/sdk_eavb_integration.md @@ -0,0 +1,31 @@ +EAVB Integration Guide {#sdk_integration} +====================== + +Introduction {#sdk_integration_introduction} +============ +Integrating the OPENAVB AVB stack into a larger solution can be conceptually divided +into 2 parts. The first is the configuration and start up of the AVB components +such as gPTP and AVTP. The second part is the configuration of the streams that +will be active. The details of stream configuration is described in another +section. + +<br> +AVTP Control {#sdk_integration_avtp_control} +============ +Only a handful of functions are needed to control the AVTP component. This is +done indirectly when opening, running and closing talkers and listeners. The +general flow is: +1. openavbTLInitialize() is called to initialize the openavb_tl module. +2. openavbTLOpen() is called one or more times to load talkers and listeners. +3. openavbTLConfigure() is called for each talker or listener. +4. openavbTLRun() is called for each talker and listener to start their stream. +5. openavbTLClose() is called for each talker and listener to stop the stream and + close. + +<br> +Host Application Integration {#sdk_integration_host_app} +============================ +Controlling the non-AVTP components of the AVB stack are platform dependent. +Release notes for the specific port should be referenced for those details. + + diff --git a/lib/avtp_pipeline/documents/sdk_notes.md b/lib/avtp_pipeline/documents/sdk_notes.md new file mode 100644 index 00000000..f1c9a300 --- /dev/null +++ b/lib/avtp_pipeline/documents/sdk_notes.md @@ -0,0 +1,7 @@ +Developer Notes {#sdk_notes} +=============== + +These are additional topics from the trenches but the details have not yet been incorporated into the formal SDK documentation sections. + +- [Media Queue Usage](@ref sdk_notes_media_queue_usage) + diff --git a/lib/avtp_pipeline/documents/sdk_notes_media_queue_usage.md b/lib/avtp_pipeline/documents/sdk_notes_media_queue_usage.md new file mode 100644 index 00000000..aaa4cda8 --- /dev/null +++ b/lib/avtp_pipeline/documents/sdk_notes_media_queue_usage.md @@ -0,0 +1,124 @@ +Media Queue Usage {#sdk_notes_media_queue_usage} +================= + +# Description + +This section presents the work flow required for working with the Media Queue. +Please note that most of the described steps are performed inside the AVB stack +and are hidden from interface module implemented. There are also some +simplifications to make description as straightforward as possible. + +<br> +# Workflow + +The following work flow steps will be described. +* [Starting](@ref media_queue_usage_start) - Initialization common for talker +and listener streams +* [Talker specific](@ref media_queue_usage_talker) - Talker specific usage +* [Listener specific](@ref media_queue_usage_listener) - Listener specific usage +* [Stopping](@ref media_queue_usage_stop) - Stopping procedure common for talker +and listener streams +* [Rules](@ref media_queue_usage_rules) - general rules to follow while using +the Media Queue + +<br> +Starting {#media_queue_usage_start} +======== + +First the Media Queue has to be created and initialized with correct data. Below +are the function calls needed for proper creation and initialization. Note: +these calls are initiated from the AVTP module. + +* @ref openavbMediaQCreate - data is allocated, queue is initialized, internal data +structures are prepared +* @ref openavbMediaQSetMaxStaleTail - sets maximum stale in microseconds before data +is being purged +* The Interface module initialization function for this media queue is called +with media queue as a parameter, those functions + * Set internal interface module parameters + * Initialize Media Queue + * Creates Media Queue Interface Module Private Data + * Fills the private data structure with information needed by interface + module +* Stream configuration values are processed. This actually configuration data +may come from .ini files or internally set within the AVB host application. +* Mapping module init function openavb_map_cb_t::map_gen_init_cb is being called +during which several steps (media queue parameters **have to** be set) + * Media Queue Items count is set + * Media Queue Items are allocated +* Interface module init function openavb_intf_cb_t::intf_gen_init_cb function is +called + +Now the listener/talker stream is running. See next steps below for details of +Media Queue interaction for the talker and listener. + +<br> +Talker specific flow {#media_queue_usage_talker} +==================== + +As a talker, an interface module is responsible for writing data to Media Queue, +so important steps are: + +* @ref openavbMediaQHeadLock function for getting the head of the media queue and +marking it as locked +* @ref openavbMediaQHeadUnlock function which releases head. Any subsequent calls to +@ref openavbMediaQHeadLock will return the same MediaQueueItem +* @ref openavbMediaQHeadPush function unlocks head previously taken by the Lock +function and informs that work on the current Item is finished so that next call +to @ref openavbMediaQHeadLock will return next Media Queue Item. Call of this +function makes the item accessible via the @ref openavbMediaQTailLock function. This +function additionally unlocks the head so the @ref openavbMediaQHeadUnlock call is +not needed + +<br> +Listener specific flow {#media_queue_usage_listener} +====================== + +As a listener, an interface module works on Media Queue tail elements. Data in +those items is being written by the mapping module. These are the key Media +Queue functions for the listener functionality in an interface module: + +* @ref openavbMediaQTailLock gets an item from the tail and allows working on +the current tail item of the Media Queue +* @ref openavbMediaQTailUnlock unlocks the tail element in MediaQueue which means +that interface module has stopped working on it for now but processing of +current tail element will be continued later +* @ref openavbMediaQTailPull unlocks the tail element and removes it from the Media +Queue. This means that the interface module has finished processing of current +tail element and it can be rewritten again by new data. This function +additionally unlocks tail, so it is not necessary to call @ref +openavbMediaQTailUnlock. + +<br> +Stopping {#media_queue_usage_stop} +======== + +During the stopping process following action are taken + +* openavb_intf_cb_t::intf_gen_end_cb is called +* openavb_map_cb_t::map_gen_end_cb is called +* Media Queue is deleted using the @ref openavbMediaQDelete function which frees +the memory taken by all internal structures of Media Queue + +<br> +Guidelines {#media_queue_usage_rules} +========== + +* Calls to **Lock** functions must always be paired with their **Unlock** +counterparts to avoid problems while working with MediaQueue. Push and Pull +functions additional result in an unlock. +* The current implementation Media Queue allows accessing two elements at the +same time - one is head and second the tail +* The number of Media Queue items and their sizes depends on configuration for +the stream. The main driving factor is to make the data accessible for the next +element that follows interface module. The size of the Media Queue item is a +factor of the According to this Media Queue number of size of AVTP stream +encapsulation payload multiplied by the packing factor. The number of Media +Queue items required if different for a talker and listener. The talker usually +needs minimal Media Queue items. However, the listener must have enough Media +Queue items to buffer data until the AVTP presentation time. This is based on +the maximum transit time for the SR Class in use. ring decoding +* If there are not enough Media Queue items warnings will be logged to the +implemented logging system for the port for debugging purposes. + +More implementation details might be find in @ref openavb_mediaq_pub.h diff --git a/lib/avtp_pipeline/documents/sdk_overview.md b/lib/avtp_pipeline/documents/sdk_overview.md new file mode 100644 index 00000000..75d6f926 --- /dev/null +++ b/lib/avtp_pipeline/documents/sdk_overview.md @@ -0,0 +1,233 @@ +EAVB SDK Overview {#sdk_overview} +================= + + +Introduction {#sdk_overview_introduction} +============ +Symphony Teleca Corporation (OPENAVB) has developed a software Protocol Stack to +support Ethernet Audio Video Bridging (EAVB or AVB) on a variety of platforms. +The software includes explicit Operating System and Hardware Abstraction Layers +(OSAL and HAL) to facilitate rapid and efficient porting of the generic stack to +specific platforms. The complete OPENAVB EAVB implementation, and its role within an +overall EAVB system are discussed in OPENAVB?s Ethernet Audio Video Bridging (AVB) +High Level Specification (OPENAVB13-01059). + +The OPENAVB AVB stack is designed to work on both General Purpose OSes (GPOS), such +as Linux, as well as Real-Time OSes (RTOS). There are some difference in +concepts and terms between a GPOS and RTOS, for example threads vs tasks, or the +concept of multiple processes. For the purposes of the SDK guides the terms +thread and task are used interchangeably. + +The guides in this SDK focus on integrating the OPENAVB AVB stack in an application, +developing interface module components and configuration AVB streams. These +guides are separated into four sections: + +- [EAVB SDK Overview](@ref sdk_overview) +- [EAVB Integration](@ref sdk_integration) +- [AVTP Interface Module Development](@ref sdk_avtp_interface_module_dev) +- [AVTP Stream Configuration](@ref sdk_avtp_stream_cfg) + +This overview section will cover general architecture. + +<br> +Glossary {#sdk_overview_glossary} +======== +**AAF:** AVTP Audio Format + +**AVB:** Audio Video Bridging (used interchangeably with EAVB) + +**AVB Stack:** The primary functionality that implements the various AVB +protocols + +**AVTP:** Audio Video Transport Protocol + +**EAVB:** Ethernet Audio Video Bridging (used interchangeably with AVB) + +**EMAC:** Ethernet Media Access Control + +**FQTSS:** Forwarding and Queuing enhancements for Time Sensitive Streams + +**GPOS:** General purpose OS + +**gPTP:** generalized Precision Time Protocol + +**HAL:** Hardware Abstraction Layer + +**Listener:** Receives an AVTP stream, unpacks it and pushes it to a media sink +for playback. It consists of a dedicated task and uses functionality in the AVTP +module, one mapping module and one interface module. + +**IEEE:** Institute of Electrical and Electronics Engineers + +**Interface Module:** An AVTP component that when called from the talker pulls +data from a media source and pushes it onto the media queue in the format +expected by the mapping module running on the talker. When called from a +listener it pulls data from the media queue and pushes it to the media sink for +playback. + +**ISR:** Interrupt Service Routine + +**MAAP:** Multicast Address Allocation Protocol + +**MAC:** Media Access Control + +**Mapping Module:** An AVTP component that when called from the talker pulls +data from the media queue and packages it into the AVTP data payload according +to a specific AVB encapsulation. When called from a listener it unpacks the AVTP +data payload according to the specific AVB encapsulation and pushes it on to the +media queue. + +**Media Queue:** A circular FIFO container used to opaquely pass media data +blocks between interface modules and mapping modules. This is the only way +interface modules and mapping modules communicate. + +**MJPEG:** Motion Joint Photographic Expert Group + +**MPEG2-TS:** Motion Picture Expert Group (version 2) Transport Stream + +**OS:** Operating System + +**OSAL:** Operating System Abstraction Layer + +**SDK:** Software Development Kit + +**SR:** Stream Reservation + +**RTOS:** Real-time OS + +**SRP:** Stream Reservation Protocol + +**OPENAVB:** Symphony Teleca Corporation + +**Stream:** A series of AVTP data packets + +**Talker:** Takes an audio or video source and transmits it as an AVTP stream on +the AVB network. It consists of a dedicated task and uses functionality in the +AVTP module, one mapping module and one interface module. + +<br> +Architecture {#sdk_overview_architecture} +============ + +The complete OPENAVB EAVB implementation and its role within an overall EAVB system +are discussed in OPENAVB?s Ethernet Audio Video Bridging (AVB) High Level +Specification (OPENAVB13-01059). + +What follows in this section are details that more directly effect the use and +understanding of the SDK. + +<br> +Below is the component diagram of the Core AVB stack. Notice that the interface +modules are not shown as part of the formal core stack but instead the +interfaces that they use in the core stack are shown. This is also true for the +interfaces the host application will use (TL APIs). + +@image html Core_AVB.png "Core AVB" +@image latex Core_AVB.png "Core AVB" width=15cm + +<br> +General Purpose OSes will generally have the core AVB stack split across +multiple processes. Whereas in an RTOS everything sits within a single execution +image. Here is a process diagram of the components split across processes in the +Linux reference implementation. + +@image html fig1.png "AVB Components" +@image latex fig1.png "AVB Components" width=15cm + +As shown above the AVTP component of AVB is present in the application task. +The libopenavbAVBStack library gets initialized and loaded via the APIs exposed and +documented here. This static library implements that AVTP functionality as well +as controlling talker and listener initialization and life cycle. The PTP task +initialization is handled during stack initialization. + +<br> +The common AVTP stream data flow is shown here. + +@image html AVTP_Data_Flow.png "AVTP Data Flow" +@image latex AVTP_Data_Flow.png "AVPT Data Flow" width=15cm + +<br> +The diagram below shows an audio stream flowing through the AVB system on the +Linux platform. Typically the talker and listener will be in different tasks and +may use different interfaces. + +@image html fig2.png "AVB Audio Stream Flow" +@image latex fig2.png "AVB Audio Stream Flow" width=15cm + +<br> +Understanding the stream life-cycle is important for use of this SDK. Below are +sequence diagrams that show the component interaction for key stream use cases. + +@image html Stream_Initialize.png "Stream Initialization" +@image latex Stream_Initialize.png "Stream Initialization" width=15cm + +<br> +@image html Talker_Stream_Data_Flow.png "Talker Stream Data Flow" +@image latex Talker_Stream_Data_Flow.png "Talker Stream Data Flow" width=15cm + +<br> +@image html Listener_Stream_Data_Flow.png "Listener Stream Data Flow" +@image latex Listener_Stream_Data_Flow.png "Listener Stream Data Flow" width=15cm + +<br> +Integration, Extension and Configuration {#sdk_overview_integration} +======================================== +Three distinct uses of the SDK are as follows. + +**Integration:** This refers to the integration of the OPENAVB AVB stack into an +existing or new application. This is also sometimes referred to as the hosting +application. For details related to this see [EAVB Integration](@ref +sdk_integration) + +**AVTP Interface Module Development:** In order for the AVB stack to be used it +must have interaction with media data sources and sinks on the platform. This is +accomplished with Interface modules. The AVB stack ported to a platform may +include some interface modules already but typically new interface modules are +needed for the specific hardware and drivers being targeted. See [AVTP Interface +Module Development](@ref sdk_avtp_interface_module_dev) for more details. + +**Configuration:** Configuration can be split into 2 distinct area. Hosting +application configuration and stream configuration. Hosting application +configuration is port specifici and therefore not covered in detail in this SDK. +However, in the configuration guide section of the SDK some host application +configuration example are provided as a point of reference. The other primary +area of configuration is related to stream. See the section [AVTP Stream +Configuration](@ref sdk_avtp_stream_cfg) for more details. + +<br> +Platform Specific Considerations {#sdk_overview_platform} +================================ +The OPENAVB AVB stack is designed for portability covering both general purpose OSes +and real-time OSes. The base public APIs of the SDK do not change depending on +the port. However, some elements of working with the SDK do change depending on +the port of the OPENAVB stack. See the release notes for the specific port for +details that may be beyond the common SDK usage. + +## Threads / Tasks +Within the SDK guides the terms threads and tasks are used interchangeability +and for the purposes of the SDK they can be considered the same. + +## Processes +Some platform OSes support multiple processes. The OPENAVB stack for a particular +port may make use of multiple processes. For example in the Linux reference +implementation gPTP resided in a separate process. Additionally in this +reference implementation the talk / listener end-station functionality for each +stream can be in a single process or split across multiple processes. Whereas in +a RTOS there isn't typically the concept of multile processes. + +## Configuration Overview +Configuration of both the host application as well as the streams can vary based +on platform capabilities. For example on configuration information may come from +.ini files on the device and in other platforms there may not be a file system +in which case configuration may be set at build time. + +## Startup +See the port specific Release Notes for the details on AVB start up. + +## Libraries +The AVB core stack commonly is build as a static library and linked with a +hosting application. Interface modules may be built differently depending on the +platform. In some cases they my be dynamic libraries and other cases they may be +static libraries. It is also possible that the interface modules will be built +with the host application or as part of the AVB core library. + diff --git a/lib/avtp_pipeline/endpoint/CMakeLists.txt b/lib/avtp_pipeline/endpoint/CMakeLists.txt new file mode 100644 index 00000000..24b6d058 --- /dev/null +++ b/lib/avtp_pipeline/endpoint/CMakeLists.txt @@ -0,0 +1,39 @@ +include_directories( + ${AVB_OSAL_DIR}/endpoint + ${AVB_OSAL_DIR}/maap + ${AVB_OSAL_DIR}/tl + ${AVB_SRC_DIR}/maap + ${AVB_SRC_DIR}/endpoint + ${AVB_SRC_DIR}/srp + ${AVB_SRC_DIR}/tl + ${AVB_OSAL_DIR}/fqtss/qmgr + ${AVB_OSAL_DIR}/srp + ) + +# File sets are curreintly the same for AVB_FEATURE_ENDPOINT or not. Needs code changes to properly allow linux to run with and without SRP/MAPP +if (AVB_FEATURE_ENDPOINT) + SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/endpoint/openavb_endpoint.c + ${AVB_OSAL_DIR}/endpoint/openavb_endpoint_osal.c + ${AVB_SRC_DIR}/endpoint/openavb_endpoint_server.c + ${AVB_OSAL_DIR}/endpoint/openavb_endpoint_cfg.c + PARENT_SCOPE + ) +else(AVB_FEATURE_ENDPOINT) + SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/endpoint/openavb_endpoint.c + ${AVB_OSAL_DIR}/endpoint/openavb_endpoint_osal.c + ${AVB_SRC_DIR}/endpoint/openavb_endpoint_server.c + ${AVB_OSAL_DIR}/endpoint/openavb_endpoint_cfg.c + PARENT_SCOPE + ) +endif (AVB_FEATURE_ENDPOINT) + +# If FQTSS is enabled... +if ( AVB_FEATURE_FQTSS ) + # set define for ifdefs + set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAVB_FEATURE_FQTSS=1" ) + # and add library to link list + set ( AVB_ENDPOINT_LIBRARIES "${AVB_ENDPOINT_LIBRARIES}" qmgr ) +endif ( AVB_FEATURE_FQTSS ) + diff --git a/lib/avtp_pipeline/endpoint/NOTES.TXT b/lib/avtp_pipeline/endpoint/NOTES.TXT new file mode 100644 index 00000000..a4b912da --- /dev/null +++ b/lib/avtp_pipeline/endpoint/NOTES.TXT @@ -0,0 +1,11 @@ +The endpoint process hosts the per-node functionality that's used by +all the talkers/listeners on the node. This includesthe SRP and FQTSS +logic, which is provided by the SRP and FQTSS libraries. + +The actual AVTP talkers/listeners are implemented as seperate +processes. Those processes communicate with the endpoint through IPC +(currently we're using a local socket on Linux.) The IPC +communication is handled by openavb_resv_server.c (server-side IPC, +linked into the endpoint process) and openavb_resv_client.c +(client-side of the IPC, linked into the talker and listener +processes.) diff --git a/lib/avtp_pipeline/endpoint/endpoint.ini b/lib/avtp_pipeline/endpoint/endpoint.ini new file mode 100644 index 00000000..5138c661 --- /dev/null +++ b/lib/avtp_pipeline/endpoint/endpoint.ini @@ -0,0 +1,59 @@ +[network] + +# interface name on which to talk/listen +ifname=eth0 + +# information rate (wire speed) for the link specified in kilobits/sec +# (fast ethernet = 100000, gigabit ethernet = 1000000) +link_kbit = 1000000 + +# Bandwidth reserved for non-SR traffic +# specified in kilobits/sec +nsr_kbit = 250000 + +[ptp] + +# Endpoint will always start openavb_gptp on the interface specified by ifname (above) +# (unless gptp_asCapable_not_required = 1 is set - see warning below) +# Additional openavb_gptp command line options may be provided here, if desired +start_options = -u 60 + +[fqtss] + +# FQTSS mode - how talker traffic will be shaped. +# +# Modes are: +# 0 = AVB_SHAPER_DISABLED - Disable FQTSS (no shaping) +# 1 = AVB_SHAPER_DROP_ALL - Drop all frames +# 2 = AVB_SHAPER_ALL_NONSR - Treat everything as non-SR traffic +# 3 = AVB_SHAPER_DROP_SR - Drop all AVTP frames +# 4 = AVB_SHAPER_SW - Credit-based shaping per-class (in software) +# 5 = AVB_SHAPER_HWQ_PER_CLASS - shaping in HW per AVB class +# +# By default on platforms which support HW shaping, the default mode +# will be AVB_SHAPER_HWQ_PER_CLASS, on other platforms the default +# mode is AVB_SHAPER_SW. +#mode = 4 + +[srp] + +# To disable dynamic SRP operation in the case of manually preconfigured streams +# (e.g. in an AVB network using Broadcom Polar Switches) set: +#preconfigured = 1 +# CAUTION: All streams registered by talker or listener are assumed to be valid. +# WARNING: Preconfigured streams require COMPLETE manual stream configuration +# on EVERY device in the network, without exception. +# ANY NETWORK WHICH IS NOT EITHER +# - USING DYNAMIC SRP ON **EVERY** NETWORK DEVICE, OR +# - HAS COMPLETE MANUAL STREAM CONFIGURATION ON **EVERY** NETWORK DEVICE +# IS NOT AN ETHERNET AVB NETWORK AND WILL NOT FUNCTION PROPERLY. + +# IEEE 802.1ba section 6.4 requires that SRP grant stream reservations on a link +# only if that link is "asCapable" with gPTP (IEEE 802.1AS) operating. To bypass +# that requirement, set: +# gptp_asCapable_not_required = 1 +# WARNING: This non-standards complaint option is intended ONLY to allow for the +# use of PTPv2 on older AVB netowrks where gPTP may not be supported. +# ANY NETWORK WHICH IS NOT USING PTP TO KEEP ALL DEVICE CLOCKS IN SYNC +# IS NOT AN ETHERNET AVB NETWORK AND WILL NOT FUNCTION PROPERLY. + diff --git a/lib/avtp_pipeline/endpoint/gstreamer.txt b/lib/avtp_pipeline/endpoint/gstreamer.txt new file mode 100644 index 00000000..14f164df --- /dev/null +++ b/lib/avtp_pipeline/endpoint/gstreamer.txt @@ -0,0 +1,98 @@ +Notes on creating GSgtreamer audio/video pipelines, and the +talker/listener.ini entries for those streams. + +First, need figure out the GStreamer pipelines using gst-inspect and +gst-launch. gst-inspect shows info about the plugins +(src/sink/filter). Gst-launch creates a pipeline in the same way that +our talker/listener (openavb-gst-talk and openavb-gst-listen) do - but all the +parts obviously run on the same node. + +To use a pipeline with AVTP, we need a capability filter (a format +specification) in the middle of the pipeline. This is where we'll +break the pipe between talker and listener. The caps filter specifies +the format of the gstreamer data samples that are sent across the +network using AVTP. + +Start with a simple "gst-launch src ! sink" pipeline, and use +the verbose option (-v) to make gst-launch show the caps negotiated +between the source and sink. + +Next, use filters to transform the stream to/from the format that you +want to transport over AVTP. + +Audio example: + +For audio, "aplay -l" and "arecord -l" list the ALSA sinks and +sources. On my boards, device 1 was the USB microphone, and device +0 was the HDMI audio output. + +Next I ran this to verify that the pipeline works: + + gst-launch -v alsasrc device=hw:1 ! audio/x-raw-int,rate=48000 ! audioconvert ! alsasink device=hw:0 + +The audioconvert filter was needed because the microphone is mono, +and HDMI is stereo. Without that, gst-launch complains that the +"capabilities couldn't be negotiated". + +The "audio/x-raw-int,rate-48000" was needed to get alsasrc to use the +USB audio. Without that, gstreamer complained of an internal error. (Ugh!) + +When I ran the simple pipeline, it reported: + + $ gst-launch -v alsasrc device=hw:1 ! audio/x-raw-int,rate=48000 ! audioconvert ! alsasink device=hw:0 + Setting pipeline to PAUSED ... + /GstPipeline:pipeline0/GstAlsaSrc:alsasrc0: actual-buffer-time = 200000 + /GstPipeline:pipeline0/GstAlsaSrc:alsasrc0: actual-latency-time = 10000 + /GstPipeline:pipeline0/GstAlsaSrc:alsasrc0.GstPad:src: caps = audio/x-raw-int, width=(int)16, depth=(int)16, rate=(int)44100, channels=(int)1, endianness=(int)1234, signed=(boolean)true + Pipeline is live and does not need PREROLL ... + Setting pipeline to PLAYING ... + New clock: GstAudioSrcClock + /GstPipeline:pipeline0/GstAudioConvert:audioconvert0.GstPad:src: caps = audio/x-raw-int, endianness=(int)1234, signed=(boolean)true, width=(int)16, depth=(int)16, rate=(int)48000, channels=(int)2 + /GstPipeline:pipeline0/GstAudioConvert:audioconvert0.GstPad:sink: caps = audio/x-raw-int, width=(int)16, depth=(int)16, rate=(int)48000, channels=(int)1, endianness=(int)1234, signed=(boolean)true + /GstPipeline:pipeline0/GstAlsaSink:alsasink0.GstPad:sink: caps = audio/x-raw-int, endianness=(int)1234, signed=(boolean)true, width=(int)16, depth=(int)16, rate=(int)48000, channels=(int)2 + +The caps lines show the format of the data at each plugin in the +pipeline. I wanted a lower sample rate to send across the network, +and since the microphone is mono, it makes sense to only send 1 sound +channel. + +That gives me the desired caps for the middle of the pipeline (where +we'll split it.) I moved the audioconvert (which does the mono -> +stereo conversion) after the split point. + +gst-launch -v alsasrc device=hw:1 + ! audio/x-raw-int,rate=48000 + ! audioresample + ! audio/x-raw-int,width=16,depth=16,rate=44100,channels=1,endianness=1234,signed=true + ! audioconvert + ! alsasink device=hw:0 + +I then retested, and verified that the longer pipeline still works. + +Next, we split the pipeline, and add our AVTP appsrc and appsink: + +For the talker, this gives us: + +alsasrc device=hw:1 + ! audio/x-raw-int,rate=48000 + ! audioresample + ! audio/x-raw-int,width=16,depth=16,rate=44100,channels=1,endianness=1234,signed=true + ! appsink name=avbsink + +And for the listener: + +appsrc name=avbsrc + ! audio/x-raw-int,width=16,depth=16,rate=44100,channels=1,endianness=1234,signed=true + ! audioconvert + ! alsasink device=hw:0 + +Those lines become the values for "pipeline" in talker.ini and +listner.ini. + +The final step is to find the tx_size and tx_samples for talker.ini. +GStreamer decides how and when to deliver data to our appsink, so we +need to run an experiment. Add the pipelines to the +talker/listener.ini files, and run the talker and listener. Watch for +the "INFO: TX Sample=N" log lines. That line shows tx_size, which +goes directly into talker.ini, and tx_samples, which is used to +calculate tx_rate. (tx_rate = pipeline rate / tx_samples). diff --git a/lib/avtp_pipeline/endpoint/openavb_endpoint.c b/lib/avtp_pipeline/endpoint/openavb_endpoint.c new file mode 100644 index 00000000..f4059c92 --- /dev/null +++ b/lib/avtp_pipeline/endpoint/openavb_endpoint.c @@ -0,0 +1,528 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : The AVB endpoint is the "master process". +* It includes the SRP and QMgr libraries which handle SRP reservations +* and TX queuing. +* +* The actual AVTP talker/listener work is done in a separate processs. +* The aim of using separate processes is to (1) reduce the +* complexity in the central process; and (2) to allow multiple types +* of children for different AVTP encapsulations and data sources. +* +* Streamer processes contact the endpoint process through an IPC to +* declare their streams. Currently, the IPC uses unix sockets. The +* IPC methods are implemented in openavb_endpoint_client.c and +* openavb_endpoint_server.c. The streamers (talkers and listeners) +* are referred to as our"clients", and the endpoint process is their +* "server". +* +* When SRP establishes a reservation (or when a reservation goes +* away), the endpoint communicates the event back to the streamer +* process through a callback (which also uses the IPC mechanism.) +*/ + +#include <stdlib.h> +#include <string.h> +#include "openavb_platform.h" +#include "openavb_trace.h" +#include "openavb_endpoint.h" +#include "openavb_endpoint_cfg.h" +#include "openavb_avtp.h" +#include "openavb_qmgr.h" +#include "openavb_maap.h" + +#define AVB_LOG_COMPONENT "Endpoint" +#include "openavb_pub.h" +#include "openavb_log.h" + +/* Information that we need to remember about each stream + */ + + +// list of streams that we're managing +clientStream_t* x_streamList; +// true until we are signalled to stop +bool endpointRunning = TRUE; +// data from our configuation file +openavb_endpoint_cfg_t x_cfg; + +/************************************************************* + * Functions to manage our list of streams. + */ + +/* Log information on all statically configured streams. + * (Dynamically configured streams are logged by SRP.) +*/ +void openavbEndPtLogAllStaticStreams(void) +{ + bool hdrDone = FALSE; + + if(x_cfg.noSrp) { + clientStream_t **lpp; + for(lpp = &x_streamList; *lpp != NULL; lpp = &(*lpp)->next) { + if ((*lpp)->clientHandle != AVB_ENDPOINT_HANDLE_INVALID) { + if (!hdrDone) { + AVB_LOG_INFO("Statically Configured Streams:"); + AVB_LOG_INFO(" | SR | Destination | ----Max Frame(s)--- |"); + AVB_LOG_INFO(" Role | Stream Id | Class | Address | Size | Per Interval |"); + hdrDone = TRUE; + } + if ((*lpp)->role == clientTalker) { + AVB_LOGF_INFO(" Talker | %02x:%02x:%02x:%02x:%02x:%02x - %4d | %c | %02x:%02x:%02x:%02x:%02x:%02x | %4u | %2u |", + (*lpp)->streamID.addr[0], (*lpp)->streamID.addr[1], (*lpp)->streamID.addr[2], + (*lpp)->streamID.addr[3], (*lpp)->streamID.addr[4], (*lpp)->streamID.addr[5], + (*lpp)->streamID.uniqueID, + AVB_CLASS_LABEL((*lpp)->srClass), + (*lpp)->destAddr[0], (*lpp)->destAddr[1], (*lpp)->destAddr[2], + (*lpp)->destAddr[3], (*lpp)->destAddr[4], (*lpp)->destAddr[5], + (*lpp)->tSpec.maxFrameSize, (*lpp)->tSpec.maxIntervalFrames ); + } else if ((*lpp)->role == clientListener) { + AVB_LOGF_INFO(" Listener | %02x:%02x:%02x:%02x:%02x:%02x - %4d | - | --:--:--:--:--:-- | -- | -- |", + (*lpp)->streamID.addr[0], (*lpp)->streamID.addr[1], (*lpp)->streamID.addr[2], + (*lpp)->streamID.addr[3], (*lpp)->streamID.addr[4], (*lpp)->streamID.addr[5], + (*lpp)->streamID.uniqueID ); + + } + } + } + } +} + +/* Called for each talker or listener stream declared by clients + */ +clientStream_t* addStream(int h, AVBStreamID_t *streamID) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + clientStream_t *newClientStream = NULL, **lpp; + + do { + newClientStream = (clientStream_t *)calloc(1, sizeof(clientStream_t)); + if(newClientStream == NULL) { + AVB_LOG_ERROR("addStream: Failed to malloc stream"); + break; + } + + memcpy(newClientStream->streamID.addr, streamID->addr, ETH_ALEN); + newClientStream->streamID.uniqueID = streamID->uniqueID; + newClientStream->clientHandle = h; + newClientStream->fwmark = INVALID_FWMARK; + + if(x_streamList == NULL) { + x_streamList = newClientStream; + }else { + // insert at end + for(lpp = &x_streamList; *lpp != NULL; lpp = &(*lpp)->next) { + if ((*lpp)->next == NULL) { + (*lpp)->next = newClientStream; + break; + } + } + } + } while (0); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return newClientStream; +} + +void delStream(clientStream_t* ps) +{ + clientStream_t **lpp; + for(lpp = &x_streamList; *lpp != NULL; lpp = &(*lpp)->next) { + if((*lpp) == ps) { + *lpp = (*lpp)->next; + free(ps); + break; + } + } +} + +/* Find a stream in the list of streams we're handling + */ +clientStream_t* findStream(AVBStreamID_t *streamID) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + clientStream_t* ps = NULL; + + // Check for default stream MAC address, and fill it in with + // interface MAC so that if the talker didn't fill in + // the stream MAC, we use the one that the endpoint is + // configured to use. + // + // Listener should never pass a default MAC in, + // because the config code forces the listener to specify MAC in + // its configuration. Talker may send a default (empty) MAC in + // the stream ID, in which case we fill it in. + // + // TODO: This is sketchy - it would probably be better to force every + // client to send fully populated stream IDs. I think the reason + // I didn't do that is that it would cause duplicate configuration + // (in the talker and in the endpoint.) Perhaps the filling in of + // the MAC could happen in the endpoint function which calls + // findstream for the talker. + // + static const U8 emptyMAC[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + if (memcmp(streamID->addr, emptyMAC, ETH_ALEN) == 0) { + memcpy(streamID->addr, x_cfg.ifmac, ETH_ALEN); + AVB_LOGF_DEBUG("Replaced default streamID MAC with interface MAC "ETH_FORMAT, ETH_OCTETS(streamID->addr)); + } + + clientStream_t **lpp; + for(lpp = &x_streamList; *lpp != NULL; lpp = &(*lpp)->next) { + if (memcmp(streamID->addr, (*lpp)->streamID.addr, ETH_ALEN) == 0 + && streamID->uniqueID == (*lpp)->streamID.uniqueID) + { + ps = *lpp; + break; + } + } + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return ps; +} + +/* Find a stream by MAAP handle + */ +static clientStream_t* findStreamMaap(void* hndMaap) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + clientStream_t* ps = NULL; + + clientStream_t **lpp; + for(lpp = &x_streamList; *lpp != NULL; lpp = &(*lpp)->next) { + if ((*lpp)->hndMaap == hndMaap) + { + ps = *lpp; + break; + } + } + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return ps; +} + +/************************************************************* + * + * Internal function to cleanup streams + * + */ +bool x_talkerDeregister(clientStream_t *ps) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + openavbRC rc = OPENAVB_SUCCESS; + + if(!x_cfg.noSrp) { + // Pass to SRP + rc = openavbSrpDeregisterStream(&ps->streamID); + } + + // Remove QMgr entry for stream + if (ps->fwmark != INVALID_FWMARK) { + openavbQmgrRemoveStream(ps->fwmark); + ps->fwmark = INVALID_FWMARK; + } + + // Release MAAP address allocation + if (ps->hndMaap) { + openavbMaapRelease(ps->hndMaap); + ps->hndMaap = NULL; + } + + // remove record + delStream(ps); + + openavbEndPtLogAllStaticStreams(); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return IS_OPENAVB_SUCCESS(rc); +} + +bool x_listenerDetach(clientStream_t *ps) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + openavbRC rc = OPENAVB_SUCCESS; + + if(!x_cfg.noSrp) { + // Pass to SRP + rc = openavbSrpDetachStream(&ps->streamID); + } + + // remove record + delStream(ps); + + openavbEndPtLogAllStaticStreams(); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return IS_OPENAVB_SUCCESS(rc); +} + + +/************************************************* + * SRP CALLBACKS + */ + +/* SRP tells us about a listener peer (Listener Ready or Failed) + */ +openavbRC strmAttachCb(void* pv, + openavbSrpLsnrDeclSubtype_t lsnrDecl) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + openavbRC rc = OPENAVB_FAILURE; + clientStream_t *ps = (clientStream_t*)pv; + + AVB_LOGF_INFO("SRP talker callback uid=%d: lsnrDecl=%x", ps->streamID.uniqueID, lsnrDecl); + + if (lsnrDecl == openavbSrp_LDSt_Ready + || lsnrDecl == openavbSrp_LDSt_Ready_Failed) + { + // Somebody is listening - get ready to stream + + if (ps->fwmark != INVALID_FWMARK) { + AVB_LOG_DEBUG("attach callback: already setup queues"); + rc = OPENAVB_SUCCESS; + } + else { + AVB_LOG_DEBUG("Attach callback: setting up queues for streaming"); + + rc = openavbSrpGetClassParams(ps->srClass, &ps->priority, &ps->vlanID, &ps->classRate); + if (IS_OPENAVB_SUCCESS(rc)) { + ps->fwmark = openavbQmgrAddStream(ps->srClass, ps->classRate, ps->tSpec.maxIntervalFrames, ps->tSpec.maxFrameSize); + if (ps->fwmark == INVALID_FWMARK) { + AVB_LOG_ERROR("Error in attach callback: unable to setup stream queues"); + rc = OPENAVB_FAILURE; + } + else { + rc = OPENAVB_SUCCESS; + } + } + else { + AVB_LOG_ERROR("Error in attach callback: unable to get class params"); + rc = OPENAVB_FAILURE; + } + } + } + else { + // Nobody listening + if (ps->fwmark != INVALID_FWMARK) { + AVB_LOG_DEBUG("Attach callback: tearing down queues"); + openavbQmgrRemoveStream(ps->fwmark); + ps->fwmark = INVALID_FWMARK; + } + rc = OPENAVB_SUCCESS; + } + + if (IS_OPENAVB_SUCCESS(rc)) { + + openavbEptSrvrNotifyTlkrOfSrpCb(ps->clientHandle, + &ps->streamID, + x_cfg.ifname, + ps->destAddr, + lsnrDecl, + ps->classRate, + ps->vlanID, + ps->priority, + ps->fwmark); + rc = OPENAVB_SUCCESS; + } + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return rc; +} + +/* SRP tells us about talker peer (Talker Registration or De-registration) + */ +openavbRC strmRegCb(void *pv, + openavbSrpAttribType_t tlkrDecl, + U8 destAddr[], + AVBTSpec_t *tSpec, + SRClassIdx_t srClass, + U32 accumLatency, + openavbSrpFailInfo_t *failInfo) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + clientStream_t *ps = (clientStream_t*)pv; + AVB_LOGF_INFO("SRP listener callback uid=%d: tlkrDecl=%x", ps->streamID.uniqueID, tlkrDecl); + + openavbEptSrvrNotifyLstnrOfSrpCb(ps->clientHandle, + &ps->streamID, + x_cfg.ifname, + destAddr, + tlkrDecl, + tSpec, + srClass, + accumLatency, + failInfo); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return OPENAVB_SUCCESS; +} + + +/************************************************************* + * MAAP Restart - our destination MAC has been changed. + * + * This is a clunky way to handle it - but it works, and this code + * won't be called often. (MAAP sends probes before settling on an + * address, so only a buggy or malicious peer should send us down this + * path.) + * + * A better way to handle this would require SRP and the + * talkers/listeners to look at destination addresses (in addition + * to StreamID and talker/listner declaration) and explicitly handle + * destination address changes. + */ +static void maapRestartCallback(void* handle, struct ether_addr *addr) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + static clientStream_t* ps; + openavbRC rc; + + ps = findStreamMaap(handle); + AVB_LOGF_STATUS("MAAP restart callback: handle=%p, stream=%p", handle, ps); + + if (ps) { + memcpy(ps->destAddr, addr->ether_addr_octet, ETH_ALEN); + + // Pretend that our listeners went away + strmAttachCb(ps, (openavbSrpLsnrDeclSubtype_t)0); + + if(!x_cfg.noSrp) { + // Remove the old registration with SRP + openavbSrpDeregisterStream(&ps->streamID); + + // Re-register with the new address + rc = openavbSrpRegisterStream((void*)ps, + &ps->streamID, + ps->destAddr, + &ps->tSpec, + ps->srClass, + ps->srRank, + ps->latency); + } else { + rc = OPENAVB_SUCCESS; + } + + if (!IS_OPENAVB_SUCCESS(rc)) { + AVB_LOG_ERROR("MAAP restart: failed to re-register talker stream"); + } + } + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); +} + + +int avbEndpointLoop(void) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + int retVal = -1; + openavbRC rc = OPENAVB_SUCCESS; + do { + + if (!x_cfg.bypassAsCapableCheck && (startPTP() < 0)) { + // make sure ptp, a seperate process, starts and is using the same interface as endpoint + AVB_LOG_ERROR("PTP failed to start - Exiting"); + break; + } else if(x_cfg.bypassAsCapableCheck) { + AVB_LOG_WARNING(" "); + AVB_LOG_WARNING("Configuration 'gptp_asCapable_not_required = 1' is set."); + AVB_LOG_WARNING("This configuration bypasses the requirement for gPTP"); + AVB_LOG_WARNING("and openavb_gptp is not started automatically."); + AVB_LOG_WARNING("An appropriate ptp MUST be started seperately."); + AVB_LOG_WARNING("Any network which does not use ptp to synchronize time"); + AVB_LOG_WARNING("on each and every network device is NOT an AVB network."); + AVB_LOG_WARNING("Such a network WILL NOT FUNCTION PROPERLY."); + AVB_LOG_WARNING(" "); + } + + x_streamList = NULL; + + if (!openavbQmgrInitialize(x_cfg.fqtss_mode, x_cfg.ifindex, x_cfg.ifname, x_cfg.mtu, x_cfg.link_kbit, x_cfg.nsr_kbit)) { + AVB_LOG_ERROR("Failed to initialize QMgr"); + break; + } + + if (!openavbMaapInitialize(x_cfg.ifname, maapRestartCallback)) { + AVB_LOG_ERROR("Failed to initialize MAAP"); + openavbQmgrFinalize(); + break; + } + + if(!x_cfg.noSrp) { + // Initialize SRP + rc = openavbSrpInitialize(strmAttachCb, strmRegCb, x_cfg.ifname, x_cfg.link_kbit, x_cfg.bypassAsCapableCheck); + } else { + rc = OPENAVB_SUCCESS; + AVB_LOG_WARNING(" "); + AVB_LOG_WARNING("Configuration 'preconfigured = 1' is set."); + AVB_LOG_WARNING("SRP is disabled. Streams MUST be configured manually"); + AVB_LOG_WARNING("on each and every device in the network, without exception."); + AVB_LOG_WARNING("AN AVB NETWORK WILL NOT FUNCTION AS EXPECTED UNLESS ALL"); + AVB_LOG_WARNING("STREAMS ARE PROPERLY CONFIGURED ON ALL NETWORK DEVICES."); + AVB_LOG_WARNING(" "); + } + + if (!IS_OPENAVB_SUCCESS(rc)) { + AVB_LOG_ERROR("Failed to initialize SRP"); + openavbMaapFinalize(); + openavbQmgrFinalize(); + break; + } + + if (openavbEndpointServerOpen()) { + + while (endpointRunning) { + openavbEptSrvrService(); + } + + openavbEndpointServerClose(); + } + + if(!x_cfg.noSrp) { + // Shutdown SRP + openavbSrpShutdown(); + } + + openavbMaapFinalize(); + openavbQmgrFinalize(); + + retVal = 0; + + } while (0); + + if (!x_cfg.bypassAsCapableCheck && (stopPTP() < 0)) { + AVB_LOG_WARNING("Failed to execute PTP stop command: killall -s SIGINT openavb_gptp"); + } + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return retVal; +} diff --git a/lib/avtp_pipeline/endpoint/openavb_endpoint.h b/lib/avtp_pipeline/endpoint/openavb_endpoint.h new file mode 100644 index 00000000..782e660a --- /dev/null +++ b/lib/avtp_pipeline/endpoint/openavb_endpoint.h @@ -0,0 +1,279 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Declarations used by the endpoint code. +*/ + +#ifndef OPENAVB_ENDPOINT_H +#define OPENAVB_ENDPOINT_H + +#include "openavb_types.h" +#include "openavb_srp_api.h" +#include "openavb_endpoint_cfg.h" +#include "openavb_tl.h" + +#define AVB_ENDPOINT_HANDLE_INVALID (-1) +#define ENDPOINT_RECONNECT_SECONDS 10 +#define AVB_ENDPOINT_UNIX_PATH "/tmp/avb_endpoint" + +typedef enum { + clientNone, + clientTalker, + clientListener +} clientRole_t; + + +bool openavbEptClntService(int h, int timeout); +void openavbEptSrvrService(void); +int avbEndpointLoop(void); + + +typedef enum { + // client messages + OPENAVB_ENDPOINT_TALKER_REGISTER, + OPENAVB_ENDPOINT_LISTENER_ATTACH, + OPENAVB_ENDPOINT_CLIENT_STOP, + OPENAVB_ENDPOINT_VERSION_REQUEST, + + // server messages + OPENAVB_ENDPOINT_TALKER_CALLBACK, + OPENAVB_ENDPOINT_LISTENER_CALLBACK, + OPENAVB_ENDPOINT_VERSION_CALLBACK, +} openavbEndpointMsgType_t; + +////////////////////////////// +// Client message parameters +////////////////////////////// +typedef struct { + U8 destAddr[ETH_ALEN]; + AVBTSpec_t tSpec; + U8 srClass; + U8 srRank; + U32 latency; +} openavbEndpointParams_TalkerRegister_t; + +typedef struct { + openavbSrpLsnrDeclSubtype_t lsnrDecl; +} openavbEndpointParams_ListenerAttach_t; + +typedef struct { +} openavbEndpointParams_ClientStop_t; + +typedef struct { +} openavbEndpointParams_VersionRequest_t; + +////////////////////////////// +// Server messages parameters +////////////////////////////// +typedef struct { + char ifname[IFNAMSIZ]; + U8 destAddr[ETH_ALEN]; + openavbSrpLsnrDeclSubtype_t lsnrDecl; + U32 classRate; + U16 vlanID; + U8 priority; + U16 fwmark; +} openavbEndpointParams_TalkerCallback_t; + +typedef struct { + openavbSrpAttribType_t tlkrDecl; + char ifname[IFNAMSIZ]; + U8 destAddr[ETH_ALEN]; + AVBTSpec_t tSpec; + U8 srClass; + U32 latency; + openavbSrpFailInfo_t failInfo; +} openavbEndpointParams_ListenerCallback_t; + +typedef struct { + U32 AVBVersion; +} openavbEndpointParams_VersionCallback_t; + +#define OPENAVB_ENDPOINT_MSG_LEN sizeof(openavbEndpointMessage_t) + +typedef struct clientStream_t { + struct clientStream_t *next; // next link list pointer + + int clientHandle; // ID that links this info to client (talker or listener) + + // Information provided by the client (talker or listener) + AVBStreamID_t streamID; // stream identifier + clientRole_t role; // is client a talker or listener? + + // Information provided by the client (talker) + AVBTSpec_t tSpec; // traffic specification (bandwidth for reservation) + SRClassIdx_t srClass; // AVB class + U8 srRank; // AVB rank + U32 latency; // internal latency + + // Information provided by SRP + U8 priority; // AVB priority to use for stream + U16 vlanID; // VLAN ID to use for stream + U32 classRate; // observation intervals per second + + // Information provided by MAAP + void *hndMaap; // handle for MAAP address allocation + U8 destAddr[ETH_ALEN]; // destination MAC address (from config or MAAP) + + // Information provided by QMgr + int fwmark; // mark to identify packets of this stream +} clientStream_t; + +int startPTP(void); +int stopPTP(void); + +bool openavbEndpointServerOpen(void); +void openavbEndpointServerClose(void); +clientStream_t* findStream(AVBStreamID_t *streamID); +void delStream(clientStream_t* ps); +clientStream_t* addStream(int h, AVBStreamID_t *streamID); +void openavbEndPtLogAllStaticStreams(void); +bool x_talkerDeregister(clientStream_t *ps); +bool x_listenerDetach(clientStream_t *ps); + + +openavbRC strmRegCb(void *pv, + openavbSrpAttribType_t tlkrDecl, + U8 destAddr[], + AVBTSpec_t *tSpec, + SRClassIdx_t srClass, + U32 accumLatency, + openavbSrpFailInfo_t *failInfo); + +openavbRC strmAttachCb(void* pv, + openavbSrpLsnrDeclSubtype_t lsnrDecl); + + +#include "openavb_endpoint_osal.h" + + +/************************** Endpoint Client-Server *************************/ +// There is a single endpoint server for the entire talker/listener device +// There is an endpoint client for each stream. + +// Endpoint client open a connection to endpoint server. +// (Note that the server never opens a connection to the client.) +int openavbEptClntOpenSrvrConnection(tl_state_t *pTLState); +// Endpoint client close its connection to the server. +void openavbEptClntCloseSrvrConnection(int h); +// Endpoint server close its connection to the client. +void openavbEptSrvrCloseClientConnection(int h); + +// Immediately after establishing connection with the endpoint server, +// the endpoint client checks that the server's version is the same as its own. +// The client sends a request to the cleint for its version. +// The server handles the request and sends its version to the requesting client. +// The clinet compares the recevied version to its own. +bool openavbEptClntRequestVersionFromServer(int h); +bool openavbEptSrvrHndlVerRqstFromClient(int h); +void openavbEptSrvrSendServerVersionToClient(int h, U32 AVBVersion); +void openavbEptClntCheckVerMatchesSrvr(int h, U32 AVBVersion); + + +// Each talker registers its stream with SRP via Endpoint. +// Endpoint communication is from client to server +bool openavbEptClntRegisterStream(int h, + AVBStreamID_t *streamID, + U8 destAddr[], + AVBTSpec_t *tSpec, + U8 srClass, + U8 srRank, + U32 latency); +bool openavbEptSrvrRegisterStream(int h, + AVBStreamID_t *streamID, + U8 destAddr[], + AVBTSpec_t *tSpec, + U8 srClass, + U8 srRank, + U32 latency); + +// Each lister attaches to the stream it wants to receive; +// specifically the listener indicates interest / declaration to SRP. +// Endpoint communication is from client to server. +bool openavbEptClntAttachStream(int h, + AVBStreamID_t *streamID, + openavbSrpLsnrDeclSubtype_t ld); +bool openavbEptSrvrAttachStream(int h, + AVBStreamID_t *streamID, + openavbSrpLsnrDeclSubtype_t ld); + + +// SRP notifies the talker when its stream has been established to taken down. +// Endpoint communication is from server to client. +void openavbEptSrvrNotifyTlkrOfSrpCb(int h, + AVBStreamID_t *streamID, + char *ifname, + U8 destAddr[], + openavbSrpLsnrDeclSubtype_t lsnrDecl, + U32 classRate, + U16 vlanID, + U8 priority, + U16 fwmark); +void openavbEptClntNotifyTlkrOfSrpCb(int h, + AVBStreamID_t *streamID, + char *ifname, + U8 destAddr[], + openavbSrpLsnrDeclSubtype_t lsnrDecl, + U32 classRate, + U16 vlanID, + U8 priority, + U16 fwmark); + +// SRP notifies the listener when its stream is available, failed or gone away. +// Endpoint communication is from server to client. +void openavbEptSrvrNotifyLstnrOfSrpCb(int h, + AVBStreamID_t *streamID, + char *ifname, + U8 destAddr[], + openavbSrpAttribType_t tlkrDecl, + AVBTSpec_t *tSpec, + U8 srClass, + U32 latency, + openavbSrpFailInfo_t *failInfo); +void openavbEptClntNotifyLstnrOfSrpCb(int h, + AVBStreamID_t *streamID, + char *ifname, + U8 destAddr[], + openavbSrpAttribType_t tlkrDecl, + AVBTSpec_t *tSpec, + U8 srClass, + U32 latency, + openavbSrpFailInfo_t *failInfo); + + +// A talker can withdraw its stream registration at any time; +// a listener can withdraw its stream attachement at any time; +// in ether case, endpoint communication is from client to server +bool openavbEptClntStopStream(int h, AVBStreamID_t *streamID); +bool openavbEptSrvrStopStream(int h, AVBStreamID_t *streamID); + + +#endif // OPENAVB_ENDPOINT_H diff --git a/lib/avtp_pipeline/endpoint/openavb_endpoint_client.c b/lib/avtp_pipeline/endpoint/openavb_endpoint_client.c new file mode 100644 index 00000000..da10d734 --- /dev/null +++ b/lib/avtp_pipeline/endpoint/openavb_endpoint_client.c @@ -0,0 +1,199 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : +* +* Stream clients (talkers or listeners) must connect to the central +* "endpoint" process to create a reservation for their traffic. +* This code provides the means for them to do so. +* +* This source file is compiled into the streamer executable. +* +* It provides proxy functions for the streamer to call. The arguments +* for those calls are packed into messages, which are unpacked in the +* endpoint process and then used to call the real functions. +* +* Current IPC uses unix sockets. Can change this by creating a new +* implementations in openavb_enpoint_client.c and openavb_endpoint_server.c +*/ + +#include <stdlib.h> +#include <string.h> + +#include "openavb_platform.h" + +#include "openavb_trace.h" +#include "openavb_endpoint.h" + +#define AVB_LOG_COMPONENT "Endpoint" +#include "openavb_pub.h" +#include "openavb_log.h" + +// forward declarations +static bool openavbEptClntReceiveFromServer(int h, openavbEndpointMessage_t *msg); + +// OSAL specific functions for openavb_endpoint_client.c +#include "openavb_endpoint_client_osal.c" + +static bool openavbEptClntReceiveFromServer(int h, openavbEndpointMessage_t *msg) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + if (!msg || h == AVB_ENDPOINT_HANDLE_INVALID) { + AVB_LOG_ERROR("Client receive; invalid argument passed"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + + switch(msg->type) { + case OPENAVB_ENDPOINT_TALKER_CALLBACK: + openavbEptClntNotifyTlkrOfSrpCb(h, + &msg->streamID, + msg->params.talkerCallback.ifname, + msg->params.talkerCallback.destAddr, + msg->params.talkerCallback.lsnrDecl, + msg->params.talkerCallback.classRate, + msg->params.talkerCallback.vlanID, + msg->params.talkerCallback.priority, + msg->params.talkerCallback.fwmark); + break; + case OPENAVB_ENDPOINT_LISTENER_CALLBACK: + openavbEptClntNotifyLstnrOfSrpCb(h, + &msg->streamID, + msg->params.listenerCallback.ifname, + msg->params.listenerCallback.destAddr, + msg->params.listenerCallback.tlkrDecl, + &msg->params.listenerCallback.tSpec, + msg->params.listenerCallback.srClass, + msg->params.listenerCallback.latency, + &msg->params.listenerCallback.failInfo); + break; + case OPENAVB_ENDPOINT_VERSION_CALLBACK: + openavbEptClntCheckVerMatchesSrvr(h, msg->params.versionCallback.AVBVersion); + break; + default: + AVB_LOG_ERROR("Client receive: unexpected message"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return TRUE; +} + +bool openavbEptClntRegisterStream(int h, + AVBStreamID_t *streamID, + U8 destAddr[], + AVBTSpec_t *tSpec, + U8 srClass, + U8 srRank, + U32 latency) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + openavbEndpointMessage_t msgBuf; + + // Check for valid parameters. destAddr is optional and checked later in this function before using. + if (!streamID || !tSpec) { + AVB_LOG_ERROR("Client register: invalid argument"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + + memset(&msgBuf, 0, OPENAVB_ENDPOINT_MSG_LEN); + msgBuf.type = OPENAVB_ENDPOINT_TALKER_REGISTER; + memcpy(&(msgBuf.streamID), streamID, sizeof(AVBStreamID_t)); + if (destAddr) + memcpy(msgBuf.params.talkerRegister.destAddr, destAddr, ETH_ALEN); + msgBuf.params.talkerRegister.tSpec.maxFrameSize = tSpec->maxFrameSize; + msgBuf.params.talkerRegister.tSpec.maxIntervalFrames = tSpec->maxIntervalFrames; + msgBuf.params.talkerRegister.srClass = srClass; + msgBuf.params.talkerRegister.srRank = srRank; + msgBuf.params.talkerRegister.latency = latency; + bool ret = openavbEptClntSendToServer(h, &msgBuf); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return ret; +} + +bool openavbEptClntAttachStream(int h, AVBStreamID_t *streamID, + openavbSrpLsnrDeclSubtype_t ld) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + openavbEndpointMessage_t msgBuf; + + if (!streamID) { + AVB_LOG_ERROR("Client attach: invalid argument"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + + memset(&msgBuf, 0, OPENAVB_ENDPOINT_MSG_LEN); + msgBuf.type = OPENAVB_ENDPOINT_LISTENER_ATTACH; + memcpy(&(msgBuf.streamID), streamID, sizeof(AVBStreamID_t)); + msgBuf.params.listenerAttach.lsnrDecl = ld; + bool ret = openavbEptClntSendToServer(h, &msgBuf); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return ret; +} + +bool openavbEptClntStopStream(int h, AVBStreamID_t *streamID) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + openavbEndpointMessage_t msgBuf; + + if (!streamID) { + AVB_LOG_ERROR("Client stop: invalid argument"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + + memset(&msgBuf, 0, OPENAVB_ENDPOINT_MSG_LEN); + msgBuf.type = OPENAVB_ENDPOINT_CLIENT_STOP; + memcpy(&(msgBuf.streamID), streamID, sizeof(AVBStreamID_t)); + bool ret = openavbEptClntSendToServer(h, &msgBuf); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return ret; +} + +bool openavbEptClntRequestVersionFromServer(int h) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + openavbEndpointMessage_t msgBuf; + + memset(&msgBuf, 0, OPENAVB_ENDPOINT_MSG_LEN); + msgBuf.type = OPENAVB_ENDPOINT_VERSION_REQUEST; + bool ret = openavbEptClntSendToServer(h, &msgBuf); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return ret; +} + diff --git a/lib/avtp_pipeline/endpoint/openavb_endpoint_server.c b/lib/avtp_pipeline/endpoint/openavb_endpoint_server.c new file mode 100644 index 00000000..a609d2ed --- /dev/null +++ b/lib/avtp_pipeline/endpoint/openavb_endpoint_server.c @@ -0,0 +1,394 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* + * MODULE SUMMARY : + * + * Stream clients (talkers or listeners) must connect to the central + * "endpoint" process to create a reservation for their traffic. + * + * This code implements the endpoint (server) side of the IPC. + * + * It provides proxy functions for the endpoint to call. The arguments + * for those calls are packed into messages, which are unpacked in the + * streamer processes and then used to call the real functions. + * + * Current IPC uses unix sockets. Can change this by creating a new + * implementations in openavb_endoint_client.c and openavb_endpoint_server.c + */ + +#include <stdlib.h> +#include <string.h> + +#include "openavb_endpoint.h" +#include "openavb_trace.h" + +//#define AVB_LOG_LEVEL AVB_LOG_LEVEL_DEBUG +#define AVB_LOG_COMPONENT "Endpoint Server" +#include "openavb_pub.h" +#include "openavb_log.h" +#include "openavb_qmgr.h" // for INVALID_FWMARK +#include "openavb_maap.h" + +// forward declarations +static bool openavbEptSrvrReceiveFromClient(int h, openavbEndpointMessage_t *msg); + +#include "openavb_endpoint_server_osal.c" + +// the following are from openavb_endpoint.c +extern openavb_endpoint_cfg_t x_cfg; +extern clientStream_t* x_streamList; + + +static bool openavbEptSrvrReceiveFromClient(int h, openavbEndpointMessage_t *msg) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + if (!msg) { + AVB_LOG_ERROR("Receiving message; invalid argument passed"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + + bool ret = FALSE; + switch (msg->type) { + case OPENAVB_ENDPOINT_TALKER_REGISTER: + AVB_LOGF_DEBUG("TalkerRegister from client uid=%d", msg->streamID.uniqueID); + ret = openavbEptSrvrRegisterStream(h, &msg->streamID, + msg->params.talkerRegister.destAddr, + &msg->params.talkerRegister.tSpec, + msg->params.talkerRegister.srClass, + msg->params.talkerRegister.srRank, + msg->params.talkerRegister.latency); + break; + case OPENAVB_ENDPOINT_LISTENER_ATTACH: + AVB_LOGF_DEBUG("ListenerAttach from client uid=%d", msg->streamID.uniqueID); + ret = openavbEptSrvrAttachStream(h, &msg->streamID, + msg->params.listenerAttach.lsnrDecl); + break; + case OPENAVB_ENDPOINT_CLIENT_STOP: + AVB_LOGF_DEBUG("Stop from client uid=%d", msg->streamID.uniqueID); + ret = openavbEptSrvrStopStream(h, &msg->streamID); + break; + case OPENAVB_ENDPOINT_VERSION_REQUEST: + AVB_LOG_DEBUG("Version request from client"); + ret = openavbEptSrvrHndlVerRqstFromClient(h); + break; + default: + AVB_LOG_ERROR("Unexpected message received at server"); + break; + } + + AVB_LOGF_VERBOSE("Message handled, ret=%d", ret); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return ret; +} + +void openavbEptSrvrNotifyTlkrOfSrpCb(int h, + AVBStreamID_t *streamID, + char *ifname, + U8 destAddr[ETH_ALEN], + openavbSrpLsnrDeclSubtype_t lsnrDecl, + U32 classRate, + U16 vlanID, + U8 priority, + U16 fwmark) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + openavbEndpointMessage_t msgBuf; + + if (!streamID || !ifname || !destAddr) { + AVB_LOG_ERROR("Talker callback; invalid argument passed"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return; + } + + memset(&msgBuf, 0, OPENAVB_ENDPOINT_MSG_LEN); + msgBuf.type = OPENAVB_ENDPOINT_TALKER_CALLBACK; + memcpy(&(msgBuf.streamID), streamID, sizeof(AVBStreamID_t)); + strncpy(msgBuf.params.talkerCallback.ifname, ifname, IFNAMSIZ - 1); + memcpy(msgBuf.params.talkerCallback.destAddr, destAddr, ETH_ALEN); + msgBuf.params.talkerCallback.lsnrDecl = lsnrDecl; + msgBuf.params.talkerCallback.classRate = classRate; + msgBuf.params.talkerCallback.vlanID = vlanID; + msgBuf.params.talkerCallback.priority = priority; + msgBuf.params.talkerCallback.fwmark = fwmark; + openavbEptSrvrSendToClient(h, &msgBuf); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); +} + +void openavbEptSrvrNotifyLstnrOfSrpCb(int h, + AVBStreamID_t *streamID, + char *ifname, + U8 destAddr[], + openavbSrpAttribType_t tlkrDecl, + AVBTSpec_t *tSpec, + U8 srClass, + U32 latency, + openavbSrpFailInfo_t* failInfo) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + openavbEndpointMessage_t msgBuf; + + // Check for valid parameters. DestAddr is optional and checked later. + if (!streamID || !ifname) { + AVB_LOG_ERROR("Listener callback; invalid argument passed"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return; + } + + memset(&msgBuf, 0, OPENAVB_ENDPOINT_MSG_LEN); + msgBuf.type = OPENAVB_ENDPOINT_LISTENER_CALLBACK; + memcpy(&(msgBuf.streamID), streamID, sizeof(AVBStreamID_t)); + strncpy(msgBuf.params.listenerCallback.ifname, ifname, IFNAMSIZ - 1); + if (destAddr) + memcpy(msgBuf.params.listenerCallback.destAddr, destAddr, ETH_ALEN); + msgBuf.params.listenerCallback.tlkrDecl = tlkrDecl; + if (tSpec) + memcpy(&msgBuf.params.listenerCallback.tSpec, tSpec, sizeof(AVBTSpec_t)); + msgBuf.params.listenerCallback.srClass = srClass; + msgBuf.params.listenerCallback.latency = latency; + if (failInfo) + memcpy(&msgBuf.params.listenerCallback.failInfo, failInfo, sizeof(openavbSrpFailInfo_t)); + openavbEptSrvrSendToClient(h, &msgBuf); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); +} + +void openavbEptSrvrSendServerVersionToClient(int h, U32 AVBVersion) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + openavbEndpointMessage_t msgBuf; + memset(&msgBuf, 0, OPENAVB_ENDPOINT_MSG_LEN); + msgBuf.type = OPENAVB_ENDPOINT_VERSION_CALLBACK; + msgBuf.params.versionCallback.AVBVersion = AVBVersion; + openavbEptSrvrSendToClient(h, &msgBuf); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); +} + +/* Talker client registers a stream + */ +bool openavbEptSrvrRegisterStream(int h, + AVBStreamID_t *streamID, + U8 destAddr[], + AVBTSpec_t *tSpec, + U8 srClass, + U8 srRank, + U32 latency) +{ + openavbRC rc = OPENAVB_SUCCESS; + + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + clientStream_t *ps = findStream(streamID); + + if (ps && ps->clientHandle != h) { + AVB_LOGF_ERROR("Error registering talker; multiple clients for stream %d", streamID->uniqueID); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + + ps = addStream(h, streamID); + if (!ps) { + AVB_LOGF_ERROR("Error registering talker; unable to add client stream %d", streamID->uniqueID); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + ps->role = clientTalker; + ps->tSpec = *tSpec; + ps->srClass = (SRClassIdx_t)srClass; + ps->srRank = srRank; + ps->latency = latency; + ps->fwmark = INVALID_FWMARK; + + if (memcmp(ps->destAddr, destAddr, ETH_ALEN) == 0) { + // no client-supplied address, use MAAP + struct ether_addr addr; + ps->hndMaap = openavbMaapAllocate(1, &addr); + if (ps->hndMaap) { + memcpy(ps->destAddr, addr.ether_addr_octet, ETH_ALEN); + strmAttachCb((void*)ps, openavbSrp_LDSt_Stream_Info); // Inform talker about MAAP + } + else { + AVB_LOG_ERROR("Error registering talker: MAAP failed to allocate MAC address"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + delStream(ps); + return FALSE; + } + } + else { + // client-supplied destination MAC address + memcpy(ps->destAddr, destAddr, ETH_ALEN); + ps->hndMaap = NULL; + } + + // Do SRP talker register + AVB_LOGF_DEBUG("REGISTER: ps=%p, streamID=%d, tspec=%d,%d, srClass=%d, srRank=%d, latency=%d, da="ETH_FORMAT"", + ps, streamID->uniqueID, + tSpec->maxFrameSize, tSpec->maxIntervalFrames, + ps->srClass, ps->srRank, ps->latency, + ETH_OCTETS(ps->destAddr)); + + + if(x_cfg.noSrp) { + // we are operating in a mode supporting preconfigured streams; SRP is not in use, + // so, as a proxy for SRP, which would normally make this call after establishing + // the stream, call the callback from here + strmAttachCb((void*)ps, openavbSrp_LDSt_Ready); + } else { + // normal SRP operation + rc = openavbSrpRegisterStream((void*)ps, &ps->streamID, + ps->destAddr, &ps->tSpec, + ps->srClass, ps->srRank, + ps->latency); + if (!IS_OPENAVB_SUCCESS(rc)) { + if (ps->hndMaap) + openavbMaapRelease(ps->hndMaap); + delStream(ps); + } + } + + openavbEndPtLogAllStaticStreams(); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return IS_OPENAVB_SUCCESS(rc); +} + +/* Listener client attaches to a stream + */ +bool openavbEptSrvrAttachStream(int h, + AVBStreamID_t *streamID, + openavbSrpLsnrDeclSubtype_t ld) +{ + openavbRC rc = OPENAVB_SUCCESS; + static U8 emptyMAC[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + static AVBTSpec_t emptytSpec = {0, 0}; + + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + clientStream_t *ps = findStream(streamID); + if (ps && ps->clientHandle != h) { + AVB_LOGF_ERROR("Error attaching listener: multiple clients for stream %d", streamID->uniqueID); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + + if (!ps) { + ps = addStream(h, streamID); + if (!ps) { + AVB_LOGF_ERROR("Error attaching listener: unable to add client stream %d", streamID->uniqueID); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + ps->role = clientListener; + } + + if(x_cfg.noSrp) { + // we are operating in a mode supporting preconfigured streams; SRP is not in use, + if(ld == openavbSrp_LDSt_Interest) { + // As a proxy for SRP, which would normally make this call after confirming + // availability of the stream, call the callback from here + strmRegCb((void*)ps, openavbSrp_AtTyp_TalkerAdvertise, + emptyMAC, // a flag to listener to read info from configuration file + &emptytSpec, + MAX_AVB_SR_CLASSES, // srClass - value doesn't matter because openavbEptSrvrNotifyLstnrOfSrpCb() throws it away + 1, // accumLatency + NULL); // *failInfo + } + } else { + // Normal SRP Operation so pass to SRP + rc = openavbSrpAttachStream((void*)ps, streamID, ld); + if (!IS_OPENAVB_SUCCESS(rc)) + delStream(ps); + } + + openavbEndPtLogAllStaticStreams(); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return IS_OPENAVB_SUCCESS(rc); +} + +/* Client (talker or listener) going away + */ +bool openavbEptSrvrStopStream(int h, AVBStreamID_t *streamID) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + clientStream_t *ps = findStream(streamID); + if (!ps || ps->clientHandle != h) { + AVB_LOGF_ERROR("Error stopping client: missing record for stream %d", streamID->uniqueID); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + + bool rc = FALSE; + if (ps->role == clientTalker) + rc = x_talkerDeregister(ps); + else if (ps->role == clientListener) + rc = x_listenerDetach(ps); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return rc; +} + +/* Client version request + */ +bool openavbEptSrvrHndlVerRqstFromClient(int h) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + openavbEptSrvrSendServerVersionToClient(h, AVB_CORE_VER_FULL); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return TRUE; +} + +/* Called if a client closes their end of the IPC + */ +void openavbEptSrvrCloseClientConnection(int h) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + clientStream_t **lpp; + for(lpp = &x_streamList; *lpp != NULL; lpp = &(*lpp)->next) { + if ((*lpp)->clientHandle == h) + { + if ((*lpp)->role == clientTalker) + x_talkerDeregister((*lpp)); + else if ((*lpp)->role == clientListener) + x_listenerDetach((*lpp)); + break; + } + } + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); +} + diff --git a/lib/avtp_pipeline/endpoint/shutdown_openavb_endpoint.sh b/lib/avtp_pipeline/endpoint/shutdown_openavb_endpoint.sh new file mode 100644 index 00000000..7020ec85 --- /dev/null +++ b/lib/avtp_pipeline/endpoint/shutdown_openavb_endpoint.sh @@ -0,0 +1,23 @@ +#! /bin/sh +# +# AVB endpoint clean-up script +# +# Only needs to be run if the endpoint process crashes. +# +# Removes resources created/loaded by the endpoint, so that a new +# instance can run. + +IFACES=$(cat /proc/net/dev | grep -- : | cut -d: -f1) + +echo "removing endpoint resources" + +killall -s SIGINT openavb_endpoint > /dev/null 2>&1 +killall -s SIGINT openavb_gptp > /dev/null 2>&1 +rm -f /tmp/avb_endpoint > /dev/null 2>&1 + +for I in ${IFACES} +do + ./tc qdisc del dev ${I} root > /dev/null 2>&1 +done + +rmmod sch_avb > /dev/null 2>&1 diff --git a/lib/avtp_pipeline/include/avb_sched.h b/lib/avtp_pipeline/include/avb_sched.h new file mode 100644 index 00000000..5e5c3775 --- /dev/null +++ b/lib/avtp_pipeline/include/avb_sched.h @@ -0,0 +1,126 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#ifndef AVB_SCHED_H +#define AVB_SCHED_H + +// Macros to map stream/class into 16-bit fwmark +// Bottom 8 bits are used for stream index (allows 32 streams/class) +// Upper 8 bits are used for class index +// +// The fwmark is attached to the socket, and the kernel uses it to +// steer AVTP frames into the correct queues. +// +// If we ever want more than 256 classes (won't happen) or 256 streams +// per class (very unlikely) we'll need to shift the boundary between +// the bits used for the class idex and those used for stream idx. +// +// If the combination of the two needs more than 16 bits, we'll have +// to use something other than the FWMARK to communicate with the kernel. +// +#define TC_AVB_CLASS_SHIFT 8 +#define TC_AVB_STREAM_MASK ((1 << TC_AVB_CLASS_SHIFT) - 1) +#define TC_AVB_MARK_CLASS(M) (((M) >> TC_AVB_CLASS_SHIFT) - 1) +#define TC_AVB_MARK_STREAM(M) ((M) & TC_AVB_STREAM_MASK) +#define TC_AVB_MARK(C,S) (((C + 1) << TC_AVB_CLASS_SHIFT) | (TC_AVB_STREAM_MASK & (S))) + +#ifndef AVB_CLASS_LABEL +#define AVB_CLASS_LABEL(C) ('A'+C) +#endif + +//#define AVB_ENABLE_HWQ_PER_CLASS 1 + +// Modes for qdisc shaping +// +typedef enum { + // Disable FQTSS + AVB_SHAPER_DISABLED, + // Drop all frames + AVB_SHAPER_DROP_ALL, + // Treat everything as non-SR traffic + AVB_SHAPER_ALL_NONSR, + // Drop all AVB stream frames + AVB_SHAPER_DROP_SR, + // Shaping done in SW + // - Credit-based shaping per-class (in software) + // - Priority between classes + // - WRR for streams within each class + AVB_SHAPER_SW, +#ifdef AVB_ENABLE_HWQ_PER_CLASS + // Shaping in HW w/TXQ per AVB class + // - Each AVB class gets its own txq w/shaping in HW + // - SW does round-robin among classes to keep all TX queues fed + // - SW does WRR for streams within each class + AVB_SHAPER_HWQ_PER_CLASS, +#endif +#ifdef AVB_ENABLE_HWQ_PER_STREAM + // Shaping in HW w/TXQ per AVB stream + // - Each AVB stream gets its own txq w/shaping in HW + // - SW does round-robin among streams to keep all TX queues fed + AVB_SHAPER_HWQ_PER_STREAM +#endif +} avb_shaper_mode; + +/* Options/Stats for AVB qdisc + * (information passed back/forth between kernel and userland) + */ +struct tc_avb_qopt { + __u16 limit; // number of packets that may be queued in qdisc + __u16 num_classes; // number of SR classes + __u16 num_streams; // number of streams per SR class + __u16 linkBytesPerSec; // link speed + __u8 mode; // shaping mode (HW or SW) +}; + +/* Options/Stats for AVB streams + * (information passed back/forth between kernel and userland) + */ +struct tc_avb_sopt { + __u32 mark; // mark associated with stream + __u32 streamBytesPerSec; // reserved bandwith (non-zero for established stream) + __u16 maxIntervalFrames; // max frames per interval + __u16 maxFrameSize; // max frame size + __u32 nDropped, nQueued, nSent; +}; + +enum { + TCA_AVB_UNSPEC, + TCA_AVB_QOPT, + TCA_AVB_SOPT, + __TCA_AVB_MAX, +}; + +#define TCA_AVB_MAX (__TCA_AVB_MAX - 1)\ + +#define AVB_CLASS_A 2 +#define AVB_CLASS_B 1 +#define AVB_CLASS_NR 0 + +#endif diff --git a/lib/avtp_pipeline/include/openavb_audio_pub.h b/lib/avtp_pipeline/include/openavb_audio_pub.h new file mode 100644 index 00000000..2bb8a00f --- /dev/null +++ b/lib/avtp_pipeline/include/openavb_audio_pub.h @@ -0,0 +1,157 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : General Audio Types Public +*/ + +#ifndef AVB_AUDIO_PUB_H +#define AVB_AUDIO_PUB_H 1 + +/** \file + * General audio types + */ + +/** Audio rate + */ +typedef enum { + /// 8000 + AVB_AUDIO_RATE_8KHZ = 8000, + /// 11025 + AVB_AUDIO_RATE_11_025KHZ = 11025, + /// 16000 + AVB_AUDIO_RATE_16KHZ = 16000, + /// 22050 + AVB_AUDIO_RATE_22_05KHZ = 22050, + /// 32000 + AVB_AUDIO_RATE_32KHZ = 32000, + /// 44100 + AVB_AUDIO_RATE_44_1KHZ = 44100, + /// 48000 + AVB_AUDIO_RATE_48KHZ = 48000, + /// 64000 + AVB_AUDIO_RATE_64KHZ = 64000, + /// 88200 + AVB_AUDIO_RATE_88_2KHZ = 88200, + /// 96000 + AVB_AUDIO_RATE_96KHZ = 96000, + /// 176400 + AVB_AUDIO_RATE_176_4KHZ = 176400, + /// 192000 + AVB_AUDIO_RATE_192KHZ = 192000 +} avb_audio_rate_t; + +/** Defines what type is data. + * + * Information is needed together with endianes and bit depth to configure the + * sample format correctly + */ +typedef enum { + /// Data type undefined + AVB_AUDIO_TYPE_UNSPEC, + /// Data type int + AVB_AUDIO_TYPE_INT, + /// Data type unsigned int + AVB_AUDIO_TYPE_UINT, + /// Data type float + AVB_AUDIO_TYPE_FLOAT, +} avb_audio_type_t; + +/** Defines endianess of data. + * + * Information is needed together with data type and bit depth to configure the + * sample format correctly + */ +typedef enum { + /// Unspecified + AVB_AUDIO_ENDIAN_UNSPEC, + /// Little endian + AVB_AUDIO_ENDIAN_LITTLE, + /// Big endian + AVB_AUDIO_ENDIAN_BIG, +} avb_audio_endian_t; + +/** Bit depth of audio. + * + * Information is needed together with endianes and data type to configure the + * sample format correctly + */ +typedef enum { + /// 1 bit + AVB_AUDIO_BIT_DEPTH_1BIT = 1, + /// 8 bit + AVB_AUDIO_BIT_DEPTH_8BIT = 8, + /// 16 bit + AVB_AUDIO_BIT_DEPTH_16BIT = 16, + /// 20 bit + AVB_AUDIO_BIT_DEPTH_20BIT = 20, + /// 24 bit + AVB_AUDIO_BIT_DEPTH_24BIT = 24, + /// 32 bit + AVB_AUDIO_BIT_DEPTH_32BIT = 32, + /// 48 bit + AVB_AUDIO_BIT_DEPTH_48BIT = 48, + /// 64 bit + AVB_AUDIO_BIT_DEPTH_64BIT = 64 +} avb_audio_bit_depth_t; + +/** Number of channels + */ +typedef enum { + /// 1 channel + AVB_AUDIO_CHANNELS_1 = 1, + /// 2 channels + AVB_AUDIO_CHANNELS_2 = 2, + /// 3 channels + AVB_AUDIO_CHANNELS_3 = 3, + /// 4 channels + AVB_AUDIO_CHANNELS_4 = 4, + /// 5 channels + AVB_AUDIO_CHANNELS_5 = 5, + /// 6 channels + AVB_AUDIO_CHANNELS_6 = 6, + /// 7 channels + AVB_AUDIO_CHANNELS_7 = 7, + /// 8 channels + AVB_AUDIO_CHANNELS_8 = 8 +} avb_audio_channels_t; + +/** Media Clock Recovery. + */ +typedef enum { + /// No Media Clock Recovery is Done, this is the default + AVB_MCR_NONE, + /// Media Clock Recovery done by using AVTP timestamps + AVB_MCR_AVTP_TIMESTAMP, + /// Media Clock Recovery done by using 1722(a), Clock Reference Stream (CRS) + AVB_MCR_CRS +}avb_audio_mcr_t; + +#endif // AVB_AUDIO_PUB_H diff --git a/lib/avtp_pipeline/include/openavb_intf_pub.h b/lib/avtp_pipeline/include/openavb_intf_pub.h new file mode 100755 index 00000000..5594e7fd --- /dev/null +++ b/lib/avtp_pipeline/include/openavb_intf_pub.h @@ -0,0 +1,230 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Common interface module public header +*/ + +#ifndef OPENAVB_INTF_PUB_H +#define OPENAVB_INTF_PUB_H 1 + +#include "openavb_types_pub.h" +#include "openavb_mediaq_pub.h" + +/** \file + * Common interface module public header. + */ + +/** Configuration callback into the interface module. + * + * This callback function is called during the reading of the configuration file + * by the talker and listener for any named configuration item starting with + * "intf_nv". + * This is a convenient way to store new configuration name/value pairs that are + * needed in an interface module. + * + * \param pMediaQ A pointer to the media queue for this stream + * \param name The item name from the configuration file + * \param value The item value from the configuration file + */ +typedef void (*openavb_intf_cfg_cb_t)(media_q_t *pMediaQ, const char *name, const char *value); + +/** General initialize callback regardless if a talker or listener. + * + * This callback function is called when the openavbTLOpen() function is called for + * the EAVB SDK API. + * + * \param pMediaQ A pointer to the media queue for this stream + */ +typedef void (*openavb_intf_gen_init_cb_t)(media_q_t *pMediaQ); + +/** AVDECC initialize callback for both a talker or listener. + * + * Entity model based configuration can be processed at this time. + * This callback is optional and only executed when AVDECC is used to connect + * streams. + * + * \param pMediaQ A pointer to the media queue for this stream + * \param configIdx current configuration descriptor index for the Entity Model + * \param descriptorType The descriptorType is expected to be of STREAM_INPUT + * for listeners and STREAM_OUTPUT for talkers. + * \param descriptorIdx descriptor index in the Entity Model + */ +typedef void (*openavb_intf_avdecc_init_cb_t)(media_q_t *pMediaQ, U16 configIdx, U16 descriptorType, U16 descriptorIdx); + +/** Initialize transmit callback into the interface module. + * + * This callback function is called anytime a stream reservation has completed + * successfully within a talker process. It does not get called when running + * within a listener process. + * + * \param pMediaQ A pointer to the media queue for this stream + */ +typedef void (*openavb_intf_tx_init_cb_t)(media_q_t *pMediaQ); + +/** Transmit callback into the interface module. + * + * This is the transmit callback function for the interface module. + * This function will typically be called thousands of times per second + * depending the SR class type (A or B). This frequency may also be changed by + * the mapping module and at times configurable by mapping modules for example + * with the map_nv_tx_rate configuration value. + * If pacing is done in the interface module by: + * + * cfg->tx_blocking_in_intf = FALSE; + * + * Then this callback will suspend task execution until there is media data + * available for the mapping module. + * + * \param pMediaQ A pointer to the media queue for this stream + */ +typedef bool (*openavb_intf_tx_cb_t)(media_q_t *pMediaQ); + +/** Initialize the receive callback into the interface module. + * + * This callback function is called anytime a stream reservation has completed + * successfully within a listener process. It does not get called when running + * within a talker process. + * + * \param pMediaQ A pointer to the media queue for this stream + */ +typedef void (*openavb_intf_rx_init_cb_t)(media_q_t *pMediaQ); + +/** Translate RX data callback. + * + * This callback that may be used by mapping modules to allow + * interfaces to translate packet data as it arrives and before + * it gets packed into the media queue Item. Mapping modules + * MUST expose a function pointer var in their public data and + * the interface module must set it for the CB to be used. + * + * \param pMediaQ A pointer to the media queue for this stream + * \param pPubDataQ A pointer to the data + * \param length Length of the data + */ +typedef void (*openavb_intf_rx_translate_cb_t)(media_q_t *pMediaQ, U8 *pPubData, U32 length); + +/** Receive callback into the interface module. + * + * This callback function is called when AVB packet data is received or when + * tail data item within the media queue has reached the presentation time + * + * \param pMediaQ A pointer to the media queue for this stream + */ +typedef bool (*openavb_intf_rx_cb_t)(media_q_t *pMediaQ); + +/** Callback when the stream is ending. + * + * This callback function is called when a stream is closing. + * + * \param pMediaQ A pointer to the media queue for this stream + */ +typedef void (*openavb_intf_end_cb_t)(media_q_t *pMediaQ); + +/** General shutdown callback into the interface module. + * + * This callback function is called when the openavbTLClose() function is called for + * the EAVB SDK API + * + * \param pMediaQ A pointer to the media queue for this stream + */ +typedef void (*openavb_intf_gen_end_cb_t)(media_q_t *pMediaQ); + +/** Query the interface for source bitrate. + * + * This callback is called to get the maximum bitrate of the source (in bits per + * second). For example for a mpeg2ts file interface this callback returns the + * maximum bitrate of the mpeg2ts file. + * \param pMediaQ A pointer to the media queue for this stream + * \return Maximum bitrate of the source. + * + * \note This callback is optional, does not need to be implemented in the + * interface module. + */ +typedef unsigned int (*openavb_intf_get_src_bitrate_t)(media_q_t *pMediaQ); + +/** Interface callbacks structure. + */ +typedef struct { + /// Configuration callback. + openavb_intf_cfg_cb_t intf_cfg_cb; + /// General initialize callback. + openavb_intf_gen_init_cb_t intf_gen_init_cb; + /// AVDECC initialize callback. + openavb_intf_avdecc_init_cb_t intf_avdecc_init_cb; + /// Initialize transmit callback. + openavb_intf_tx_init_cb_t intf_tx_init_cb; + /// Transmit callback. + openavb_intf_tx_cb_t intf_tx_cb; + /// Initialize receive callback. + openavb_intf_rx_init_cb_t intf_rx_init_cb; + // openavb_intf_rx_translate_cb_t intf_rx_translate_cb; // Hidden since it is for direct mapping -> interface CBs + /// Receive callback. + openavb_intf_rx_cb_t intf_rx_cb; + /// Stream end callback. + openavb_intf_end_cb_t intf_end_cb; + /// General shutdown callback. + openavb_intf_gen_end_cb_t intf_gen_end_cb; + /// Pointer to interface specific callbacks that a hosting application may call. + /// It is pointer to openavb_intf_host_cb_list_t structure. + void * intf_host_cb_list; + /// Source bit rate callback. + openavb_intf_get_src_bitrate_t intf_get_src_bitrate_cb; +} openavb_intf_cb_t; + +/** Main initialization entry point into the interface module. + * + * This is the main entry point into the interface module. Every interface + * module must define this function. The talker process and listener process + * call this function directly while the configuration file it being read. The + * function address is discovered via the settings in the talker and listener + * configuration structure. For example: + * + * osalCfg.pIntfInitFn = openavbIntfJ6Video; + * + * Within this function the callbacks must all be set into the structure pointer + * parameter. + * Any interface module initialization can take place during this call such as + * setting default values into configuration settings. + * Private interface module should be allocated during this call. This can be + * used to hold configuration data as well as functional variable data. + * + * \param pMediaQ A pointer to the media queue for this stream + * \param pIntfCB Pointer to the callback structure. All the members of this + * structure must be set during this function call + * \return TRUE on success or FALSE on failure + */ +typedef bool (*openavb_intf_initialize_fn_t)(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); + +/** \example openavb_intf_echo.c + * \brief A source code for a complete sample interface module. + */ +#endif // OPENAVB_INTF_PUB_H + diff --git a/lib/avtp_pipeline/include/openavb_log.h b/lib/avtp_pipeline/include/openavb_log.h new file mode 100644 index 00000000..9acb2fab --- /dev/null +++ b/lib/avtp_pipeline/include/openavb_log.h @@ -0,0 +1,43 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Provide a simple logging facility for use +* during development. Expect that this logging will be switched +* off eventually. Uses macros that can be redefined to empty. +* Details have been moved to the public interface. +*/ + +#ifndef OPENAVB_LOG_H +#define OPENAVB_LOG_H 1 + +#include "openavb_log_pub.h" + +#endif // OPENAVB_LOG_H diff --git a/lib/avtp_pipeline/include/openavb_log_pub.h b/lib/avtp_pipeline/include/openavb_log_pub.h new file mode 100644 index 00000000..4b7f01e0 --- /dev/null +++ b/lib/avtp_pipeline/include/openavb_log_pub.h @@ -0,0 +1,246 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+/*
+* MODULE SUMMARY : A simple logging facility for use during
+* development.
+*/
+
+#ifndef OPENAVB_LOG_PUB_H
+#define OPENAVB_LOG_PUB_H 1
+
+// ********
+// Merge Issue
+// TODO: Restructure to remove #ifdef code.
+// ********
+
+#include "openavb_platform_pub.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "openavb_types_pub.h"
+
+// Uncomment AVB_LOG_ON to enable logging.
+#define AVB_LOG_ON 1
+
+// Uncomment AVB_LOG_ON_OVERRIDE to override all AVB_LOG_ON usage in the stack to ensure all logs are off.
+//#define AVB_LOG_ON_OVERRIDE 1
+
+#ifdef AVB_LOG_ON_OVERRIDE
+#ifdef AVB_LOG_ON
+#undef AVB_LOG_ON
+#endif
+#endif
+
+#define AVB_LOG_LEVEL_NONE 0
+#define AVB_LOG_LEVEL_ERROR 1
+#define AVB_LOG_LEVEL_WARNING 2
+#define AVB_LOG_LEVEL_INFO 3
+#define AVB_LOG_LEVEL_STATUS 4
+#define AVB_LOG_LEVEL_DEBUG 5
+#define AVB_LOG_LEVEL_VERBOSE 6
+
+// Special case development logging levels for use with AVB_LOGF_DEV and AVB_LOG_DEV
+#define AVB_LOG_LEVEL_DEV_ON AVB_LOG_LEVEL_NONE
+#define AVB_LOG_LEVEL_DEV_OFF AVB_LOG_LEVEL_VERBOSE + 1
+
+// Default log level, can override in source files
+#ifndef AVB_LOG_LEVEL
+//#define AVB_LOG_LEVEL AVB_LOG_LEVEL_ERROR
+//#define AVB_LOG_LEVEL AVB_LOG_LEVEL_INFO
+#define AVB_LOG_LEVEL AVB_LOG_LEVEL_STATUS
+//#define AVB_LOG_LEVEL AVB_LOG_LEVEL_DEBUG
+//#define AVB_LOG_LEVEL AVB_LOG_LEVEL_VERBOSE
+#endif
+
+#ifndef AVB_LOG_COMPANY
+#define AVB_LOG_COMPANY "OPENAVB"
+#endif
+
+#ifndef AVB_LOG_COMPONENT
+#define AVB_LOG_COMPONENT "AVB Stack"
+#endif
+
+// Log format options and sizes. Uncomment to include the formatted info.
+#define LOG_MSG_LEN 1024
+
+// The length of the full message
+#define LOG_FULL_MSG_LEN 1024
+
+static const bool OPENAVB_LOG_TIME_INFO = FALSE;
+#define LOG_TIME_LEN 9
+//#define LOG_TIME_LEN 1
+
+static const bool OPENAVB_LOG_TIMESTAMP_INFO = TRUE;
+#define LOG_TIMESTAMP_LEN 32
+//#define LOG_TIMESTAMP_LEN 1
+
+static const bool OPENAVB_LOG_FILE_INFO = FALSE;
+//#define LOG_FILE_LEN 256
+#define LOG_FILE_LEN 1
+
+static const bool OPENAVB_LOG_PROC_INFO = FALSE;
+//#define LOG_PROC_LEN 64
+#define LOG_PROC_LEN 1
+
+static const bool OPENAVB_LOG_THREAD_INFO = FALSE;
+//#define LOG_THREAD_LEN 64
+#define LOG_THREAD_LEN 1
+
+#define LOG_RT_MSG_LEN 256
+//#define LOG_RT_MSG_LEN 1
+
+#define AVB_LOG_OUTPUT_FD stderr
+//#define AVB_LOG_OUTPUT_FD stdout
+
+// When OPENAVB_LOG_FROM_THREAD the message output will be output from a separate thread/task
+// Primary intended use is for debugging.
+// It is expected that OPENAVB_LOG_PULL_MODE will not be used at the same time as this optoin.
+static const bool OPENAVB_LOG_FROM_THREAD = TRUE;
+
+// When OPENAVB_LOG_PULL_MODE the messages will be queued and can be pulled using the
+// avbLogGetMsg() call. This could be from an logger interface module or host application.
+// It is expected that OPENAVB_LOG_FROM_THREAD will not be used at the same time as this option.
+static const bool OPENAVB_LOG_PULL_MODE = FALSE;
+
+// When using the OPENAVB_LOG_FROM_THREAD option. These defines control the behavior of the msg queue
+#define LOG_QUEUE_MSG_LEN 256
+#define LOG_QUEUE_MSG_SIZE (LOG_QUEUE_MSG_LEN + 1)
+#define LOG_QUEUE_MSG_CNT 82
+#define LOG_QUEUE_SLEEP_MSEC 100
+
+// RT (RealTime logging) related defines
+#define LOG_RT_QUEUE_CNT 128
+#define LOG_RT_BEGIN TRUE
+#define LOG_RT_ITEM TRUE
+#define LOG_RT_END TRUE
+typedef enum {
+ LOG_RT_DATATYPE_NONE,
+ LOG_RT_DATATYPE_CONST_STR,
+ LOG_RT_DATATYPE_NOW_TS,
+ LOG_RT_DATATYPE_U16,
+ LOG_RT_DATATYPE_S16,
+ LOG_RT_DATATYPE_U32,
+ LOG_RT_DATATYPE_S32,
+ LOG_RT_DATATYPE_U64,
+ LOG_RT_DATATYPE_S64,
+ LOG_RT_DATATYPE_FLOAT
+} log_rt_datatype_t;
+
+
+#define LOG_VARX(x, y) x ## y
+#define LOG_VAR(x, y) LOG_VARX(x, y)
+
+// Log a message once. Technically once every 4.2 billion attempts. Usage: LOG_ONCE AVB_LOG_INFO(...)
+#define IF_LOG_ONCE() static U32 LOG_VAR(logOnce,__LINE__); if (!LOG_VAR(logOnce,__LINE__)++)
+
+// Log a message at an interval. Usage: LOG_INTERVAL(100) AVB_LOG_INFO(...)
+#define IF_LOG_INTERVAL(x) static U32 LOG_VAR(logOnce,__LINE__); if (!(LOG_VAR(logOnce,__LINE__)++ % (x - 1)))
+
+
+#define ETH_FORMAT "%02x:%02x:%02x:%02x:%02x:%02x"
+#define ETH_OCTETS(a) (a)[0],(a)[1],(a)[2],(a)[3],(a)[4],(a)[5]
+
+#define STREAMID_FORMAT "%02x:%02x:%02x:%02x:%02x:%02x/%d"
+#define STREAMID_ARGS(s) (s)->addr[0],(s)->addr[1],(s)->addr[2],(s)->addr[3],(s)->addr[4],(s)->addr[5],(s)->uniqueID
+
+void avbLogInit(void);
+
+void avbLogExit(void);
+
+void avbLogFn(
+ int level,
+ const char *tag,
+ const char *company,
+ const char *component,
+ const char *path,
+ int line,
+ const char *fmt,
+ ...);
+
+void avbLogRT(int level, bool bBegin, bool bItem, bool bEnd, char *pFormat, log_rt_datatype_t dataType, void *pVar);
+
+
+#define avbLogFn2(level, tag, company, component, path, line, fmt, ...) \
+ ({\
+ if (level <= AVB_LOG_LEVEL) \
+ avbLogFn(0, tag, company, component, path, line, fmt, __VA_ARGS__); \
+ })
+
+#ifdef AVB_LOG_ON
+#define AVB_LOGF_DEV(LEVEL, FMT, ...) avbLogFn2(LEVEL, "DEV", AVB_LOG_COMPANY, AVB_LOG_COMPONENT, __FILE__, __LINE__, FMT, __VA_ARGS__)
+#define AVB_LOGF_ERROR(FMT, ...) avbLogFn2(AVB_LOG_LEVEL_ERROR, "ERROR", AVB_LOG_COMPANY, AVB_LOG_COMPONENT, __FILE__, __LINE__, FMT, __VA_ARGS__)
+#define AVB_LOGF_WARNING(FMT, ...) avbLogFn2(AVB_LOG_LEVEL_WARNING, "WARNING", AVB_LOG_COMPANY, AVB_LOG_COMPONENT, __FILE__, __LINE__, FMT, __VA_ARGS__)
+#define AVB_LOGF_INFO(FMT, ...) avbLogFn2(AVB_LOG_LEVEL_INFO, "INFO", AVB_LOG_COMPANY, AVB_LOG_COMPONENT, __FILE__, __LINE__, FMT, __VA_ARGS__)
+#define AVB_LOGF_STATUS(FMT, ...) avbLogFn2(AVB_LOG_LEVEL_STATUS, "STATUS", AVB_LOG_COMPANY, AVB_LOG_COMPONENT, __FILE__, __LINE__, FMT, __VA_ARGS__)
+#define AVB_LOGF_DEBUG(FMT, ...) avbLogFn2(AVB_LOG_LEVEL_DEBUG, "DEBUG", AVB_LOG_COMPANY, AVB_LOG_COMPONENT, __FILE__, __LINE__, FMT, __VA_ARGS__)
+#define AVB_LOGF_VERBOSE(FMT, ...) avbLogFn2(AVB_LOG_LEVEL_VERBOSE, "VERBOSE", AVB_LOG_COMPANY, AVB_LOG_COMPONENT, __FILE__, __LINE__, FMT, __VA_ARGS__)
+#define AVB_LOG_DEV(LEVEL, FMT, ...) avbLogFn2(LEVEL, "DEV", AVB_LOG_COMPANY, AVB_LOG_COMPONENT, __FILE__, __LINE__, FMT, __VA_ARGS__)
+#define AVB_LOG_ERROR(MSG) avbLogFn2(AVB_LOG_LEVEL_ERROR, "ERROR", AVB_LOG_COMPANY, AVB_LOG_COMPONENT, __FILE__, __LINE__, "%s", MSG)
+#define AVB_LOG_WARNING(MSG) avbLogFn2(AVB_LOG_LEVEL_WARNING, "WARNING", AVB_LOG_COMPANY, AVB_LOG_COMPONENT, __FILE__, __LINE__, "%s", MSG)
+#define AVB_LOG_INFO(MSG) avbLogFn2(AVB_LOG_LEVEL_INFO, "INFO", AVB_LOG_COMPANY, AVB_LOG_COMPONENT, __FILE__, __LINE__, "%s", MSG)
+#define AVB_LOG_STATUS(MSG) avbLogFn2(AVB_LOG_LEVEL_STATUS, "STATUS", AVB_LOG_COMPANY, AVB_LOG_COMPONENT, __FILE__, __LINE__, "%s", MSG)
+#define AVB_LOG_DEBUG(MSG) avbLogFn2(AVB_LOG_LEVEL_DEBUG, "DEBUG", AVB_LOG_COMPANY, AVB_LOG_COMPONENT, __FILE__, __LINE__, "%s", MSG)
+#define AVB_LOG_VERBOSE(MSG) avbLogFn2(AVB_LOG_LEVEL_VERBOSE, "VERBOSE", AVB_LOG_COMPANY, AVB_LOG_COMPONENT, __FILE__, __LINE__, "%s", MSG)
+#define AVB_LOGRT_ERROR(BEGIN, ITEM, END, FMT, TYPE, VAL) avbLogRT(AVB_LOG_LEVEL_ERROR, BEGIN, ITEM, END, FMT, TYPE, VAL)
+#define AVB_LOGRT_WARNING(BEGIN, ITEM, END, FMT, TYPE, VAL) avbLogRT(AVB_LOG_LEVEL_WARNING, BEGIN, ITEM, END, FMT, TYPE, VAL)
+#define AVB_LOGRT_INFO(BEGIN, ITEM, END, FMT, TYPE, VAL) avbLogRT(AVB_LOG_LEVEL_INFO, BEGIN, ITEM, END, FMT, TYPE, VAL)
+#define AVB_LOGRT_STATUS(BEGIN, ITEM, END, FMT, TYPE, VAL) avbLogRT(AVB_LOG_LEVEL_STATUS, BEGIN, ITEM, END, FMT, TYPE, VAL)
+#define AVB_LOGRT_DEBUG(BEGIN, ITEM, END, FMT, TYPE, VAL) avbLogRT(AVB_LOG_LEVEL_DEBUG, BEGIN, ITEM, END, FMT, TYPE, VAL)
+#define AVB_LOGRT_VERBOSE(BEGIN, ITEM, END, FMT, TYPE, VAL) avbLogRT(AVB_LOG_LEVEL_VERBOSE, BEGIN, ITEM, END, FMT, TYPE, VAL)
+#else
+#define AVB_LOGF_DEV(LEVEL, FMT, ...)
+#define AVB_LOGF_ERROR(FMT, ...)
+#define AVB_LOGF_WARNING(FMT, ...)
+#define AVB_LOGF_INFO(FMT, ...)
+#define AVB_LOGF_STATUS(FMT, ...)
+#define AVB_LOGF_DEBUG(FMT, ...)
+#define AVB_LOGF_VERBOSE(FMT, ...)
+#define AVB_LOG_DEV(LEVEL, FMT, ...)
+#define AVB_LOG_ERROR(MSG)
+#define AVB_LOG_WARNING(MSG)
+#define AVB_LOG_INFO(MSG)
+#define AVB_LOG_STATUS(MSG)
+#define AVB_LOG_DEBUG(MSG)
+#define AVB_LOG_VERBOSE(MSG)
+#define AVB_LOGRT_ERROR(BEGIN, ITEM, END, FMT, TYPE, VAL)
+#define AVB_LOGRT_WARNING(BEGIN, ITEM, END, FMT, TYPE, VAL)
+#define AVB_LOGRT_INFO(BEGIN, ITEM, END, FMT, TYPE, VAL)
+#define AVB_LOGRT_STATUS(BEGIN, ITEM, END, FMT, TYPE, VAL)
+#define AVB_LOGRT_DEBUG(BEGIN, ITEM, END, FMT, TYPE, VAL)
+#define AVB_LOGRT_VERBOSE(BEGIN, ITEM, END, FMT, TYPE, VAL)
+#endif // AVB_LOG_ON
+
+// Get a queued log message. Intended to be used with the OPENAVB_LOG_PULL_MODE option.
+// Message will not be null terminated.
+U32 avbLogGetMsg(U8 *pBuf, U32 bufSize);
+
+#endif // OPENAVB_LOG_PUB_H
diff --git a/lib/avtp_pipeline/include/openavb_map_pub.h b/lib/avtp_pipeline/include/openavb_map_pub.h new file mode 100755 index 00000000..3eb1afc0 --- /dev/null +++ b/lib/avtp_pipeline/include/openavb_map_pub.h @@ -0,0 +1,238 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Common mapper module public interface +*/ + +#ifndef OPENAVB_MAP_PUB_H +#define OPENAVB_MAP_PUB_H 1 + +#include "openavb_types_pub.h" +#include "openavb_mediaq_pub.h" + +/** \file + * Common mapper module public interface. + */ + +// Vendor (OPENAVB) Specific formats used AVTP headers. +#define MAP_NULL_OPENAVB_FORMAT 0x00 +#define MAP_PIPE_OPENAVB_FORMAT 0x01 + +// Vendor (OPENAVB) Specific CTRL formats used AVTP . +#define MAP_CTRL_OPENAVB_FORMAT 0x00 + +/** Return value of talker callback. + */ +typedef enum { + TX_CB_RET_PACKET_NOT_READY = 0, ///< Packet will not be sent on this callback interval + TX_CB_RET_PACKET_READY, ///< Packet will be sent on this callback interal. + TX_CB_RET_MORE_PACKETS ///< Packet will be sent and the callback called immediately again. +} tx_cb_ret_t; + +/** Configuration callback. + * + * Each configuration name value pair for this mapping will result in this + * callback being called. + * \param pMediaQ A pointer to the media queue for this stream + * \param name configuration item name + * \param value configuration item value + */ +typedef void (*openavb_map_cfg_cb_t)(media_q_t *pMediaQ, const char *name, const char *value); + +/** AVB subtype callback. + * + * \return The AVB subtype for this mapping. + */ +typedef U8(*openavb_map_subtype_cb_t)(); + +/** AVTP version callback. + * + * \return The AVTP version for this mapping. + */ +typedef U8(*openavb_map_avtp_version_cb_t)(); + +/** Maximum data size callback. + * + * \param pMediaQ A pointer to the media queue for this stream + * \return The maximum data size that will be used. + */ +typedef U16(*openavb_map_max_data_size_cb_t)(media_q_t *pMediaQ); + +/** Transmit interval callback. + * + * \param pMediaQ A pointer to the media queue for this stream + * \return The intended transmit interval (in frames per second). + * 0 = default for talker / class. + */ +typedef U32(*openavb_map_transmit_interval_cb_t)(media_q_t *pMediaQ); + +/** General initialize callback regardless if a talker or listener. + * + * Called once during openavbTLOpen(). + * \param pMediaQ A pointer to the media queue for this stream + */ +typedef void (*openavb_map_gen_init_cb_t)(media_q_t *pMediaQ); + +/** AVDECC initialize callback for both a talker or listener. + * + * Entity model based configuration can be processed at this time. This + * callback is optional and only executed when AVDECC is used to connect + * streams. The descriptorType is expected to be of STREAM_INPUT for listeners + * and STREAM_OUTPUT for talkers. + * + * \param pMediaQ A pointer to the media queue for this stream + * \param configIdx current configuration descriptor index for the Entity Model + * \param descriptorType The descriptorType is expected to be of STREAM_INPUT + * for listeners and STREAM_OUTPUT for talkers. + * \param descriptorIdx descriptor index in the Entity Model + * \see openavb_intf_avdecc_init_cb_t + */ +typedef void (*openavb_map_avdecc_init_cb_t)(media_q_t *pMediaQ, U16 configIdx, U16 descriptorType, U16 descriptorIdx); + +/** A call to this callback indicates that this mapping module will be a talker. + * + * Any talker initialization can be done in this function. + * \param pMediaQ A pointer to the media queue for this stream + */ +typedef void (*openavb_map_tx_init_cb_t)(media_q_t *pMediaQ); + +/** This talker callback will be called for each AVB observation interval. + * + * \param pMediaQ A pointer to the media queue for this stream + * \param pData pointer to data + * \param dataLen length of data + * \return One of enum \ref tx_cb_ret_t values. + */ +typedef tx_cb_ret_t(*openavb_map_tx_cb_t)(media_q_t *pMediaQ, U8 *pData, U32 *datalen); + +/** A call to this callback indicates that this mapping module will be + * a listener. + * + * Any listener initialization can be done in this function. + * \param pMediaQ A pointer to the media queue for this stream + */ +typedef void (*openavb_map_rx_init_cb_t)(media_q_t *pMediaQ); + +/** This callback occurs when running as a listener and data is available. + * + * \param pMediaQ A pointer to the media queue for this stream + * \param pData pointer to data + * \param dataLen length of data + */ +typedef bool (*openavb_map_rx_cb_t)(media_q_t *pMediaQ, U8 *pData, U32 datalen); + +/** This callback will be called when the stream is closing. + * + * \param pMediaQ A pointer to the media queue for this stream + */ +typedef void (*openavb_map_end_cb_t)(media_q_t *pMediaQ); + +/** General shutdown callback regardless if a talker or listener. + * + * Called once during openavbTLClose(). + * \param pMediaQ A pointer to the media queue for this stream + */ +typedef void (*openavb_map_gen_end_cb_t)(media_q_t *pMediaQ); + +/** Set source bitrate callback. + * + * Used to inform mapping module on the bitrate of the data source (computed by + * the interface module). The reported bitrate is used by the + * openavb_map_get_max_interval_frames_cb_t() callback. + * \param pMediaQ A pointer to the media queue for this stream + * \param bitrate Data source bitrate + * + * \note This callback is optional, does not need to be implemented in the + * mapping module. + */ +typedef void (*openavb_map_set_src_bitrate_cb_t)(media_q_t *pMediaQ, unsigned int bitrate); + +/** Get max interval frames. + * + * Called to get the maximum number of frames per interval for the talker. The + * calculation is based on the source bitrate provided in the call to + * openavb_map_set_src_bitrate_cb_t(). Will override ini file "max_interval_frames" + * configuration option. + * \param pMediaQ A pointer to the media queue for this stream + * \param sr_class Stream reservation class + * \return Maximum number of frames per interval + * + * \note This callback is optional, does not need to be implemented in the + * mapping module. + */ +typedef unsigned int (*openavb_map_get_max_interval_frames_cb_t)(media_q_t *pMediaQ, SRClassIdx_t sr_class); + +/** Mapping callbacks structure. + */ +typedef struct { + /// Configuration callback. + openavb_map_cfg_cb_t map_cfg_cb; + /// AVB subtype callback. + openavb_map_subtype_cb_t map_subtype_cb; + /// AVTP version callback. + openavb_map_avtp_version_cb_t map_avtp_version_cb; + /// Maximum data size callback. + openavb_map_max_data_size_cb_t map_max_data_size_cb; + /// Transmit interval callback. + openavb_map_transmit_interval_cb_t map_transmit_interval_cb; + /// General initialize callback. + openavb_map_gen_init_cb_t map_gen_init_cb; + /// AVDECC initialize callback. + openavb_map_avdecc_init_cb_t map_avdecc_init_cb; + /// Initialize transmit callback. + openavb_map_tx_init_cb_t map_tx_init_cb; + /// Transmit callback. + openavb_map_tx_cb_t map_tx_cb; + /// Initialize receive callback. + openavb_map_rx_init_cb_t map_rx_init_cb; + /// Receive callback. + openavb_map_rx_cb_t map_rx_cb; + /// Stream end callback. + openavb_map_end_cb_t map_end_cb; + /// General shutdown callback. + openavb_map_gen_end_cb_t map_gen_end_cb; + /// Set source bit rate callback. + openavb_map_set_src_bitrate_cb_t map_set_src_bitrate_cb; + /// Max interval frames callback. + openavb_map_get_max_interval_frames_cb_t map_get_max_interval_frames_cb; +} openavb_map_cb_t; + +/** Main initialization entry point into the mapping module. + * + * \param pMediaQ A pointer to the media queue for this stream + * \param[out] pMapCB Pointer to the callback structure. All the members of this + * structure must be set during this function call. + * \param inMaxTransitUsec maximum expected latency. + */ +typedef bool (*openavb_map_initialize_fn_t)(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); + +#endif // OPENAVB_MAP_PUB_H + diff --git a/lib/avtp_pipeline/include/openavb_platform.h b/lib/avtp_pipeline/include/openavb_platform.h new file mode 100644 index 00000000..6aa44de8 --- /dev/null +++ b/lib/avtp_pipeline/include/openavb_platform.h @@ -0,0 +1,47 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE : Interface for platform functionality +* Each platform is comprised of a HAL and an OSAL layer. +* The build scripts must correctly establish the include +* paths for each of these. +*/ + +#ifndef _OPENAVB_PLATFORM_H +#define _OPENAVB_PLATFORM_H + +#include "openavb_types_base.h" +#include "openavb_osal.h" +#include "openavb_mem_tcal.h" +#include <stdint.h> +#include "openavb_types.h" + +#endif // _OPENAVB_PLATFORM_H diff --git a/lib/avtp_pipeline/include/openavb_platform_pub.h b/lib/avtp_pipeline/include/openavb_platform_pub.h new file mode 100644 index 00000000..a8d1b02a --- /dev/null +++ b/lib/avtp_pipeline/include/openavb_platform_pub.h @@ -0,0 +1,42 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+/*
+* MODULE : Public interface for platform specific functionality
+*/
+
+#ifndef _OPENAVB_PLATFORM_PUB_H
+#define _OPENAVB_PLATFORM_PUB_H
+
+#include "openavb_types_pub.h"
+#include "openavb_osal_pub.h"
+#include "openavb_mem_tcal_pub.h"
+
+#endif // _OPENAVB_PLATFORM_PUB_H
diff --git a/lib/avtp_pipeline/include/openavb_pub.h b/lib/avtp_pipeline/include/openavb_pub.h new file mode 100644 index 00000000..d3b72c99 --- /dev/null +++ b/lib/avtp_pipeline/include/openavb_pub.h @@ -0,0 +1,87 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : General header file for the AVB stack +*/ + +#ifndef AVB_PUB_H +#define AVB_PUB_H 1 + +#include "openavb_log_pub.h" + +///////////////////////////////////////////////////////// +// AVB Core version related macros +// +// These must NOT be edited for project related work +///////////////////////////////////////////////////////// +#define AVB_CORE_NAME "OpenAVB AVTP Pipeline" + +#define AVB_CORE_VER_MAJOR (0) +#define AVB_CORE_VER_MINOR (1) +#define AVB_CORE_VER_REVISION (0) + +#define AVB_CORE_VER_FULL (AVB_CORE_VER_MAJOR << 16 | AVB_CORE_VER_MINOR << 8 | AVB_CORE_VER_REVISION) + +// Standard release designations. Uncomment one AVB_RELEASE_TYPE +#define AVB_CORE_RELEASE_TYPE "Development" +//#define AVB_CORE_RELEASE_TYPE "Alpha" +//#define AVB_CORE_RELEASE_TYPE "Beta" +//#define AVB_CORE_RELEASE_TYPE "Release" + +#define LOG_EAVB_CORE_VERSION() AVB_LOGF_INFO("%s: %i.%i.%i (%s)", AVB_CORE_NAME, AVB_CORE_VER_MAJOR, AVB_CORE_VER_MINOR, AVB_CORE_VER_REVISION, AVB_CORE_RELEASE_TYPE) + + + +///////////////////////////////////////////////////////// +// AVB Project version related macros +// +// These can be used for project solutions +///////////////////////////////////////////////////////// +#define AVB_PROJECT_NAME "Sample AVB Solution" + +#define AVB_PROJECT_VER_MAJOR (1) +#define AVB_PROJECT_VER_MINOR (0) +#define AVB_PROJECT_VER_REVISION (0) + +#define AVB_PROJECT_VER_FULL (AVB_PROJECT_VER_MAJOR << 16 | AVB_PROJECT_VER_MINOR << 8 | AVB_PROJECT_VER_REVISION) + +// Standard release designations. Uncomment one AVB_RELEASE_TYPE +#define AVB_PROJECT_RELEASE_TYPE "Development" +//#define AVB_PROJECT_RELEASE_TYPE "Alpha" +//#define AVB_PROJECT_RELEASE_TYPE "Beta" +//#define AVB_PROJECT_RELEASE_TYPE "Release" + +#define LOG_EAVB_PROJECT_VERSION() AVB_LOGF_INFO("%s: %i.%i.%i (%s)", AVB_PROJECT_NAME, AVB_PROJECT_VER_MAJOR, AVB_PROJECT_VER_MINOR, AVB_PROJECT_VER_REVISION, AVB_PROJECT_RELEASE_TYPE) + + + + +#endif // AVB_PUB_H diff --git a/lib/avtp_pipeline/include/openavb_trace.h b/lib/avtp_pipeline/include/openavb_trace.h new file mode 100644 index 00000000..140c2d0e --- /dev/null +++ b/lib/avtp_pipeline/include/openavb_trace.h @@ -0,0 +1,78 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Provide a simple tracing facility for use +* during development. +* +* Based off of the openavb_log facility. +* +* NOTE: The Tracing uses a high resolution timer and therefore +* librt must be linked into any application that uses this +* facility. +*/ + +#ifndef AVB_TRACE_H +#define AVB_TRACE_H 1 + +#include "openavb_trace_pub.h" + +// Features to trace. 1 is enabled, 0 is disabled. +#define AVB_TRACE_ACMP 1 +#define AVB_TRACE_ADP 1 +#define AVB_TRACE_AECP 1 +#define AVB_TRACE_AEM 1 +#define AVB_TRACE_AVDECC 1 +#define AVB_TRACE_AVTP 1 +#define AVB_TRACE_AVTP_DETAIL 1 +#define AVB_TRACE_AVTP_TIME 1 +#define AVB_TRACE_AVTP_TIME_DETAIL 1 +#define AVB_TRACE_ENDPOINT 1 +#define AVB_TRACE_MEDIAQ 1 +#define AVB_TRACE_MEDIAQ_DETAIL 1 +#define AVB_TRACE_QUEUE_MANAGER 1 +#define AVB_TRACE_MAAP 1 +#define AVB_TRACE_RAWSOCK 1 +#define AVB_TRACE_RAWSOCK_DETAIL 1 +#define AVB_TRACE_SRP_PUBLIC 1 +#define AVB_TRACE_SRP_PRIVATE 1 +#define AVB_TRACE_TL 1 +#define AVB_TRACE_TL_DETAIL 1 +#define AVB_TRACE_PTP 1 +#define AVB_TRACE_FQTSS 1 +#define AVB_TRACE_FQTSS_DETAIL 1 +#define AVB_TRACE_HR_TMR 1 +#define AVB_TRACE_NANOSLEEP 1 +#define AVB_TRACE_TIME 1 +#define AVB_TRACE_HAL_ETHER 1 +#define AVB_TRACE_HAL_ETHER_DETAIL 1 +#define AVB_TRACE_HAL_TASK_TIMER 1 +#define AVB_TRACE_DEBUG 1 +#endif // AVB_TRACE_H diff --git a/lib/avtp_pipeline/include/openavb_trace_pub.h b/lib/avtp_pipeline/include/openavb_trace_pub.h new file mode 100644 index 00000000..71e4a275 --- /dev/null +++ b/lib/avtp_pipeline/include/openavb_trace_pub.h @@ -0,0 +1,247 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Provide a simple tracing facility for use +* during development. +* +* Based off of the openavb_log facility. +* +* NOTE: The Tracing uses a high resolution timer and therefore +* librt must be linked into any application that uses this +* facility. +*/ + +#ifndef AVB_TRACE_PUB_H +#define AVB_TRACE_PUB_H 1 + +#include "openavb_platform_pub.h" +#include <stdio.h> +#include "openavb_types_pub.h" + +// Uncomment AVB_TRACE_ON to enable tracing. +//#define AVB_TRACE_ON 1 + +// Specific reporting modes +#define AVB_TRACE_MODE_NONE 0 +#define AVB_TRACE_MODE_MINIMAL 1 +#define AVB_TRACE_MODE_NORMAL 2 +#define AVB_TRACE_MODE_DELTA_TIME 3 +#define AVB_TRACE_MODE_DELTA_STATS 4 +#define AVB_TRACE_MODE_FUNC_TIME 5 +#define AVB_TRACE_MODE_DOC 6 + +// One of the above reporting modes must be set here +#define AVB_TRACE_MODE AVB_TRACE_MODE_NORMAL + +// Delta Stats Interval +#define AVB_TRACE_OPT_DELTA_STATS_INTERVAL 80000 + +// Function Time interval +#define AVB_TRACE_OPT_FUNC_TIME_INTERVAL 80000 +//#define AVB_TRACE_OPT_FUNC_TIME_INTERVAL 100 + +// Option to show file name +//#define AVB_TRACE_OPT_SHOW_FILE_NAME 1 + + +//#define AVB_TRACE_PRINTF(FMT, ...) fprintf(stderr, FMT, __VA_ARGS__) +//#define AVB_TRACE_PRINTF(FMT, ...) fprintf(stdout, FMT, __VA_ARGS__) +#define AVB_TRACE_PRINTF(FMT, ...) printf(FMT, __VA_ARGS__) + + +// Features to trace. 1 is enabled, 0 is disabled. +#define AVB_TRACE_MAP 1 +#define AVB_TRACE_MAP_DETAIL 1 +#define AVB_TRACE_MAP_LINE 1 +#define AVB_TRACE_INTF 1 +#define AVB_TRACE_INTF_DETAIL 1 +#define AVB_TRACE_INTF_LINE 1 +#define AVB_TRACE_HOST 1 + +#define TRACE_VAR1(x, y) x ## y +#define TRACE_VAR2(x, y) TRACE_VAR1(x, y) + +#ifdef AVB_TRACE_OPT_SHOW_FILE_NAME +#define TRACE_FILE_NAME __FILE__ +#else +#define TRACE_FILE_NAME "" +#endif + + +#if !defined(AVB_TRACE_ON) || (AVB_TRACE_MODE == AVB_TRACE_MODE_NONE) +#define AVB_TRACE_LINE(FEATURE) +#define AVB_TRACE_ENTRY(FEATURE) +#define AVB_TRACE_EXIT(FEATURE) +#define AVB_TRACE_LOOP_ENTRY(FEATURE) +#define AVB_TRACE_LOOP_EXIT(FEATURE) +#elif (AVB_TRACE_MODE == AVB_TRACE_MODE_MINIMAL) +#define AVB_TRACE_LINE(FEATURE) avbTraceMinimalFn(FEATURE, "TRACE LINE ", __FUNCTION__, TRACE_FILE_NAME, __LINE__) +#define AVB_TRACE_ENTRY(FEATURE) avbTraceMinimalFn(FEATURE, "TRACE ENTRY", __FUNCTION__, TRACE_FILE_NAME, __LINE__) +#define AVB_TRACE_EXIT(FEATURE) avbTraceMinimalFn(FEATURE, "TRACE EXIT ", __FUNCTION__, TRACE_FILE_NAME, __LINE__) +#define AVB_TRACE_LOOP_ENTRY(FEATURE) avbTraceMinimalFn(FEATURE, "TRACE LOOP{", __FUNCTION__, TRACE_FILE_NAME, __LINE__) +#define AVB_TRACE_LOOP_EXIT(FEATURE) avbTraceMinimalFn(FEATURE, "TRACE LOOP}", __FUNCTION__, TRACE_FILE_NAME, __LINE__) +#elif (AVB_TRACE_MODE == AVB_TRACE_MODE_NORMAL) +#define AVB_TRACE_LINE(FEATURE) avbTraceNormalFn(FEATURE, "TRACE LINE ", __FUNCTION__, TRACE_FILE_NAME, __LINE__) +#define AVB_TRACE_ENTRY(FEATURE) avbTraceNormalFn(FEATURE, "TRACE ENTRY", __FUNCTION__, TRACE_FILE_NAME, __LINE__) +#define AVB_TRACE_EXIT(FEATURE) avbTraceNormalFn(FEATURE, "TRACE EXIT ", __FUNCTION__, TRACE_FILE_NAME, __LINE__) +#define AVB_TRACE_LOOP_ENTRY(FEATURE) avbTraceNormalFn(FEATURE, "TRACE LOOP{", __FUNCTION__, TRACE_FILE_NAME, __LINE__) +#define AVB_TRACE_LOOP_EXIT(FEATURE) avbTraceNormalFn(FEATURE, "TRACE LOOP}", __FUNCTION__, TRACE_FILE_NAME, __LINE__) +#elif (AVB_TRACE_MODE == AVB_TRACE_MODE_DELTA_TIME) +#define AVB_TRACE_LINE(FEATURE) avbTraceDeltaTimeFn(FEATURE, "TRACE LINE ", __FUNCTION__, TRACE_FILE_NAME, __LINE__, NULL, NULL) +#define AVB_TRACE_ENTRY(FEATURE) avbTraceDeltaTimeFn(FEATURE, "TRACE ENTRY", __FUNCTION__, TRACE_FILE_NAME, __LINE__, NULL, NULL) +#define AVB_TRACE_EXIT(FEATURE) avbTraceDeltaTimeFn(FEATURE, "TRACE EXIT ", __FUNCTION__, TRACE_FILE_NAME, __LINE__, NULL, NULL) +#define AVB_TRACE_LOOP_ENTRY(FEATURE) avbTraceDeltaTimeFn(FEATURE, "TRACE LOOP{", __FUNCTION__, TRACE_FILE_NAME, __LINE__, NULL, NULL) +#define AVB_TRACE_LOOP_EXIT(FEATURE) avbTraceDeltaTimeFn(FEATURE, "TRACE LOOP}", __FUNCTION__, TRACE_FILE_NAME, __LINE__, NULL, NULL) +#elif (AVB_TRACE_MODE == AVB_TRACE_MODE_DELTA_STATS) +#define AVB_TRACE_LINE(FEATURE) static U64 TRACE_VAR2(traceCnt,__LINE__); static U64 TRACE_VAR2(traceNSec, __LINE__); avbTraceDeltaTimeFn(FEATURE, "TRACE LINE ", __FUNCTION__, TRACE_FILE_NAME, __LINE__, &TRACE_VAR2(traceCnt,__LINE__), &TRACE_VAR2(traceNSec, __LINE__)) +#define AVB_TRACE_ENTRY(FEATURE) static U64 TRACE_VAR2(traceCnt,__LINE__); static U64 TRACE_VAR2(traceNSec, __LINE__); avbTraceDeltaTimeFn(FEATURE, "TRACE ENTRY", __FUNCTION__, TRACE_FILE_NAME, __LINE__, &TRACE_VAR2(traceCnt,__LINE__), &TRACE_VAR2(traceNSec, __LINE__)) +#define AVB_TRACE_EXIT(FEATURE) static U64 TRACE_VAR2(traceCnt,__LINE__); static U64 TRACE_VAR2(traceNSec, __LINE__); avbTraceDeltaTimeFn(FEATURE, "TRACE EXIT ", __FUNCTION__, TRACE_FILE_NAME, __LINE__, &TRACE_VAR2(traceCnt,__LINE__), &TRACE_VAR2(traceNSec, __LINE__)) +#define AVB_TRACE_LOOP_ENTRY(FEATURE) static U64 TRACE_VAR2(traceCnt,__LINE__); static U64 TRACE_VAR2(traceNSec, __LINE__); avbTraceDeltaTimeFn(FEATURE, "TRACE LOOP{", __FUNCTION__, TRACE_FILE_NAME, __LINE__, &TRACE_VAR2(traceCnt,__LINE__), &TRACE_VAR2(traceNSec, __LINE__)) +#define AVB_TRACE_LOOP_EXIT(FEATURE) static U64 TRACE_VAR2(traceCnt,__LINE__); static U64 TRACE_VAR2(traceNSec, __LINE__); avbTraceDeltaTimeFn(FEATURE, "TRACE LOOP}", __FUNCTION__, TRACE_FILE_NAME, __LINE__, &TRACE_VAR2(traceCnt,__LINE__), &TRACE_VAR2(traceNSec, __LINE__)) +#elif (AVB_TRACE_MODE == AVB_TRACE_MODE_FUNC_TIME) +#define AVB_TRACE_LINE(FEATURE) +#define AVB_TRACE_ENTRY(FEATURE) static struct timespec TRACE_VAR2(tsFuncEntry,__FUNCTION__); avbTraceFuncTimeFn(FEATURE, TRUE, __FUNCTION__, TRACE_FILE_NAME, __LINE__, &TRACE_VAR2(tsFuncEntry,__FUNCTION__), NULL, NULL) +#define AVB_TRACE_EXIT(FEATURE) static U64 TRACE_VAR2(traceCnt,__LINE__); static U64 TRACE_VAR2(traceNSec, __LINE__); avbTraceFuncTimeFn(FEATURE, FALSE, __FUNCTION__, TRACE_FILE_NAME, __LINE__, &TRACE_VAR2(tsFuncEntry,__FUNCTION__), &TRACE_VAR2(traceCnt,__LINE__), &TRACE_VAR2(traceNSec, __LINE__)) +#define AVB_TRACE_LOOP_ENTRY(FEATURE) static struct timespec TRACE_VAR2(tsLoopEntry,__FUNCTION__); avbTraceFuncTimeFn(FEATURE, TRUE, __FUNCTION__, TRACE_FILE_NAME, __LINE__, &TRACE_VAR2(tsLoopEntry,__FUNCTION__), NULL, NULL) +#define AVB_TRACE_LOOP_EXIT(FEATURE) static U64 TRACE_VAR2(traceCnt,__LINE__); static U64 TRACE_VAR2(traceNSec, __LINE__); avbTraceFuncTimeFn(FEATURE, FALSE, __FUNCTION__, TRACE_FILE_NAME, __LINE__, &TRACE_VAR2(tsLoopEntry,__FUNCTION__), &TRACE_VAR2(traceCnt,__LINE__), &TRACE_VAR2(traceNSec, __LINE__)) +#elif (AVB_TRACE_MODE == AVB_TRACE_MODE_DOC) +#define AVB_TRACE_LINE(FEATURE) avbTraceDocFn(FEATURE, "|", __FUNCTION__, TRACE_FILE_NAME, __LINE__, NULL, NULL) +#define AVB_TRACE_ENTRY(FEATURE) avbTraceDocFn(FEATURE, ">", __FUNCTION__, TRACE_FILE_NAME, __LINE__, NULL, NULL) +#define AVB_TRACE_EXIT(FEATURE) avbTraceDocFn(FEATURE, "<", __FUNCTION__, TRACE_FILE_NAME, __LINE__, NULL, NULL) +#define AVB_TRACE_LOOP_ENTRY(FEATURE) +#define AVB_TRACE_LOOP_EXIT(FEATURE) +#endif + +#ifdef AVB_TRACE_ON + +static inline void avbTraceMinimalFn(int featureOn, const char *tag, const char *function, const char *file, int line) +{ + if (featureOn) { + AVB_TRACE_PRINTF("%s: %s():%d %s\n", tag, function, line, file); + } +} + +static inline void avbTraceNormalFn(int featureOn, const char *tag, const char *function, const char *file, int line) +{ + if (featureOn) { + time_t tNow = time(NULL); + struct tm tmNow; + localtime_r(&tNow, &tmNow); + + AVB_TRACE_PRINTF("[%2.2d:%2.2d:%2.2d] [P:%5.5d T:%lu] %s: %s():%d %s\n", + tmNow.tm_hour, tmNow.tm_min, tmNow.tm_sec, GET_PID(), THREAD_SELF(), tag, function, line, file); + } +} + +static inline void avbTraceDeltaTimeFn(int featureOn, const char *tag, const char *function, const char *file, int line, U64 *pCnt, U64 *pNSec) +{ + if (featureOn) { + static struct timespec traceDeltaTimePrevTS; + + struct timespec nowTS; + CLOCK_GETTIME(OPENAVB_CLOCK_REALTIME, &nowTS); + + time_t tNow = time(NULL); + struct tm tmNow; + localtime_r(&tNow, &tmNow); + + U64 prevNSec = ((U64)traceDeltaTimePrevTS.tv_sec * (U64)NANOSECONDS_PER_SECOND) + (U64)traceDeltaTimePrevTS.tv_nsec; + U64 nowNSec = ((U64)nowTS.tv_sec * (U64)NANOSECONDS_PER_SECOND) + (U64)nowTS.tv_nsec; + U64 deltaNSec = nowNSec - prevNSec; + + if (pCnt && pNSec) { + // Delta Stats + (*pCnt)++; + *pNSec += deltaNSec; + + if (*pCnt % AVB_TRACE_OPT_DELTA_STATS_INTERVAL == 0) { + AVB_TRACE_PRINTF("[%2.2d:%2.2d:%2.2d] [P:%5.5d T:%lu] [cnt:%6llu deltaUS:%10llu totalUS:%10llu] %s: %s():%d %s\n", + tmNow.tm_hour, tmNow.tm_min, tmNow.tm_sec, GET_PID(), THREAD_SELF(), (unsigned long long)(*pCnt), (unsigned long long)(deltaNSec / 1000), (unsigned long long)(*pNSec / 1000), tag, function, line, file); + } + } + else { + AVB_TRACE_PRINTF("[%2.2d:%2.2d:%2.2d] [P:%5.5d T:%lu] [deltaUS:%10llu] %s: %s():%d %s\n", + tmNow.tm_hour, tmNow.tm_min, tmNow.tm_sec, GET_PID(), THREAD_SELF(), (unsigned long long)(deltaNSec / 1000), tag, function, line, file); + } + + CLOCK_GETTIME(OPENAVB_CLOCK_REALTIME, &traceDeltaTimePrevTS); + } +} + +static inline void avbTraceFuncTimeFn(int featureOn, bool bEntry, const char *function, const char *file, int line, struct timespec *pTS, U64 *pCnt, U64 *pNSec) +{ + if (featureOn) { + if (bEntry) { + CLOCK_GETTIME(OPENAVB_CLOCK_REALTIME, pTS); + } + else { + struct timespec nowTS; + CLOCK_GETTIME(OPENAVB_CLOCK_REALTIME, &nowTS); + + time_t tNow = time(NULL); + struct tm tmNow; + localtime_r(&tNow, &tmNow); + + U64 entryNSec = ((U64)pTS->tv_sec * (U64)NANOSECONDS_PER_SECOND) + (U64)pTS->tv_nsec; + U64 nowNSec = ((U64)nowTS.tv_sec * (U64)NANOSECONDS_PER_SECOND) + (U64)nowTS.tv_nsec; + U64 deltaNSec = nowNSec - entryNSec; + + (*pCnt)++; + *pNSec += deltaNSec; + + if (*pCnt % AVB_TRACE_OPT_FUNC_TIME_INTERVAL == 0) { + AVB_TRACE_PRINTF("[%2.2d:%2.2d:%2.2d] [P:%5.5d T:%lu] [cnt:%6llu lastUS:%10llu totalUS:%10llu avgUS:%10llu] %s():%d %s\n", + tmNow.tm_hour, tmNow.tm_min, tmNow.tm_sec, GET_PID(), THREAD_SELF(), (unsigned long long)(*pCnt), (unsigned long long)(deltaNSec / 1000), (unsigned long long)(*pNSec / 1000), (unsigned long long)((*pNSec / *pCnt) / 1000), function, line, file); + } + } + } +} + +static inline void avbTraceDocFn(int featureOn, const char *tag, const char *function, const char *file, int line, U64 *pCnt, U64 *pNSec) +{ + if (featureOn) { + static int depthTrace = 0; + if (tag[0] == '>') { + AVB_TRACE_PRINTF("%*s%s %s [%s]\n", depthTrace * 4, "", tag, function, file); + depthTrace++; + } + else if (tag[0] == '<') { + depthTrace--; + AVB_TRACE_PRINTF("%*s%s %s [%s]\n", depthTrace * 4, "", tag, function, file); + } + else { + AVB_TRACE_PRINTF("%*s%s %s [%s]\n", depthTrace * 4, "", tag, function, file); + } + } +} + +#endif // AVB_TRACE_ON +#endif // AVB_TRACE_PUB_H diff --git a/lib/avtp_pipeline/include/openavb_types.h b/lib/avtp_pipeline/include/openavb_types.h new file mode 100644 index 00000000..cf24142b --- /dev/null +++ b/lib/avtp_pipeline/include/openavb_types.h @@ -0,0 +1,45 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Provide common types and defines for use in AVB +*/ + +#ifndef AVB_TYPES_H +#define AVB_TYPES_H 1 + +#include "openavb_platform.h" + +typedef struct { // per IEEE 802.1Q-2011 Section 35.2.2.8.2 + U8 addr[ETH_ALEN]; + U16 uniqueID; +} AVBStreamID_t; + +#endif // AVB_TYPES_H diff --git a/lib/avtp_pipeline/include/openavb_types_base.h b/lib/avtp_pipeline/include/openavb_types_base.h new file mode 100644 index 00000000..dab5e35f --- /dev/null +++ b/lib/avtp_pipeline/include/openavb_types_base.h @@ -0,0 +1,79 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE : Internal AVB Base Types (have no dependencies) +*/ + +#ifndef AVB_TYPES_BASE_H +#define AVB_TYPES_BASE_H 1 + +#include <assert.h> +#include "openavb_types_base_pub.h" +#include "openavb_result_codes.h" + +typedef struct { // per IEEE 802.1Q-2011 Section 35.2.2.8.4 + U16 maxFrameSize; + U16 maxIntervalFrames; +} AVBTSpec_t; + +// Ethernet Frame overhead +// L2 includes MAC src/dest, VLAN tag, Ethertype +#define OPENAVB_AVTP_L2_OVERHEAD 18 +// L1 includes preamble, start-of-frame, FCS, interframe gap +#define OPENAVB_AVTP_L1_OVERHEAD 24 +// All of the above +#define OPENAVB_AVTP_ETHER_FRAME_OVERHEAD (OPENAVB_AVTP_L1_OVERHEAD + OPENAVB_AVTP_L2_OVERHEAD) + +// Maximum number of streams per class +// (Fixed number for efficiency in FQTSS kernel module) +#define MAX_AVB_STREAMS_PER_CLASS 16 +// Maximum number of streams that we handle +#define MAX_AVB_STREAMS (MAX_AVB_SR_CLASSES * MAX_AVB_STREAMS_PER_CLASS) + +#define SR_CLASS_IS_VALID(C) (C>=0 && C<MAX_AVB_SR_CLASSES) +#define AVB_CLASS_LABEL(C) ('A'+C) + +#define OPENAVB_DEFAULT_CLASSA_MAX_LATENCY (2 * NANOSECONDS_PER_MSEC) +#define OPENAVB_DEFAULT_CLASSB_MAX_LATENCY (50 * NANOSECONDS_PER_MSEC) + +// Ethernet MAC Address Length - 6 bytes +#define ETH_MAC_ADDR_LEN 6 + +// For production builds, use the first define here to turn assert off +// For debug builds, use the second define here to turn assert on +// CAUTION: Any executable code in _AST_ will not be included in production code. +//#define OPENAVB_ASSERT(_AST_) +#define OPENAVB_ASSERT(_AST_) assert(_AST_) + +// Features +//#define OPENAVB_GST_PIPELINE_INSTRUMENTATION 1 + +#endif // AVB_TYPES_BASE_H diff --git a/lib/avtp_pipeline/include/openavb_types_base_pub.h b/lib/avtp_pipeline/include/openavb_types_base_pub.h new file mode 100644 index 00000000..a78ef307 --- /dev/null +++ b/lib/avtp_pipeline/include/openavb_types_base_pub.h @@ -0,0 +1,120 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+/*
+* MODULE : Public AVB Base Types (have no dependencies)
+*/
+
+#ifndef AVB_TYPES_BASE_PUB_H
+#define AVB_TYPES_BASE_PUB_H 1
+
+#include "openavb_types_base_tcal_pub.h"
+#include <stdbool.h>
+#include <stdint.h>
+
+/** \file
+ * Common Base AVB Types that are exposed outside of the AVB
+ * stack.
+ */
+
+/*
+ * Useful types for OPENAVB AVB
+ *
+ */
+/// Number of nanoseconds in second
+#define NANOSECONDS_PER_SECOND (1000000000ULL)
+/// Number of nanoseconds in milisecond
+#define NANOSECONDS_PER_MSEC (1000000L)
+/// Number of nanoseconds in microsecond
+#define NANOSECONDS_PER_USEC (1000L)
+/// Number of microseconds in second
+#define MICROSECONDS_PER_SECOND (1000000L)
+/// Number of microseconds in milisecond
+#define MICROSECONDS_PER_MSEC (1000L)
+
+#ifndef TRUE // possible confict with gboolean
+/// True boolean value
+#define TRUE true
+/// False boolean value
+#define FALSE false
+#endif
+
+#ifndef NULL
+/// Null pointer value
+#define NULL 0
+#endif
+
+/// Signed 8 bit type
+typedef int8_t S8;
+/// Unsigned 8 bit type
+typedef uint8_t U8;
+/// Signed 16 bit type
+typedef int16_t S16;
+/// Unsigned 16 bit type
+typedef uint16_t U16;
+/// Signed 32 bit type
+typedef int32_t S32;
+/// Unsigned 32 bit type
+typedef uint32_t U32;
+/// Signed 64 bit type
+typedef int64_t S64;
+/// Unsigned 64 bit type
+typedef uint64_t U64;
+
+/// Describes role of the host
+typedef enum {
+ /// Role undefined or wrong handle
+ AVB_ROLE_UNDEFINED = 0,
+ /// Host acts as a talker
+ AVB_ROLE_TALKER,
+ /// Host acts as a listener
+ AVB_ROLE_LISTENER
+} avb_role_t;
+
+
+
+/// Supported AVB classes.
+typedef enum {
+ /// Stream reservation class A. 8000 packets per second
+ SR_CLASS_A,
+ /// Stream reservation class B. 4000 packets per second
+ SR_CLASS_B,
+// SR_CLASS_C,
+// SR_CLASS_D,
+ /// Number of supported stream reservation classes
+ MAX_AVB_SR_CLASSES
+} SRClassIdx_t;
+
+/// Regular
+#define SR_RANK_REGULAR 1
+/// Emergency
+#define SR_RANK_EMERGENCY 0
+
+#endif // AVB_TYPES_BASE_PUB_H
diff --git a/lib/avtp_pipeline/include/openavb_types_pub.h b/lib/avtp_pipeline/include/openavb_types_pub.h new file mode 100644 index 00000000..45016c68 --- /dev/null +++ b/lib/avtp_pipeline/include/openavb_types_pub.h @@ -0,0 +1,48 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Common AVB Types that are exposed outside of the +* AVB stack. +*/ + +#ifndef AVB_TYPES_PUB_H +#define AVB_TYPES_PUB_H 1 + +#include "openavb_types_base_pub.h" + +// Visiblity; used to define which function need to be externally visible +#if __GNUC__ >= 4 +#define DLL_EXPORT __attribute__ ((visibility ("default"))) +#else +#define DLL_EXPORT +#endif + +#endif // AVB_TYPES_PUB_H diff --git a/lib/avtp_pipeline/inih/CMakeLists.txt b/lib/avtp_pipeline/inih/CMakeLists.txt new file mode 100644 index 00000000..1876de1e --- /dev/null +++ b/lib/avtp_pipeline/inih/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/inih/ini.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/inih/LICENSE.txt b/lib/avtp_pipeline/inih/LICENSE.txt new file mode 100644 index 00000000..44a3093a --- /dev/null +++ b/lib/avtp_pipeline/inih/LICENSE.txt @@ -0,0 +1,27 @@ +
+The "inih" library is distributed under the New BSD license:
+
+Copyright (c) 2009, Brush Technology
+All rights reserved.
+
+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 Brush Technology 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 BRUSH TECHNOLOGY ''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 BRUSH TECHNOLOGY 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/avtp_pipeline/inih/README.TXT b/lib/avtp_pipeline/inih/README.TXT new file mode 100644 index 00000000..b8f4536e --- /dev/null +++ b/lib/avtp_pipeline/inih/README.TXT @@ -0,0 +1,15 @@ +"inih" is an .ini file parser. I'm currently using it for the +prototype talker and listener. It uses a BSD license, which requires +a copyright notification in releases of derived works, but does not +require the source code for derived works to be released. + +The description from the project page on Google code: + + inih (INI Not Invented Here) is a simple .INI file parser written in C. + It's only a couple of pages of code, and it was designed to be small + and simple, so it's good for embedded systems. It's also more or + less compatible with Python's ConfigParser style of .INI files, + including RFC 822-style multi-line syntax and name: value entries. + +Downloaded from: + http://code.google.com/p/inih/ diff --git a/lib/avtp_pipeline/inih/ini.c b/lib/avtp_pipeline/inih/ini.c new file mode 100644 index 00000000..4fad5c51 --- /dev/null +++ b/lib/avtp_pipeline/inih/ini.c @@ -0,0 +1,292 @@ +/* inih -- simple .INI file parser
+
+inih is released under the New BSD license (see LICENSE.txt). Go to the project
+home page for more info:
+
+http://code.google.com/p/inih/
+
+*/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include "ini.h"
+
+#if !INI_USE_STACK
+#include <stdlib.h>
+#endif
+
+#define MAX_SECTION 50
+#define MAX_NAME 50
+
+/* Strip whitespace chars off end of given string, in place. Return s. */
+static char* rstrip(char* s)
+{
+ char* p = s + strlen(s);
+ while (p > s && isspace(*--p))
+ *p = '\0';
+ return s;
+}
+
+/* Return pointer to first non-whitespace char in given string. */
+static char* lskip(const char* s)
+{
+ while (*s && isspace(*s))
+ s++;
+ return (char*)s;
+}
+
+/* Return pointer to first char c or ';' comment in given string, or pointer to
+ null at end of string if neither found. ';' must be prefixed by a whitespace
+ character to register as a comment. */
+static char* find_char_or_comment(const char* s, char c)
+{
+ int was_whitespace = 0;
+ while (*s && *s != c && !(was_whitespace && *s == ';')) {
+ was_whitespace = isspace(*s);
+ s++;
+ }
+ return (char*)s;
+}
+
+/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
+static char* strncpy0(char* dest, const char* src, size_t size)
+{
+ strncpy(dest, src, size);
+ dest[size - 1] = '\0';
+ return dest;
+}
+
+/* See documentation in header file. */
+int ini_parse_file(FILE* file,
+ int (*handler)(void*, const char*, const char*,
+ const char*),
+ void* user)
+{
+ /* Uses a fair bit of stack (use heap instead if you need to) */
+#if INI_USE_STACK
+ char line[INI_MAX_LINE];
+#else
+ char* line;
+#endif
+ char section[MAX_SECTION] = "";
+ char prev_name[MAX_NAME] = "";
+
+ char* start;
+ char* end;
+ char* name;
+ char* value;
+ int lineno = 0;
+ int error = 0;
+
+#if !INI_USE_STACK
+ line = (char*)malloc(INI_MAX_LINE);
+ if (!line) {
+ return -2;
+ }
+#endif
+
+ /* Scan through file line by line */
+ while (fgets(line, INI_MAX_LINE, file) != NULL) {
+ lineno++;
+
+ start = line;
+#if INI_ALLOW_BOM
+ if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
+ (unsigned char)start[1] == 0xBB &&
+ (unsigned char)start[2] == 0xBF) {
+ start += 3;
+ }
+#endif
+ start = lskip(rstrip(start));
+
+ if (*start == ';' || *start == '#') {
+ /* Per Python ConfigParser, allow '#' comments at start of line */
+ }
+#if INI_ALLOW_MULTILINE
+ else if (*prev_name && *start && start > line) {
+ /* Non-black line with leading whitespace, treat as continuation
+ of previous name's value (as per Python ConfigParser). */
+ if (!handler(user, section, prev_name, start) && !error)
+ error = lineno;
+ }
+#endif
+ else if (*start == '[') {
+ /* A "[section]" line */
+ end = find_char_or_comment(start + 1, ']');
+ if (*end == ']') {
+ *end = '\0';
+ strncpy0(section, start + 1, sizeof(section));
+ *prev_name = '\0';
+ }
+ else if (!error) {
+ /* No ']' found on section line */
+ error = lineno;
+ }
+ }
+ else if (*start && *start != ';') {
+ /* Not a comment, must be a name[=:]value pair */
+ end = find_char_or_comment(start, '=');
+ if (*end != '=') {
+ end = find_char_or_comment(start, ':');
+ }
+ if (*end == '=' || *end == ':') {
+ *end = '\0';
+ name = rstrip(start);
+ value = lskip(end + 1);
+ end = find_char_or_comment(value, '\0');
+ if (*end == ';')
+ *end = '\0';
+ rstrip(value);
+
+ /* Valid name[=:]value pair found, call handler */
+ strncpy0(prev_name, name, sizeof(prev_name));
+ if (!handler(user, section, name, value) && !error)
+ error = lineno;
+ }
+ else if (!error) {
+ /* No '=' or ':' found on name[=:]value line */
+ error = lineno;
+ }
+ }
+ }
+
+#if !INI_USE_STACK
+ free(line);
+#endif
+
+ return error;
+}
+
+#define NULL_CHAR '\0'
+#define COMMA ','
+
+int ini_parse_override(char *override,
+ int (*handler)(void*, const char*, const char*,
+ const char*),
+ void* user)
+{
+#if INI_USE_STACK
+ char line[INI_MAX_LINE];
+#else
+ char* line;
+#endif
+ char section[MAX_SECTION] = "";
+ char *name, *value;
+
+ char *src = override;
+ int error = -1;
+ int i, j;
+
+#if !INI_USE_STACK
+ line = (char*)malloc(INI_MAX_LINE);
+ if (!line) {
+ return -2;
+ }
+#endif
+
+ while (*src != NULL_CHAR)
+ {
+ if (*src == COMMA) {
+ src++;
+ }
+ else if (*src == '[') {
+ // section
+ src++; // skip opening delim
+ for (i = 0; i < MAX_SECTION; i++) {
+ section[i] = *src++;
+ if (section[i] == NULL_CHAR) {
+ // error, section not completed
+ goto out;
+ }
+ else if (section[i] == ']') {
+ section[i] = NULL_CHAR;
+ break;
+ }
+ }
+ if (i > MAX_SECTION)
+ goto out; // error, overflowed buffer
+ }
+ else {
+ // name/value
+ name = &line[0];
+ for (i = 0; i < MAX_NAME; i++) {
+ line[i] = *src++;
+ if (line[i] == NULL_CHAR) {
+ // error, name not completed
+ goto out;
+ }
+ else if (line[i] == COMMA) {
+ // error, no value
+ goto out;
+ }
+ else if (line[i] == '=') {
+ line[i] = NULL_CHAR;
+ break;
+ }
+ }
+ if (i > MAX_NAME)
+ goto out; // error, overflowed buffer
+
+ value = &line[i + 1];
+ for (j = i + 1; j < INI_MAX_LINE; j++) {
+ line[j] = *src++;
+ if (line[j] == ',') {
+ line[j] = NULL_CHAR;
+ break;
+ }
+ if (line[j] == NULL_CHAR) {
+ src--; // oops!
+ break;
+ }
+
+ }
+ if (j > INI_MAX_LINE)
+ goto out; // error, overflowed buffer
+
+ if (!handler(user, section, name, value) && !error)
+ error = -3;
+ }
+ }
+
+ // all parsed!
+ error = 0;
+
+ out:
+#if !INI_USE_STACK
+ free(line);
+#endif
+ return error;
+}
+
+/* See documentation in header file. */
+int ini_parse(const char* filename,
+ int (*handler)(void*, const char*, const char*, const char*),
+ void* user)
+{
+ FILE* file;
+ int error;
+ char *pvtFilename = strdup(filename);
+ if (!pvtFilename) {
+ return -1;
+ }
+
+ char *override = strchr(pvtFilename, COMMA);
+ if (override)
+ *override++ = '\0';
+
+ file = fopen(pvtFilename, "r");
+ if (!file) {
+ free (pvtFilename);
+ return -1;
+ }
+ error = ini_parse_file(file, handler, user);
+
+ if (error == 0 && override)
+ error = ini_parse_override(override, handler, user);
+ fclose(file);
+ free (pvtFilename);
+
+ return error;
+}
diff --git a/lib/avtp_pipeline/inih/ini.h b/lib/avtp_pipeline/inih/ini.h new file mode 100644 index 00000000..9ebfd492 --- /dev/null +++ b/lib/avtp_pipeline/inih/ini.h @@ -0,0 +1,72 @@ +/* inih -- simple .INI file parser
+
+inih is released under the New BSD license (see LICENSE.txt). Go to the project
+home page for more info:
+
+http://code.google.com/p/inih/
+
+*/
+
+#ifndef __INI_H__
+#define __INI_H__
+
+/* Make this header file easier to include in C++ code */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+
+/* Parse given INI-style file. May have [section]s, name=value pairs
+ (whitespace stripped), and comments starting with ';' (semicolon). Section
+ is "" if name=value pair parsed before any section heading. name:value
+ pairs are also supported as a concession to Python's ConfigParser.
+
+ For each name=value pair parsed, call handler function with given user
+ pointer as well as section, name, and value (data only valid for duration
+ of handler call). Handler should return nonzero on success, zero on error.
+
+ Returns 0 on success, line number of first error on parse error (doesn't
+ stop on first error), -1 on file open error, or -2 on memory allocation
+ error (only when INI_USE_STACK is zero).
+*/
+int ini_parse(const char* filename,
+ int (*handler)(void* user, const char* section,
+ const char* name, const char* value),
+ void* user);
+
+/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
+ close the file when it's finished -- the caller must do that. */
+int ini_parse_file(FILE* file,
+ int (*handler)(void* user, const char* section,
+ const char* name, const char* value),
+ void* user);
+
+/* Nonzero to allow multi-line value parsing, in the style of Python's
+ ConfigParser. If allowed, ini_parse() will call the handler with the same
+ name for each subsequent line parsed. */
+#ifndef INI_ALLOW_MULTILINE
+#define INI_ALLOW_MULTILINE 1
+#endif
+
+/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
+ the file. See http://code.google.com/p/inih/issues/detail?id=21 */
+#ifndef INI_ALLOW_BOM
+#define INI_ALLOW_BOM 1
+#endif
+
+/* Nonzero to use stack, zero to use heap (malloc/free). */
+#ifndef INI_USE_STACK
+#define INI_USE_STACK 1
+#endif
+
+/* Maximum line length for any line in INI file. */
+#ifndef INI_MAX_LINE
+#define INI_MAX_LINE 512
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INI_H__ */
diff --git a/lib/avtp_pipeline/inih/inih_r23.zip b/lib/avtp_pipeline/inih/inih_r23.zip Binary files differnew file mode 100644 index 00000000..a8ffc5d0 --- /dev/null +++ b/lib/avtp_pipeline/inih/inih_r23.zip diff --git a/lib/avtp_pipeline/intf_ctrl/CMakeLists.txt b/lib/avtp_pipeline/intf_ctrl/CMakeLists.txt new file mode 100644 index 00000000..6fe11ae9 --- /dev/null +++ b/lib/avtp_pipeline/intf_ctrl/CMakeLists.txt @@ -0,0 +1,6 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/intf_ctrl/openavb_intf_ctrl.c + PARENT_SCOPE +) + + diff --git a/lib/avtp_pipeline/intf_ctrl/ctrl_intf.md b/lib/avtp_pipeline/intf_ctrl/ctrl_intf.md new file mode 100644 index 00000000..2aaa37a4 --- /dev/null +++ b/lib/avtp_pipeline/intf_ctrl/ctrl_intf.md @@ -0,0 +1,32 @@ +Ctrl interface {#ctrl_intf} +============== + +# Description + +Control interface module. + +This interface module sends and receives control messages. There are +two modes this interface module can be configured for normal mode and mux +mode. In normal mode the control messages are exchanged with the host +application. In mux mode the control messages will be multiplexed from +multiple control streams into one out-going talker control stream. This +is part of the dispatch model for configuring the control message system +in the OPENAVB AVB stack. + +<br> +# Interface module configuration parameters + +Name | Description +--------------------------|--------------------------- +intf_nv_mux_mode | If set to 1 the multiplier mode for the control \ + interface is enabled. This is used for the dispatch \ + model of control message configuration. <br>For a \ + listener the in mux mode any incoming control \ + message is immediatedly placed in the ctrl mux \ + talkers media queue for retransmission.<br> \ + When using mux mode, the talker MUST be created \ + prior to listeners and remain running as long as any\ + listeners are running. +intf_nv_ignore_timestamp | If set to 1 timestamps will be ignored during \ + processing of frames. This also means stale (old) \ + Media Queue items will not be purged. diff --git a/lib/avtp_pipeline/intf_ctrl/ctrl_listener.ini b/lib/avtp_pipeline/intf_ctrl/ctrl_listener.ini new file mode 100644 index 00000000..08c509a5 --- /dev/null +++ b/lib/avtp_pipeline/intf_ctrl/ctrl_listener.ini @@ -0,0 +1,106 @@ +##################################################################### +# Control Listener configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = listener + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +stream_addr = 00:0c:29:f8:3e:c6 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: see description in talker.ini +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +#max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# ptp_type: 0 = none, 1 = system clock, 2 = ptp from audioscience (L0.3), 3 = ptp from OPENAVB +# ptp_type = 3 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_ctrl.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapCtrlInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 400 + +#map_nv_max_payload_size: The maximum payload size of the control commands. +map_nv_max_payload_size = 200 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +intf_lib = ./libopenavb_intf_ctrl.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfCtrlInitialize + +# intf_nv_mux_mode: When enabled with a value of 1 the multipler mode for the control interface is enabled. +# This is used for the dispatch model of control message configuration. For a listener the in mux mode any +# incoming control message is immediatedly placed in the ctrl mux talkers media queue for retransmission. +# When using mux mode, the talker MUST be created prior to listeners and remain running as long as any +# listeners are running. +intf_nv_mux_mode = 0 + +# intf_nv_ignore_timestamp: The If set the listener will ignore the timestamp on media queue items. +#intf_nv_ignore_timestamp = 0 diff --git a/lib/avtp_pipeline/intf_ctrl/ctrl_mux_listener.ini b/lib/avtp_pipeline/intf_ctrl/ctrl_mux_listener.ini new file mode 100644 index 00000000..19d6be55 --- /dev/null +++ b/lib/avtp_pipeline/intf_ctrl/ctrl_mux_listener.ini @@ -0,0 +1,106 @@ +##################################################################### +# Control Listener configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = listener + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +stream_addr = 00:0c:29:f8:3e:c6 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: see description in talker.ini +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +#max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# ptp_type: 0 = none, 1 = system clock, 2 = ptp from audioscience (L0.3), 3 = ptp from OPENAVB +# ptp_type = 3 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_ctrl.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapCtrlInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 400 + +#map_nv_max_payload_size: The maximum payload size of the control commands. +map_nv_max_payload_size = 200 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +intf_lib = ./libopenavb_intf_ctrl.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfCtrlInitialize + +# intf_nv_mux_mode: When enabled with a value of 1 the multipler mode for the control interface is enabled. +# This is used for the dispatch model of control message configuration. For a listener the in mux mode any +# incoming control message is immediatedly placed in the ctrl mux talkers media queue for retransmission. +# When using mux mode, the talker MUST be created prior to listeners and remain running as long as any +# listeners are running. +intf_nv_mux_mode = 1 + +# intf_nv_ignore_timestamp: The If set the listener will ignore the timestamp on media queue items. +#intf_nv_ignore_timestamp = 0 diff --git a/lib/avtp_pipeline/intf_ctrl/ctrl_mux_talker.ini b/lib/avtp_pipeline/intf_ctrl/ctrl_mux_talker.ini new file mode 100644 index 00000000..ad4c0716 --- /dev/null +++ b/lib/avtp_pipeline/intf_ctrl/ctrl_mux_talker.ini @@ -0,0 +1,144 @@ +##################################################################### +# General Talker configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 00:25:64:48:ca:a8 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: destination multicast address for the stream. +# +# If using SRP and MAAP, dynamic destination addresses are generated +# automatically by the talker and passed to the listner, and don't +# need to be configured. +# +# Without MAAP, locally administered (static) addresses must be +# configured. Thouse addresses are in the range of: +# 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically use :00 for the first stream, :01 for the second, etc. +# +# When SRP is being used the static destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set (to the same value) in both the talker and listener. +# +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 50000 + +# max_transmit_deficit_usec: Allows setting the maximum packet transmit rate deficit that will +# be recovered when a talker falls behind. This is only used on a talker side. When a talker +# can not keep up with the specified transmit rate it builds up a deficit and will attempt to +# make up for this deficit by sending more packets. There is normally some variability in the +# transmit rate because of other demands on the system so this is expected. However, without this +# bounding value the deficit could grew too large in cases such where more streams are started +# than the system can support and when the number of streams is reduced the remaining streams +# will attempt to recover this deficit by sending packets at a higher rate. This can cause a problem +# at the listener side and significantly delay the recovery time before media playback will return +# to normal. Typically this value can be set to the expected buffer size (in usec) that listeners are +# expected to be buffering. For low latency solutions this is normally a small value. For non-live +# media playback such as video playback the listener side buffers can often be large enough to held many +# seconds of data. +max_transmit_deficit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# ptp_type: 0 = none, 1 = system clock, 2 = ptp from audioscience (L0.3), 3 = ptp from OPENAVB +# ptp_type = 3 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_ctrl.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapCtrlInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 100 + +# map_nv_tx_rate: Transmit rate. +# If not set default of the talker class will be used. +map_nv_tx_rate = 1000 + +#map_nv_max_payload_size: The maximum payload size that the pipe will use. +map_nv_max_payload_size = 200 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +intf_lib = ./libopenavb_intf_ctrl.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfCtrlInitialize + +# intf_nv_mux_mode: When enabled with a value of 1 the multipler mode for the control interface is enabled. +# This is used for the dispatch model of control message configuration. For a listener the in mux mode any +# incoming control message is immediatedly placed in the ctrl mux talkers media queue for retransmission. +# When using mux mode, the talker MUST be created prior to listeners and remain running as long as any +# listeners are running. +intf_nv_mux_mode = 1 + + + + + + + diff --git a/lib/avtp_pipeline/intf_ctrl/ctrl_talker.ini b/lib/avtp_pipeline/intf_ctrl/ctrl_talker.ini new file mode 100644 index 00000000..ef33fa59 --- /dev/null +++ b/lib/avtp_pipeline/intf_ctrl/ctrl_talker.ini @@ -0,0 +1,147 @@ +##################################################################### +# General Talker configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 00:25:64:48:ca:a8 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: destination multicast address for the stream. +# +# If using SRP and MAAP, dynamic destination addresses are generated +# automatically by the talker and passed to the listner, and don't +# need to be configured. +# +# Without MAAP, locally administered (static) addresses must be +# configured. Thouse addresses are in the range of: +# 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically use :00 for the first stream, :01 for the second, etc. +# +# When SRP is being used the static destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set (to the same value) in both the talker and listener. +# +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 50000 + +# max_transmit_deficit_usec: Allows setting the maximum packet transmit rate deficit that will +# be recovered when a talker falls behind. This is only used on a talker side. When a talker +# can not keep up with the specified transmit rate it builds up a deficit and will attempt to +# make up for this deficit by sending more packets. There is normally some variability in the +# transmit rate because of other demands on the system so this is expected. However, without this +# bounding value the deficit could grew too large in cases such where more streams are started +# than the system can support and when the number of streams is reduced the remaining streams +# will attempt to recover this deficit by sending packets at a higher rate. This can cause a problem +# at the listener side and significantly delay the recovery time before media playback will return +# to normal. Typically this value can be set to the expected buffer size (in usec) that listeners are +# expected to be buffering. For low latency solutions this is normally a small value. For non-live +# media playback such as video playback the listener side buffers can often be large enough to held many +# seconds of data. +max_transmit_deficit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# ptp_type: 0 = none, 1 = system clock, 2 = ptp from audioscience (L0.3), 3 = ptp from OPENAVB +# ptp_type = 3 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_ctrl.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapCtrlInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 100 + +# map_nv_tx_rate: Transmit rate. +# If not set default of the talker class will be used. +map_nv_tx_rate = 1000 + +#map_nv_max_payload_size: The maximum payload size that the pipe will use. +map_nv_max_payload_size = 200 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +intf_lib = ./libopenavb_intf_ctrl.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfCtrlInitialize + +# intf_nv_mux_mode: When enabled with a value of 1 the multipler mode for the control interface is enabled. +# This is used for the dispatch model of control message configuration. For a listener the in mux mode any +# incoming control message is immediatedly placed in the ctrl mux talkers media queue for retransmission. +# When using mux mode, the talker MUST be created prior to listeners and remain running as long as any +# listeners are running. +intf_nv_mux_mode = 0 + + + + + + + diff --git a/lib/avtp_pipeline/intf_ctrl/openavb_intf_ctrl.c b/lib/avtp_pipeline/intf_ctrl/openavb_intf_ctrl.c new file mode 100755 index 00000000..c999b20d --- /dev/null +++ b/lib/avtp_pipeline/intf_ctrl/openavb_intf_ctrl.c @@ -0,0 +1,385 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Control interface module. +* +* This interface module sends and receives control messages. There are +* two modes this interface module can be configured for normal mode and mux +* mode. In normal mode the control messages are exchanged with the host +* application. In mux mode the control messages will be multiplexed from +* multiple control streams into one out-going talker control stream. This +* is part of the dispatch model for configuring the control message system +* in the OPENAVB AVB stack. +*/ + +// WORK IN PROGRESS + +#include <stdlib.h> +#include <string.h> +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_intf_pub.h" +#include "openavb_intf_ctrl_pub.h" + +#define AVB_LOG_COMPONENT "Control Interface" +#include "openavb_log_pub.h" + +// Forward Declarations +bool openavbIntfCtrlSendControl(void *pIntfHandle, U8 *pData, U32 dataLength, U32 usecDelay); + + +typedef struct { + ///////////// + // Config data + ///////////// + // Multiplexing mode. Multiple lister streams flowing into one talker stream. + bool muxMode; + + // Ignore timestamp at listener. + bool ignoreTimestamp; + + ///////////// + // Variable data + ///////////// + // Callback into the hosting application when control messages are received. + openavb_intf_ctrl_receive_cb_t openavbIntfCtrlReceiveControlFn; + + // Handle to pass back to the hosting application. + void *pTLHandle; + + // Flag indication that the mux talker is owned by this interface instance. + bool bOwnsCtrlMuxMediaQ; + +} pvt_data_t; + +// This is the talker mux media queue. The media queue will be set as thread safe mode and loccking for head and tail operations +// will be handled within the MediaQ. This interface module is designed such that there can only be 1 ctrl mux talker in a process. +media_q_t *pCtrlMuxMediaQ = NULL; + +openavb_intf_host_cb_list_t openavbIntfHostCBList; + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbIntfCtrlCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + if (pMediaQ) { + char *pEnd; + long tmp; + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (strcmp(name, "intf_nv_mux_mode") == 0) { + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->muxMode = (tmp == 1); + } + } + else if (strcmp(name, "intf_nv_ignore_timestamp") == 0) { + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->ignoreTimestamp = (tmp == 1); + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +void openavbIntfCtrlGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// A call to this callback indicates that this interface module will be +// a talker. Any talker initialization can be done in this function. +void openavbIntfCtrlTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + // Talker control interfaces will always enable MediaQ thread safety. + openavbMediaQThreadSafeOn(pMediaQ); + + if (pPvtData->muxMode) { + if (!pPvtData->bOwnsCtrlMuxMediaQ) { + pCtrlMuxMediaQ = pMediaQ; + pPvtData->bOwnsCtrlMuxMediaQ = TRUE; + } + else { + AVB_LOG_ERROR("Only one MUX talker is allowed per process."); + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Control transmission is handled in openavbIntfCtrlSendControl() +bool openavbIntfCtrlTxCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return TRUE; +} + +// A call to this callback indicates that this interface module will be +// a listener. Any listener initialization can be done in this function. +void openavbIntfCtrlRxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// This callback is called when acting as a listener. +bool openavbIntfCtrlRxCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + if (pMediaQ) { + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + media_q_item_t *pMediaQItem = NULL; + + pMediaQItem = openavbMediaQTailLock(pMediaQ, pPvtData->ignoreTimestamp); + if (pMediaQItem) { + if (pMediaQItem->dataLen) { + if (pPvtData->muxMode) { + if (pCtrlMuxMediaQ) { + if (!openavbIntfCtrlSendControl(pCtrlMuxMediaQ, pMediaQItem->pPubData, pMediaQItem->dataLen, 0)) { + openavbMediaQTailPull(pMediaQ); + AVB_LOG_ERROR("Failed to retransmit the control message."); + return FALSE; + } + else { + openavbMediaQTailPull(pMediaQ); + return TRUE; + } + } + else { + openavbMediaQTailPull(pMediaQ); + AVB_LOG_ERROR("Control mux talker not found."); + return FALSE; + } + } + else { + if (pPvtData->openavbIntfCtrlReceiveControlFn) { + pPvtData->openavbIntfCtrlReceiveControlFn(pPvtData->pTLHandle, pMediaQItem->pPubData, pMediaQItem->dataLen); + openavbMediaQTailPull(pMediaQ); + return TRUE; + } + else { + openavbMediaQTailPull(pMediaQ); + AVB_LOG_ERROR("Control receive callback not defined."); + return FALSE; + } + + } + } + openavbMediaQTailPull(pMediaQ); + } + } + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; +} + +// This callback will be called when the stream is closing. +void openavbIntfCtrlEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + // If this instance of interface owns the mux talker remove it now + if (pPvtData->bOwnsCtrlMuxMediaQ) { + pCtrlMuxMediaQ = NULL; + pPvtData->bOwnsCtrlMuxMediaQ = FALSE; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// General shutdown callback regardless if a talker or listener. Called once during openavbTLClose() +void openavbIntfCtrlGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Host side callback +extern bool DLL_EXPORT openavbIntfCtrlRegisterReceiveControlCB(void *pIntfHandle, void *pTLHandle, openavb_intf_ctrl_receive_cb_t openavbIntfCtrlReceiveControlFn) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + media_q_t *pMediaQ = pIntfHandle; + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + pPvtData->openavbIntfCtrlReceiveControlFn = openavbIntfCtrlReceiveControlFn; + pPvtData->pTLHandle = pTLHandle; + + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return TRUE; + } + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return FALSE; +} + +extern bool DLL_EXPORT openavbIntfCtrlUnregisterReceiveControlCB(void *pIntfHandle) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + media_q_t *pMediaQ = pIntfHandle; + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + pPvtData->openavbIntfCtrlReceiveControlFn = NULL; + pPvtData->pTLHandle = NULL; + + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return TRUE; + } + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return FALSE; +} + +// Host side callback +extern bool DLL_EXPORT openavbIntfCtrlSendControl(void *pIntfHandle, U8 *pData, U32 dataLength, U32 usecDelay) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + + media_q_t *pMediaQ = pIntfHandle; + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (pMediaQItem) { + if (pMediaQItem->itemSize >= dataLength) { + memcpy(pMediaQItem->pPubData, pData, dataLength); + pMediaQItem->dataLen = dataLength; + } + else { + AVB_LOG_ERROR("Control data too large for media queue."); + openavbMediaQHeadUnlock(pMediaQ); + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; + } + + openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime); + openavbAvtpTimeAddUSec(pMediaQItem->pAvtpTime, usecDelay); + openavbMediaQHeadPush(pMediaQ); + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return TRUE; + } + else { + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; // Media queue full + } + } + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; +} + +// Main initialization entry point into the interface module +extern bool DLL_EXPORT openavbIntfCtrlInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pMediaQ->pPvtIntfInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pPvtIntfInfo) { + AVB_LOG_ERROR("Unable to allocate memory for AVTP interface module."); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + + pIntfCB->intf_cfg_cb = openavbIntfCtrlCfgCB; + pIntfCB->intf_gen_init_cb = openavbIntfCtrlGenInitCB; + pIntfCB->intf_tx_init_cb = openavbIntfCtrlTxInitCB; + pIntfCB->intf_tx_cb = openavbIntfCtrlTxCB; + pIntfCB->intf_rx_init_cb = openavbIntfCtrlRxInitCB; + pIntfCB->intf_rx_cb = openavbIntfCtrlRxCB; + pIntfCB->intf_end_cb = openavbIntfCtrlEndCB; + pIntfCB->intf_gen_end_cb = openavbIntfCtrlGenEndCB; + + openavbIntfHostCBList.register_receive_control_cb = openavbIntfCtrlRegisterReceiveControlCB; + openavbIntfHostCBList.unregister_receive_control_cb = openavbIntfCtrlUnregisterReceiveControlCB; + openavbIntfHostCBList.send_control_cb = openavbIntfCtrlSendControl; + pIntfCB->intf_host_cb_list = (void *)&openavbIntfHostCBList; + + pPvtData->muxMode = FALSE; + pPvtData->ignoreTimestamp = FALSE; + pPvtData->openavbIntfCtrlReceiveControlFn = NULL; + pPvtData->pTLHandle = NULL; + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return TRUE; +} + diff --git a/lib/avtp_pipeline/intf_ctrl/openavb_intf_ctrl_pub.h b/lib/avtp_pipeline/intf_ctrl/openavb_intf_ctrl_pub.h new file mode 100755 index 00000000..372ef978 --- /dev/null +++ b/lib/avtp_pipeline/intf_ctrl/openavb_intf_ctrl_pub.h @@ -0,0 +1,66 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Control interface module public interface +*/ + +#ifndef OPENAVB_INTF_CTRL_PUB_H +#define OPENAVB_INTF_CTRL_PUB_H 1 + +#include "openavb_types_pub.h" + +/** \file + * Control interface module public interface + */ + +/// This callback once registered will be called for each control command received by the listener. +typedef void (*openavb_intf_ctrl_receive_cb_t)(void *pTLHandle, U8 *pData, U32 dataLength); + +/// Register a callback for received control commands. +typedef bool (*openavb_intf_ctrl_register_receive_control_fn_t)(void *pIntfHandle, void *pTLHandle, openavb_intf_ctrl_receive_cb_t openavbIntfCtrlReceiveCommandCB); + +/// Register a callback for received control commands. +typedef bool (*openavb_intf_ctrl_unregister_receive_control_fn_t)(void *pIntfHandle); + +/// Submit a control command for transmission. +typedef bool (*openavb_intf_ctrl_send_control_fn_t)(void *pIntfHandle, U8 *pData, U32 dataLength, U32 usecDelay); + +/// Callbacks to control functions +typedef struct { + /// Registering callback function + openavb_intf_ctrl_register_receive_control_fn_t register_receive_control_cb; + /// Unregistering callback function + openavb_intf_ctrl_unregister_receive_control_fn_t unregister_receive_control_cb; + /// Submitting control command + openavb_intf_ctrl_send_control_fn_t send_control_cb; +} openavb_intf_host_cb_list_t; + +#endif // OPENAVB_INTF_CTRL_PUB_H diff --git a/lib/avtp_pipeline/intf_echo/CMakeLists.txt b/lib/avtp_pipeline/intf_echo/CMakeLists.txt new file mode 100644 index 00000000..0439ee04 --- /dev/null +++ b/lib/avtp_pipeline/intf_echo/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/intf_echo/openavb_intf_echo.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/intf_echo/echo_host_intf.md b/lib/avtp_pipeline/intf_echo/echo_host_intf.md new file mode 100644 index 00000000..d99a2e30 --- /dev/null +++ b/lib/avtp_pipeline/intf_echo/echo_host_intf.md @@ -0,0 +1,29 @@ +Echo interface {#echo_host_intf} +============== + +# Description + +This interface module as a talker will push a configured string into the Media +Queue for transmission. As a listener it will echo the received data to stdout. +This is strictly for testing purposes and is generally intended to work with the +[Pipe mapping](@ref pipe_map) module + +<br> +# Interface module configuration parameters + +Name | Description +--------------------------|--------------------------- +intf_nv_echo_string | String that will be sent by the talker +intf_nv_echo_string_repeat| Number of copies of the string to send in each \ + packet. <br> \ + The repeat setting if used must come after the \ + intf_nv_echo_string in this file +intf_nv_echo_increment | If set to 1 an incrementing number will be appended\ + to the string +intf_nv_tx_local_echo | If set to 1 locally output the string to stdout \ + at the talker +intf_nv_echo_no_newline | If set to 1 a newline will not be printed to the \ + stdout +intf_nv_ignore_timestamp | If set to 1 timestamps will be ignored during \ + processing of frames. This also means stale (old) \ + Media Queue items will not be purged. diff --git a/lib/avtp_pipeline/intf_echo/echo_listener.ini b/lib/avtp_pipeline/intf_echo/echo_listener.ini new file mode 100644 index 00000000..e926dd5b --- /dev/null +++ b/lib/avtp_pipeline/intf_echo/echo_listener.ini @@ -0,0 +1,110 @@ +##################################################################### +# General Listener configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = listener + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +stream_addr = 00:0c:29:f8:3e:c6 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: see description in talker.ini +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +#max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +report_seconds = 1 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_pipe.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapPipeInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 400 + +#map_nv_max_payload_size: The maximum payload size that the pipe will use. +map_nv_max_payload_size = 200 + +# map_nv_push_header +map_nv_push_header = 0 + +# map_nv_pull_header +map_nv_pull_header = 0 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +intf_lib = ./libopenavb_intf_echo.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfEchoInitialize + +# intf_nv_echo_increment: Append an incrementing number to the string. Works on both talker and listener. +# If both talker and listener have this option enable long tests can be preformed to see if the increments +# are the same at the end of the run indicating every packet the talker sent the listener received. +intf_nv_echo_increment = 1 + +# intf_nv_echo_no_newline: Do not include a newline in the stdout output. +#intf_nv_echo_no_newline = 1 + +# intf_nv_ignore_timestamp: If set the listener will ignore the timestamp on media queue items. +#intf_nv_ignore_timestamp = 1 diff --git a/lib/avtp_pipeline/intf_echo/echo_talker.ini b/lib/avtp_pipeline/intf_echo/echo_talker.ini new file mode 100644 index 00000000..f083033d --- /dev/null +++ b/lib/avtp_pipeline/intf_echo/echo_talker.ini @@ -0,0 +1,159 @@ +##################################################################### +# General Talker configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +stream_addr = a0:36:9f:66:8c:9f + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: destination multicast address for the stream. +# +# If using SRP and MAAP, dynamic destination addresses are generated +# automatically by the talker and passed to the listner, and don't +# need to be configured. +# +# Without MAAP, locally administered (static) addresses must be +# configured. Thouse addresses are in the range of: +# 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically use :00 for the first stream, :01 for the second, etc. +# +# When SRP is being used the static destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set (to the same value) in both the talker and listener. +# +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# +dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 50000 + +# max_transmit_deficit_usec: Allows setting the maximum packet transmit rate deficit that will +# be recovered when a talker falls behind. This is only used on a talker side. When a talker +# can not keep up with the specified transmit rate it builds up a deficit and will attempt to +# make up for this deficit by sending more packets. There is normally some variability in the +# transmit rate because of other demands on the system so this is expected. However, without this +# bounding value the deficit could grew too large in cases such where more streams are started +# than the system can support and when the number of streams is reduced the remaining streams +# will attempt to recover this deficit by sending packets at a higher rate. This can cause a problem +# at the listener side and significantly delay the recovery time before media playback will return +# to normal. Typically this value can be set to the expected buffer size (in usec) that listeners are +# expected to be buffering. For low latency solutions this is normally a small value. For non-live +# media playback such as video playback the listener side buffers can often be large enough to held many +# seconds of data. +max_transmit_deficit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +report_seconds = 1 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_pipe.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapPipeInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +# map_nv_tx_rate: Transmit rate. +# If not set default of the talker class will be used. +map_nv_tx_rate = 4000 + +#map_nv_max_payload_size: The maximum payload size that the pipe will use. +map_nv_max_payload_size = 200 + +# map_nv_push_header +map_nv_push_header = 0 + +# map_nv_pull_header +map_nv_pull_header = 0 + + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +intf_lib = ./libopenavb_intf_echo.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfEchoInitialize + +# intf_nv_echo_string: Echo string used by the talker. +intf_nv_echo_string = Send this string over the AVB network: + +# intf_nv_echo_string_repeat: Number of copies of the string to send in each packet. +# The repeat setting if used must come after the intf_nv_echo_string in this file. +intf_nv_echo_string_repeat = 1 + +# intf_nv_echo_increment: Append an incrementing number to the string. +intf_nv_echo_increment = 1 + +# intf_nv_tx_local_echo: Output locally at the talker. +# intf_nv_tx_local_echo = 1 + +# intf_nv_echo_no_newline: Do not include a newline in the stdout output. +# intf_nv_echo_no_newline = 1 + + + + + + diff --git a/lib/avtp_pipeline/intf_echo/openavb_intf_echo.c b/lib/avtp_pipeline/intf_echo/openavb_intf_echo.c new file mode 100755 index 00000000..9173644d --- /dev/null +++ b/lib/avtp_pipeline/intf_echo/openavb_intf_echo.c @@ -0,0 +1,349 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Echo interface module. +* +* This interface module as a talker to push a configured string out. As +* a listener it will echo the data to stdout. This is strickly for +* testing purposes and is generally intented to work with the Pipe +* mapping module. +*/ + +#include <stdlib.h> +#include <string.h> +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_intf_pub.h" + +#define AVB_LOG_COMPONENT "Echo Interface" +#include "openavb_log_pub.h" + +typedef struct { + ///////////// + // Config data + ///////////// + // intf_nv_echo_string: sets a string that will be sent by the talker. + char *pEchoString; + + // String repeat + U32 echoStringRepeat; + + // calculated value from pEchoString. + U32 echoStringLen; + + // Append an incrementing number to the string. + bool increment; + + // Output locally at the talker. + bool txLocalEcho; + + // No new line. + bool noNewline; + + // Ignore timestamp at listener. + bool ignoreTimestamp; + + ///////////// + // Variable data + ///////////// + // When increment is enable this is the counter + U32 Counter; +} pvt_data_t; + + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbIntfEchoCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + if (pMediaQ) { + char *pEnd; + long tmp; + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (strcmp(name, "intf_nv_echo_string") == 0) { + if (pPvtData->pEchoString) + free(pPvtData->pEchoString); + pPvtData->pEchoString = strdup(value); + if (pPvtData->pEchoString) { + pPvtData->echoStringLen = strlen(pPvtData->pEchoString); + } + } + else if (strcmp(name, "intf_nv_echo_string_repeat") == 0) { + pPvtData->echoStringRepeat = strtol(value, &pEnd, 10); + + // Repeat the string if needed + if (pPvtData->echoStringRepeat > 1 && pPvtData->pEchoString) { + char *pEchoStringRepeat = calloc(1, (pPvtData->echoStringLen * pPvtData->echoStringRepeat) + 1); + + int i1; + for (i1 = 0; i1 < pPvtData->echoStringRepeat; i1++) { + strcat(pEchoStringRepeat, pPvtData->pEchoString); + } + + free(pPvtData->pEchoString); + pPvtData->pEchoString = pEchoStringRepeat; + pPvtData->echoStringLen = strlen(pPvtData->pEchoString); + } + } + else if (strcmp(name, "intf_nv_echo_increment") == 0) { + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->increment = (tmp == 1); + } + } + else if (strcmp(name, "intf_nv_tx_local_echo") == 0) { + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->txLocalEcho = (tmp == 1); + } + } + else if (strcmp(name, "intf_nv_echo_no_newline") == 0) { + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->noNewline = (tmp == 1); + } + } + else if (strcmp(name, "intf_nv_ignore_timestamp") == 0) { + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->ignoreTimestamp = (tmp == 1); + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +void openavbIntfEchoGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// A call to this callback indicates that this interface module will be +// a talker. Any talker initialization can be done in this function. +void openavbIntfEchoTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + pPvtData->Counter = 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// This callback will be called for each AVB transmit interval. Commonly this will be +// 4000 or 8000 times per second. +bool openavbIntfEchoTxCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (pMediaQItem) { + if (pMediaQItem->itemSize >= pPvtData->increment ? pPvtData->echoStringLen + 16 : pPvtData->echoStringLen) { + if (pPvtData->increment) { + int len = sprintf(pMediaQItem->pPubData, "%s %u", pPvtData->pEchoString, pPvtData->Counter++); + pMediaQItem->dataLen = len; + } + else { + memcpy(pMediaQItem->pPubData, pPvtData->pEchoString, pPvtData->echoStringLen); + pMediaQItem->dataLen = pPvtData->echoStringLen; + } + } + else { + memcpy(pMediaQItem->pPubData, pPvtData->pEchoString, pMediaQItem->itemSize); + pMediaQItem->dataLen = pMediaQItem->itemSize; + } + + if (pPvtData->txLocalEcho) { + if (pPvtData->noNewline) + printf("%s ", (char *)pMediaQItem->pPubData); + else + printf("%s\n\r", (char *)pMediaQItem->pPubData); + } + + openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime); + openavbMediaQHeadPush(pMediaQ); + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return TRUE; + } + else { + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; // Media queue full + } + } + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; +} + +// A call to this callback indicates that this interface module will be +// a listener. Any listener initialization can be done in this function. +void openavbIntfEchoRxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + pPvtData->Counter = 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// This callback is called when acting as a listener. +bool openavbIntfEchoRxCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + if (pMediaQ) { + bool moreItems = TRUE; + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + media_q_item_t *pMediaQItem = NULL; + while (moreItems) { + pMediaQItem = openavbMediaQTailLock(pMediaQ, pPvtData->ignoreTimestamp); + if (pMediaQItem) { + if (pMediaQItem->dataLen) { + if (pMediaQItem->itemSize > pMediaQItem->dataLen) { + *((char*)pMediaQItem->pPubData + pMediaQItem->dataLen) = 0; //End string with 0 + } + if (pPvtData->noNewline) { + printf("%s ", (char *)pMediaQItem->pPubData); + } + else { + if (pPvtData->increment) { + printf("%s : %u\n\r", (char *)pMediaQItem->pPubData, pPvtData->Counter++); + } + else { + printf("%s\n\r", (char *)pMediaQItem->pPubData); + } + } + } + openavbMediaQTailPull(pMediaQ); + } + else { + moreItems = FALSE; + } + } + } + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; +} + +// This callback will be called when the stream is closing. +void openavbIntfEchoEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// General shutdown callback regardless if a talker or listener. Called once during openavbTLClose() +void openavbIntfEchoGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (pPvtData->pEchoString) { + free(pPvtData->pEchoString); + pPvtData->pEchoString = NULL; + } + } + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Main initialization entry point into the interface module +extern bool DLL_EXPORT openavbIntfEchoInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pMediaQ->pPvtIntfInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pPvtIntfInfo) { + AVB_LOG_ERROR("Unable to allocate memory for AVTP interface module."); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + + pIntfCB->intf_cfg_cb = openavbIntfEchoCfgCB; + pIntfCB->intf_gen_init_cb = openavbIntfEchoGenInitCB; + pIntfCB->intf_tx_init_cb = openavbIntfEchoTxInitCB; + pIntfCB->intf_tx_cb = openavbIntfEchoTxCB; + pIntfCB->intf_rx_init_cb = openavbIntfEchoRxInitCB; + pIntfCB->intf_rx_cb = openavbIntfEchoRxCB; + pIntfCB->intf_end_cb = openavbIntfEchoEndCB; + pIntfCB->intf_gen_end_cb = openavbIntfEchoGenEndCB; + + pPvtData->pEchoString = NULL; + pPvtData->echoStringRepeat = 1; + pPvtData->echoStringLen = 0; + pPvtData->increment = FALSE; + pPvtData->txLocalEcho = FALSE; + pPvtData->noNewline = FALSE; + pPvtData->ignoreTimestamp = FALSE; + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return TRUE; +} diff --git a/lib/avtp_pipeline/intf_logger/CMakeLists.txt b/lib/avtp_pipeline/intf_logger/CMakeLists.txt new file mode 100644 index 00000000..c27cce55 --- /dev/null +++ b/lib/avtp_pipeline/intf_logger/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/intf_logger/openavb_intf_logger.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/intf_logger/openavb_intf_logger.c b/lib/avtp_pipeline/intf_logger/openavb_intf_logger.c new file mode 100644 index 00000000..48517ad5 --- /dev/null +++ b/lib/avtp_pipeline/intf_logger/openavb_intf_logger.c @@ -0,0 +1,201 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+/*
+* Usage Notes: It is expected that there will be at most only 1 logger
+* interface module running.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include "openavb_types_pub.h"
+#include "openavb_trace_pub.h"
+#include "openavb_mediaq_pub.h"
+#include "openavb_intf_pub.h"
+
+#define AVB_LOG_COMPONENT "Logger Interface"
+#include "openavb_log_pub.h"
+
+typedef struct {
+ /////////////
+ // Config data
+ /////////////
+ // Ignore timestamp at listener.
+ bool ignoreTimestamp;
+
+ /////////////
+ // Variable data
+ /////////////
+} pvt_data_t;
+
+
+// Each configuration name value pair for this mapping will result in this callback being called.
+void openavbIntfLoggerCfgCB(media_q_t *pMediaQ, const char *name, const char *value)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+ if (pMediaQ) {
+ //char *pEnd;
+ //long tmp;
+
+ pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo;
+ if (!pPvtData) {
+ AVB_LOG_ERROR("Private interface module data not allocated.");
+ return;
+ }
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+void openavbIntfLoggerGenInitCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+// A call to this callback indicates that this interface module will be
+// a talker. Any talker initialization can be done in this function.
+void openavbIntfLoggerTxInitCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+
+ if (pMediaQ) {
+ pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo;
+ if (!pPvtData) {
+ AVB_LOG_ERROR("Private interface module data not allocated.");
+ return;
+ }
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+// This callback will be called for each AVB transmit interval. Commonly this will be
+// 4000 or 8000 times per second.
+bool openavbIntfLoggerTxCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL);
+ if (pMediaQ) {
+ pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo;
+ if (!pPvtData) {
+ AVB_LOG_ERROR("Private interface module data not allocated.");
+ return FALSE;
+ }
+
+ media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ);
+ if (pMediaQItem) {
+ U32 dataLen = avbLogGetMsg(pMediaQItem->pPubData, pMediaQItem->itemSize);
+ if (dataLen) {
+ pMediaQItem->dataLen = dataLen;
+ openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime);
+ openavbMediaQHeadPush(pMediaQ);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL);
+ return TRUE;
+ }
+ else {
+ openavbMediaQHeadUnlock(pMediaQ);
+ }
+ }
+ else {
+ AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL);
+ return FALSE; // Media queue full
+ }
+ }
+ AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL);
+ return FALSE;
+}
+
+// A call to this callback indicates that this interface module will be
+// a listener. Any listener initialization can be done in this function.
+void openavbIntfLoggerRxInitCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+// This callback is called when acting as a listener.
+bool openavbIntfLoggerRxCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL);
+ return FALSE;
+}
+
+// This callback will be called when the stream is closing.
+void openavbIntfLoggerEndCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+// General shutdown callback regardless if a talker or listener. Called once during openavbTLClose()
+void openavbIntfLoggerGenEndCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+ if (pMediaQ) {
+ pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo;
+ if (!pPvtData) {
+ AVB_LOG_ERROR("Private interface module data not allocated.");
+ return;
+ }
+ }
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+// Main initialization entry point into the interface module
+extern bool DLL_EXPORT openavbIntfLoggerInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+
+ if (pMediaQ) {
+ pMediaQ->pPvtIntfInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed.
+
+ if (!pMediaQ->pPvtIntfInfo) {
+ AVB_LOG_ERROR("Unable to allocate memory for AVTP interface module.");
+ return FALSE;
+ }
+
+ pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo;
+
+ pIntfCB->intf_cfg_cb = openavbIntfLoggerCfgCB;
+ pIntfCB->intf_gen_init_cb = openavbIntfLoggerGenInitCB;
+ pIntfCB->intf_tx_init_cb = openavbIntfLoggerTxInitCB;
+ pIntfCB->intf_tx_cb = openavbIntfLoggerTxCB;
+ pIntfCB->intf_rx_init_cb = openavbIntfLoggerRxInitCB;
+ pIntfCB->intf_rx_cb = openavbIntfLoggerRxCB;
+ pIntfCB->intf_end_cb = openavbIntfLoggerEndCB;
+ pIntfCB->intf_gen_end_cb = openavbIntfLoggerGenEndCB;
+
+ pPvtData->ignoreTimestamp = FALSE;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+ return TRUE;
+}
diff --git a/lib/avtp_pipeline/intf_logger/openavb_intf_logger.md b/lib/avtp_pipeline/intf_logger/openavb_intf_logger.md new file mode 100644 index 00000000..015a09f5 --- /dev/null +++ b/lib/avtp_pipeline/intf_logger/openavb_intf_logger.md @@ -0,0 +1,24 @@ +Logger Interface {#logger_intf} +================ + +# Description + +This interface module is a talker only and design to send OPENAVB AVB +internally log message over AVB for a listener to act on (such as +display). One use case of this interface module is for low end devices +that either have no provisions for console output or are restricted in +CPU cycle to lot messages. + +For this interface module to be used the logging system must be built +with the option OPENAVB_LOG_PULL_MODE set to TRUE. + +Typically the TX rate for this interface module can be quite low. The +actual value will depend on how much data is expected to be logged. For +general purpose use it is recommended to use 1000 packet per second or +less. + +<br> +# Interface module configuration parameters + +There are no configuration parameters for this interface module. + diff --git a/lib/avtp_pipeline/intf_null/CMakeLists.txt b/lib/avtp_pipeline/intf_null/CMakeLists.txt new file mode 100644 index 00000000..bf0d4c35 --- /dev/null +++ b/lib/avtp_pipeline/intf_null/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/intf_null/openavb_intf_null.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/intf_null/null_host_intf.md b/lib/avtp_pipeline/intf_null/null_host_intf.md new file mode 100644 index 00000000..8ef34d18 --- /dev/null +++ b/lib/avtp_pipeline/intf_null/null_host_intf.md @@ -0,0 +1,17 @@ +NULL interface {#null_host_intf} +============== + +# Description + +This NULL interface module neither sends or receives data but will +exercise the various functions and callback and can be used as an example +or a template for new interfaces. + +<br> +# Interface module configuration parameters + +Name | Description +--------------------------|--------------------------- +intf_nv_ignore_timestamp | If set to 1 timestamps will be ignored during \ + processing of frames. This also means stale (old) \ + Media Queue items will not be purged. diff --git a/lib/avtp_pipeline/intf_null/null_listener.ini b/lib/avtp_pipeline/intf_null/null_listener.ini new file mode 100644 index 00000000..2645f0fa --- /dev/null +++ b/lib/avtp_pipeline/intf_null/null_listener.ini @@ -0,0 +1,98 @@ +##################################################################### +# General Listener configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = listener + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +stream_addr = 00:0c:29:f8:3e:c6 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: see description in talker.ini +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +#max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +#sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +#max_transit_usec = 2000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_null.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapNullInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +#map_nv_max_payload_size: The maximum payload size that the pipe will use. +#map_nv_max_payload_size = 200; + + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +intf_lib = ./libopenavb_intf_null.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfNullInitialize + +# intf_nv_ignore_timestamp: If set the listener will ignore the timestamp on media queue items. +#intf_nv_ignore_timestamp = 1 + diff --git a/lib/avtp_pipeline/intf_null/null_talker.ini b/lib/avtp_pipeline/intf_null/null_talker.ini new file mode 100644 index 00000000..088c2009 --- /dev/null +++ b/lib/avtp_pipeline/intf_null/null_talker.ini @@ -0,0 +1,136 @@ +##################################################################### +# General Talker configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 00:25:64:48:ca:a8 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: destination multicast address for the stream. +# +# If using SRP and MAAP, dynamic destination addresses are generated +# automatically by the talker and passed to the listner, and don't +# need to be configured. +# +# Without MAAP, locally administered (static) addresses must be +# configured. Thouse addresses are in the range of: +# 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically use :00 for the first stream, :01 for the second, etc. +# +# When SRP is being used the static destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set (to the same value) in both the talker and listener. +# +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +#sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 2000 + +# max_transmit_deficit_usec: Allows setting the maximum packet transmit rate deficit that will +# be recovered when a talker falls behind. This is only used on a talker side. When a talker +# can not keep up with the specified transmit rate it builds up a deficit and will attempt to +# make up for this deficit by sending more packets. There is normally some variability in the +# transmit rate because of other demands on the system so this is expected. However, without this +# bounding value the deficit could grew too large in cases such where more streams are started +# than the system can support and when the number of streams is reduced the remaining streams +# will attempt to recover this deficit by sending packets at a higher rate. This can cause a problem +# at the listener side and significantly delay the recovery time before media playback will return +# to normal. Typically this value can be set to the expected buffer size (in usec) that listeners are +# expected to be buffering. For low latency solutions this is normally a small value. For non-live +# media playback such as video playback the listener side buffers can often be large enough to held many +# seconds of data. +max_transmit_deficit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_null.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapNullInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +#map_nv_max_payload_size: The maximum payload size that the pipe will use. +#map_nv_max_payload_size = 200; + +# map_nv_tx_rate: Transmit rate +# If not set default of the talker class will be used. +#map_nv_tx_rate = 2000 + + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +intf_lib = ./libopenavb_intf_null.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfNullInitialize + + + + + diff --git a/lib/avtp_pipeline/intf_null/openavb_intf_null.c b/lib/avtp_pipeline/intf_null/openavb_intf_null.c new file mode 100755 index 00000000..47c1b395 --- /dev/null +++ b/lib/avtp_pipeline/intf_null/openavb_intf_null.c @@ -0,0 +1,208 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : NULL interface module. +* +* This NULL interface module neither sends or receives data but will +* exercise the various functions and callback and can be used as an example +* or a template for new interfaces. +*/ + +#include <stdlib.h> +#include <string.h> +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_intf_pub.h" + +#define AVB_LOG_COMPONENT "Null Interface" +#include "openavb_log_pub.h" + +typedef struct { + ///////////// + // Config data + ///////////// + // Ignore timestamp at listener. + bool ignoreTimestamp; + + ///////////// + // Variable data + ///////////// +} pvt_data_t; + + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbIntfNullCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + char *pEnd; + long tmp; + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (strcmp(name, "intf_nv_ignore_timestamp") == 0) { + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->ignoreTimestamp = (tmp == 1); + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +void openavbIntfNullGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// A call to this callback indicates that this interface module will be +// a talker. Any talker initialization can be done in this function. +void openavbIntfNullTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// This callback will be called for each AVB transmit interval. Commonly this will be +// 4000 or 8000 times per second. +bool openavbIntfNullTxCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + if (pMediaQ) { + //pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + + media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (pMediaQItem) { + openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime); + + // Set 1 byte + *(U8 *)(pMediaQItem->pPubData) = 0xff; + pMediaQItem->dataLen = 1; + + openavbMediaQHeadPush(pMediaQ); + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return TRUE; + } + else { + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; // Media queue full + } + } + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; +} + +// A call to this callback indicates that this interface module will be +// a listener. Any listener initialization can be done in this function. +void openavbIntfNullRxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// This callback is called when acting as a listener. +bool openavbIntfNullRxCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + if (pMediaQ) { + bool moreItems = TRUE; + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + while (moreItems) { + media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, pPvtData->ignoreTimestamp); + if (pMediaQItem) { + openavbMediaQTailPull(pMediaQ); + } + else { + moreItems = FALSE; + } + } + } + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; +} + +// This callback will be called when the interface needs to be closed. All shutdown should +// occur in this function. +void openavbIntfNullEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +void openavbIntfNullGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Main initialization entry point into the interface module +extern DLL_EXPORT bool openavbIntfNullInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pMediaQ->pPvtIntfInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pPvtIntfInfo) { + AVB_LOG_ERROR("Unable to allocate memory for AVTP interface module."); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + + pIntfCB->intf_cfg_cb = openavbIntfNullCfgCB; + pIntfCB->intf_gen_init_cb = openavbIntfNullGenInitCB; + pIntfCB->intf_tx_init_cb = openavbIntfNullTxInitCB; + pIntfCB->intf_tx_cb = openavbIntfNullTxCB; + pIntfCB->intf_rx_init_cb = openavbIntfNullRxInitCB; + pIntfCB->intf_rx_cb = openavbIntfNullRxCB; + pIntfCB->intf_end_cb = openavbIntfNullEndCB; + pIntfCB->intf_gen_end_cb = openavbIntfNullGenEndCB; + + pPvtData->ignoreTimestamp = FALSE; + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return TRUE; +} diff --git a/lib/avtp_pipeline/intf_tonegen/CMakeLists.txt b/lib/avtp_pipeline/intf_tonegen/CMakeLists.txt new file mode 100644 index 00000000..7b55d40f --- /dev/null +++ b/lib/avtp_pipeline/intf_tonegen/CMakeLists.txt @@ -0,0 +1,4 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/intf_tonegen/openavb_intf_tonegen.c + PARENT_SCOPE +) diff --git a/lib/avtp_pipeline/intf_tonegen/openavb_intf_tonegen.c b/lib/avtp_pipeline/intf_tonegen/openavb_intf_tonegen.c new file mode 100644 index 00000000..dc7babce --- /dev/null +++ b/lib/avtp_pipeline/intf_tonegen/openavb_intf_tonegen.c @@ -0,0 +1,524 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+/*
+* MODULE SUMMARY : Tone generator interface module. Talker only.
+*
+* - This interface module generates and audio tone for use with -6 and AAF mappings
+* - Requires an OSAL sin implementation of reasonable performance.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "openavb_platform_pub.h"
+#include "openavb_osal_pub.h"
+#include "openavb_types_pub.h"
+#include "openavb_trace_pub.h"
+#include "openavb_mediaq_pub.h"
+#include "openavb_map_uncmp_audio_pub.h"
+#include "openavb_map_aaf_audio_pub.h"
+#include "openavb_intf_pub.h"
+
+#define AVB_LOG_COMPONENT "Tone Gen Interface"
+#include "openavb_log_pub.h"
+
+#define PI 3.14159265358979f
+
+typedef struct {
+ /////////////
+ // Config data
+ /////////////
+ // intf_nv_tone_hz: The tone hz to generate
+ U32 toneHz;
+
+ // intf_nv_on_off_interval_msec: Interval for turning tone on and off. A value of zero will keep the tone on.
+ U32 onOffIntervalMSec;
+
+ // Simple melody
+ char *pMelodyString;
+
+ // intf_nv_audio_rate
+ avb_audio_rate_t audioRate;
+
+ // intf_nv_audio_type
+ avb_audio_bit_depth_t audioType;
+
+ // intf_nv_audio_bit_depth
+ avb_audio_bit_depth_t audioBitDepth;
+
+ // intf_nv_audio_endian
+ avb_audio_bit_depth_t audioEndian;
+
+ // intf_nv_channels
+ avb_audio_channels_t audioChannels;
+
+ /////////////
+ // Variable data
+ /////////////
+
+ // Packing interval
+ U32 intervalCounter;
+
+ // Keeps track of if the tone is currently on or off
+ U32 freq;
+
+ // Keeps track of how long before toggling the tone on / off
+ U32 freqCountdown;
+
+ // Ratio precalc
+ float ratio;
+
+ // Index to into the melody string
+ U32 melodyIdx;
+
+ // Length of the melody string
+ U32 melodyLen;
+
+} pvt_data_t;
+
+#define MSEC_PER_COUNT 250
+static void xGetMelodyToneAndDuration(char note, char count, U32 *freq, U32 *sampleMSec)
+{
+ switch (note) {
+ case 'A':
+ *freq = 220;
+ break;
+ case 'B':
+ *freq = 246;
+ break;
+ case 'C':
+ *freq = 261;
+ break;
+ case 'D':
+ *freq = 293;
+ break;
+ case 'E':
+ *freq = 329;
+ break;
+ case 'F':
+ *freq = 349;
+ break;
+ case 'G':
+ *freq = 391;
+ break;
+ case 'a':
+ *freq = 440;
+ break;
+ case 'b':
+ *freq = 493;
+ break;
+ case 'c':
+ *freq = 523;
+ break;
+ case 'd':
+ *freq = 587;
+ break;
+ case 'e':
+ *freq = 659;
+ break;
+ case 'f':
+ *freq = 698;
+ break;
+ case 'g':
+ *freq = 783;
+ break;
+ case '-':
+ default:
+ *freq = 0;
+ break;
+ }
+
+ switch (count) {
+ case '1':
+ *sampleMSec = MSEC_PER_COUNT * 1;
+ break;
+ case '2':
+ *sampleMSec = MSEC_PER_COUNT * 2;
+ break;
+ case '3':
+ *sampleMSec = MSEC_PER_COUNT * 3;
+ break;
+ case '4':
+ *sampleMSec = MSEC_PER_COUNT * 4;
+ break;
+ default:
+ *sampleMSec = MSEC_PER_COUNT * 4;
+ break;
+ }
+}
+
+static bool xSupportedMappingFormat(media_q_t *pMediaQ)
+{
+ if (pMediaQ) {
+ if (pMediaQ->pMediaQDataFormat) {
+ if (strcmp(pMediaQ->pMediaQDataFormat, MapUncmpAudioMediaQDataFormat) == 0 || strcmp(pMediaQ->pMediaQDataFormat, MapAVTPAudioMediaQDataFormat) == 0) {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+// Each configuration name value pair for this mapping will result in this callback being called.
+void openavbIntfToneGenCfgCB(media_q_t *pMediaQ, const char *name, const char *value)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+
+ char *pEnd;
+ U32 val;
+
+ if (pMediaQ) {
+ pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo;
+ if (!pPvtData) {
+ AVB_LOG_ERROR("Private interface module data not allocated.");
+ return;
+ }
+
+ media_q_pub_map_uncmp_audio_info_t *pPubMapUncmpAudioInfo;
+ pPubMapUncmpAudioInfo = (media_q_pub_map_uncmp_audio_info_t *)pMediaQ->pPubMapInfo;
+ if (!pPubMapUncmpAudioInfo) {
+ AVB_LOG_ERROR("Public map data for audio info not allocated.");
+ return;
+ }
+
+ if (strcmp(name, "intf_nv_tone_hz") == 0) {
+ pPvtData->toneHz = strtol(value, &pEnd, 10);
+ }
+
+ else if (strcmp(name, "intf_nv_on_off_interval_msec") == 0) {
+ pPvtData->onOffIntervalMSec = strtol(value, &pEnd, 10);
+ }
+
+ else if (strcmp(name, "intf_nv_melody_string") == 0) {
+ if (pPvtData->pMelodyString)
+ free(pPvtData->pMelodyString);
+ pPvtData->pMelodyString = strdup(value);
+ if (pPvtData->pMelodyString) {
+ pPvtData->melodyLen = strlen(pPvtData->pMelodyString);
+ }
+ }
+
+ else if (strcmp(name, "intf_nv_audio_rate") == 0) {
+ val = strtol(value, &pEnd, 10);
+ // TODO: Should check for specific values
+ if (val >= AVB_AUDIO_RATE_8KHZ && val <= AVB_AUDIO_RATE_192KHZ) {
+ pPvtData->audioRate = (avb_audio_rate_t)val;
+ }
+ else {
+ AVB_LOG_ERROR("Invalid audio rate configured for intf_nv_audio_rate.");
+ pPvtData->audioRate = AVB_AUDIO_RATE_44_1KHZ;
+ }
+
+ // Give the audio parameters to the mapping module.
+ if (xSupportedMappingFormat(pMediaQ)) {
+ pPubMapUncmpAudioInfo->audioRate = pPvtData->audioRate;
+ }
+ }
+
+ else if (strcmp(name, "intf_nv_audio_bit_depth") == 0) {
+ val = strtol(value, &pEnd, 10);
+ // TODO: Should check for specific values
+ if (val >= AVB_AUDIO_BIT_DEPTH_1BIT && val <= AVB_AUDIO_BIT_DEPTH_64BIT) {
+ pPvtData->audioBitDepth = (avb_audio_bit_depth_t)val;
+ }
+ else {
+ AVB_LOG_ERROR("Invalid audio type configured for intf_nv_audio_bits.");
+ pPvtData->audioBitDepth = AVB_AUDIO_BIT_DEPTH_24BIT;
+ }
+
+ // Give the audio parameters to the mapping module.
+ if (xSupportedMappingFormat(pMediaQ)) {
+ pPubMapUncmpAudioInfo->audioBitDepth = pPvtData->audioBitDepth;
+ }
+ }
+
+ else if (strcmp(name, "intf_nv_audio_type") == 0) {
+ if (strncasecmp(value, "float", 5) == 0)
+ pPvtData->audioType = (avb_audio_bit_depth_t)AVB_AUDIO_TYPE_FLOAT;
+ else if (strncasecmp(value, "sign", 4) == 0 || strncasecmp(value, "int", 4) == 0)
+ pPvtData->audioType = (avb_audio_bit_depth_t)AVB_AUDIO_TYPE_INT;
+ else if (strncasecmp(value, "unsign", 6) == 0 || strncasecmp(value, "uint", 4) == 0)
+ pPvtData->audioType = (avb_audio_bit_depth_t)AVB_AUDIO_TYPE_UINT;
+ else {
+ AVB_LOG_ERROR("Invalid audio type configured for intf_nv_audio_type.");
+ pPvtData->audioType = (avb_audio_bit_depth_t)AVB_AUDIO_TYPE_UNSPEC;
+ }
+
+ // Give the audio parameters to the mapping module.
+ if (xSupportedMappingFormat(pMediaQ)) {
+ pPubMapUncmpAudioInfo->audioType = (avb_audio_type_t)pPvtData->audioType;
+ }
+ }
+
+ else if (strcmp(name, "intf_nv_audio_endian") == 0) {
+ if (strncasecmp(value, "big", 3) == 0)
+ pPvtData->audioType = (avb_audio_bit_depth_t)AVB_AUDIO_ENDIAN_BIG;
+ else if (strncasecmp(value, "little", 6) == 0)
+ pPvtData->audioType = (avb_audio_bit_depth_t)AVB_AUDIO_ENDIAN_LITTLE;
+ else {
+ AVB_LOG_ERROR("Invalid audio type configured for intf_nv_audio_endian.");
+ pPvtData->audioType = (avb_audio_bit_depth_t)AVB_AUDIO_ENDIAN_UNSPEC;
+ }
+
+ // Give the audio parameters to the mapping module.
+ if (xSupportedMappingFormat(pMediaQ)) {
+ pPubMapUncmpAudioInfo->audioEndian = (avb_audio_endian_t)pPvtData->audioEndian;
+ }
+ }
+
+ else if (strcmp(name, "intf_nv_audio_channels") == 0) {
+ val = strtol(value, &pEnd, 10);
+ // TODO: Should check for specific values
+ if (val >= AVB_AUDIO_CHANNELS_1 && val <= AVB_AUDIO_CHANNELS_8) {
+ pPvtData->audioChannels = (avb_audio_channels_t)val;
+ }
+ else {
+ AVB_LOG_ERROR("Invalid audio channels configured for intf_nv_audio_channels.");
+ pPvtData->audioChannels = (avb_audio_channels_t)AVB_AUDIO_CHANNELS_2;
+ }
+
+ // Give the audio parameters to the mapping module.
+ if (xSupportedMappingFormat(pMediaQ)) {
+ pPubMapUncmpAudioInfo->audioChannels = pPvtData->audioChannels;
+ }
+ }
+
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+void openavbIntfToneGenGenInitCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+// A call to this callback indicates that this interface module will be
+// a talker. Any talker initialization can be done in this function.
+void openavbIntfToneGenTxInitCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+
+ if (pMediaQ) {
+
+ // U8 b;
+ pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo;
+ if (!pPvtData) {
+ AVB_LOG_ERROR("Private interface module data not allocated.");
+ return;
+ }
+
+ media_q_pub_map_uncmp_audio_info_t *pPubMapUncmpAudioInfo;
+ pPubMapUncmpAudioInfo = (media_q_pub_map_uncmp_audio_info_t *)pMediaQ->pPubMapInfo;
+ if (!pPubMapUncmpAudioInfo) {
+ AVB_LOG_ERROR("Public map data for audio info not allocated.");
+ return;
+ }
+
+ // Will get toggle on at the first tx cb
+ if (pPvtData->onOffIntervalMSec > 0) {
+ pPvtData->freq = pPvtData->toneHz;
+ pPvtData->freqCountdown = 0;
+ }
+ else {
+ pPvtData->freq = 0;
+ pPvtData->freqCountdown = 0;
+ }
+
+ pPvtData->melodyIdx = 0;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+// This callback will be called for each AVB transmit interval. Commonly this will be
+// 4000 or 8000 times per second.
+bool openavbIntfToneGenTxCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL);
+
+ if (pMediaQ) {
+ media_q_pub_map_uncmp_audio_info_t *pPubMapUncmpAudioInfo = pMediaQ->pPubMapInfo;
+ pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo;
+ if (!pPvtData) {
+ AVB_LOG_ERROR("Private interface module data not allocated.");
+ return FALSE;
+ }
+
+ if (pPvtData->intervalCounter++ % pPubMapUncmpAudioInfo->packingFactor != 0)
+ return TRUE;
+
+ media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ);
+ if (pMediaQItem) {
+ if (pMediaQItem->itemSize < pPubMapUncmpAudioInfo->itemSize) {
+ AVB_LOG_ERROR("Media queue item not large enough for samples");
+ }
+
+ // Tone on
+ static U32 runningFrameCnt = 0;
+ U32 frameCnt;
+ U32 channelCnt;
+ U32 idx = 0;
+ for (frameCnt = 0; frameCnt < pPubMapUncmpAudioInfo->framesPerItem; frameCnt++) {
+
+ // Check for tone on / off toggle
+ if (!pPvtData->freqCountdown) {
+ if (pPvtData->pMelodyString) {
+ // Melody logic
+ U32 intervalMSec;
+ xGetMelodyToneAndDuration(
+ pPvtData->pMelodyString[pPvtData->melodyIdx],
+ pPvtData->pMelodyString[pPvtData->melodyIdx + 1],
+ &pPvtData->freq, &intervalMSec);
+ pPvtData->melodyIdx += 2;
+
+ pPvtData->freqCountdown = (pPubMapUncmpAudioInfo->audioRate / 1000) * intervalMSec;
+ if (pPvtData->melodyIdx >= pPvtData->melodyLen)
+ pPvtData->melodyIdx = 0;
+ }
+ else {
+ // Fixed tone
+ if (pPvtData->onOffIntervalMSec > 0) {
+ if (pPvtData->freq == 0)
+ pPvtData->freq = pPvtData->toneHz;
+ else
+ pPvtData->freq = 0;
+ pPvtData->freqCountdown = (pPubMapUncmpAudioInfo->audioRate / 1000) * pPvtData->onOffIntervalMSec;
+ }
+ else {
+ pPvtData->freqCountdown = pPubMapUncmpAudioInfo->audioRate; // Just run steady for 1 sec
+ }
+ }
+ pPvtData->ratio = pPvtData->freq / (float)pPubMapUncmpAudioInfo->audioRate;
+ }
+ pPvtData->freqCountdown--;
+
+
+ float value = SIN(2 * PI * (runningFrameCnt++ % pPubMapUncmpAudioInfo->audioRate) * pPvtData->ratio);
+
+ if (pPubMapUncmpAudioInfo->itemSampleSizeBytes == 2) {
+ // 16 bit audio
+ S16 sample = (S16)(value * 15000);
+ for (channelCnt = 0; channelCnt < pPubMapUncmpAudioInfo->audioChannels; channelCnt++) {
+ unsigned char c;
+ U8 *pData = pMediaQItem->pPubData;
+ c = (unsigned)sample % 256;
+ pData[idx++] = c;
+ c = (unsigned)sample / 256 % 256;
+ pData[idx++] = c;
+ }
+ }
+ else {
+ // CORE_TODO
+ AVB_LOG_ERROR("Audio sample size format not implemented yet for tone generator interface module");
+ }
+ }
+
+ pMediaQItem->dataLen = pPubMapUncmpAudioInfo->itemSize;
+
+ openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime);
+ openavbMediaQHeadPush(pMediaQ);
+
+ AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL);
+ return TRUE;
+ }
+ else {
+ AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL);
+ return FALSE; // Media queue full
+ }
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL);
+ return FALSE;
+}
+
+// A call to this callback indicates that this interface module will be
+// a listener. Any listener initialization can be done in this function.
+void openavbIntfToneGenRxInitCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+// This callback is called when acting as a listener.
+bool openavbIntfToneGenRxCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL);
+ return FALSE;
+}
+
+// This callback will be called when the interface needs to be closed. All shutdown should
+// occur in this function.
+void openavbIntfToneGenEndCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+void openavbIntfToneGenGenEndCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+// Main initialization entry point into the interface module
+extern DLL_EXPORT bool openavbIntfToneGenInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+
+ if (pMediaQ) {
+ pMediaQ->pPvtIntfInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed.
+
+ if (!pMediaQ->pPvtIntfInfo) {
+ AVB_LOG_ERROR("Unable to allocate memory for AVTP interface module.");
+ return FALSE;
+ }
+
+ pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo;
+
+ pIntfCB->intf_cfg_cb = openavbIntfToneGenCfgCB;
+ pIntfCB->intf_gen_init_cb = openavbIntfToneGenGenInitCB;
+ pIntfCB->intf_tx_init_cb = openavbIntfToneGenTxInitCB;
+ pIntfCB->intf_tx_cb = openavbIntfToneGenTxCB;
+ pIntfCB->intf_rx_init_cb = openavbIntfToneGenRxInitCB;
+ pIntfCB->intf_rx_cb = openavbIntfToneGenRxCB;
+ pIntfCB->intf_end_cb = openavbIntfToneGenEndCB;
+ pIntfCB->intf_gen_end_cb = openavbIntfToneGenGenEndCB;
+
+ pPvtData->intervalCounter = 0;
+ pPvtData->melodyIdx = 0;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+ return TRUE;
+}
diff --git a/lib/avtp_pipeline/intf_tonegen/openavb_intf_tonegen.md b/lib/avtp_pipeline/intf_tonegen/openavb_intf_tonegen.md new file mode 100644 index 00000000..65143e1a --- /dev/null +++ b/lib/avtp_pipeline/intf_tonegen/openavb_intf_tonegen.md @@ -0,0 +1,30 @@ +Tone Generator Interface {#tongen_intf} +======================== + +# Description + +This interface module is a talker only and is used to generate an audio +tone for testing purposes. It is designed to work with the AAF (AVTP +Audio Format) mapping but could be quickly adjusted to work with the +61883-6 mapping module as well. + +# Interface module configuration parameters + +Name | Description +-----------------------------|--------------------------- +intf_nv_tone_hz | The frequency of the generated tone +intf_nv_on_off_interval_msec | How many millisecs to turn on and off the tone +intf_nv_melody_string | A simple melody to play. When a melody string is \ + set the on/off tone hz is not used. The string \ + holds the notes in a 2 octave range with the use \ + of letters A B C D E F G a b c d e f g followed \ + by a duration of the note. For example the full \ + scale is A4B4C4D4E4F4G4a4b4c4d4e4f4g4. +intf_nv_audio_rate | Audio rate, numberic values defined by \ + @ref avb_audio_rate_t +intf_nv_audio_bit_depth | Bit depth of audio, numeric values defined by \ + @ref avb_audio_bit_depth_t +intf_nv_audio_channels | Number of audio channels, numeric values should \ + be within range of values in \ + @ref avb_audio_channels_t + diff --git a/lib/avtp_pipeline/intf_tonegen/tonegen_talker.ini b/lib/avtp_pipeline/intf_tonegen/tonegen_talker.ini new file mode 100644 index 00000000..dbbc9f2d --- /dev/null +++ b/lib/avtp_pipeline/intf_tonegen/tonegen_talker.ini @@ -0,0 +1,148 @@ +##################################################################### +# General Talker configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 00:25:64:48:ca:a8 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: destination multicast address for the stream. +# +# If using SRP and MAAP, dynamic destination addresses are generated +# automatically by the talker and passed to the listner, and don't +# need to be configured. +# +# Without MAAP, locally administered (static) addresses must be +# configured. Thouse addresses are in the range of: +# 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically use :00 for the first stream, :01 for the second, etc. +# +# When SRP is being used the static destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set (to the same value) in both the talker and listener. +# +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +sr_class = A + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 2000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_aaf_audio.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapAVTPAudioInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +# map_nv_tx_rate: Transmit rate. +# This must be set for the uncompressed audio mapping module. +map_nv_tx_rate = 8000 + +# map_nv_packing_factor: Multiple of how many packets of audio frames to place in a media queue item. +# Note: Typically when decreasing the map_nv_tx_rate the packing factor will also be decreased since +# the number of frames per packet will be increasing. +map_nv_packing_factor = 1 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +# intf_fn: The name of the initialize function in the interface. +intf_lib = ./libopenavb_intf_tonegen.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfTonegenInitialize + +# intf_nv_tone_hz: The frequence of the tone +intf_nv_tone_hz = 261 + +# intf_nv_on_off_interval_msec: How many millisecs to turn on and off the tone +intf_nv_on_off_interval_msec = 1000 + +# intf_nv_melody_string: A simple melody to play. When a melody string is set the on/off tone hz is not used. +# Full range of supported notes +# intf_nv_melody_string = A4B4C4D4E4F4G4a4b4c4d4e4f4g4 +# DoRaMi... +# intf_nv_melody_string = A4B4C4D4E4F4G4a4 +# Twinkle Twinkle little star +# intf_nv_melody_string = C2C2G2G2a2a2G2 2F2F2E2E2D2D2C2 2C2C2G2G2a2a2G2 2F2F2E2E2D2D2C2 2G2G2F2F2E2E2D2 2G2G2F2F2E2E2D2 2C2C2G2G2a2a2G2 2F2F2E2E2D2D2C2 2 + +# intf_nv_audio_rate: Sampling rate of the generated audio +intf_nv_audio_rate = 48000 + +# intf_nv_audio_bit_depth: Bit depth of the generated audio +intf_nv_audio_bit_depth = 16 + +# intf_nv_audio_channels: The number of channels of the generated audio +intf_nv_audio_channels = 2 + + + + + + + diff --git a/lib/avtp_pipeline/intf_viewer/CMakeLists.txt b/lib/avtp_pipeline/intf_viewer/CMakeLists.txt new file mode 100644 index 00000000..0cf98998 --- /dev/null +++ b/lib/avtp_pipeline/intf_viewer/CMakeLists.txt @@ -0,0 +1,6 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/intf_viewer/openavb_intf_viewer.c + PARENT_SCOPE +) + + diff --git a/lib/avtp_pipeline/intf_viewer/latency_listener.ini b/lib/avtp_pipeline/intf_viewer/latency_listener.ini new file mode 100644 index 00000000..7f83632c --- /dev/null +++ b/lib/avtp_pipeline/intf_viewer/latency_listener.ini @@ -0,0 +1,126 @@ +##################################################################### +# The Latency Talker and Latency Listener configurations are designed +# to test the total latency from the talker to the listener. This +# includes both network tranmission and AVB stack overhead. The max +# latency reported at the listener over time can be used as a basis for +# setting up the max_transit_usec in a real configuration to allow for +# synchronization of multiple listeners. To fine tune this max_transit_usec +# setting the real talker should be used. +##################################################################### + +##################################################################### +# General Listener configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = listener + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +stream_addr = 84:7E:40:2C:8F:DE + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: see description in talker.ini +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +#max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +#sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +#max_transit_usec = 2000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_pipe.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapPipeInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +#map_nv_max_payload_size: The maximum payload size that the pipe will use. +map_nv_max_payload_size = 200 + +# map_nv_push_header +map_nv_push_header = 1 + +# map_nv_pull_header +map_nv_pull_header = 1 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +intf_lib = ./libopenavb_intf_viewer.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfViewerInitialize + +# intf_nv_view_type: Type of viewing output. +intf_nv_view_type = 3 + +# intf_nv_view_interval: Frequency of output (in packet count) +intf_nv_view_interval = 4000 + +# intf_nv_raw_offset: Offest into the raw frame to output +intf_nv_raw_offset = 0 + +# intf_nv_raw_length: Length of the raw frame to output. 0 = all. +intf_nv_raw_length = 0 + +# intf_nv_ignore_timestamp: If set the listener will ignore the timestamp on media queue items. +intf_nv_ignore_timestamp = 1 + + diff --git a/lib/avtp_pipeline/intf_viewer/latency_talker.ini b/lib/avtp_pipeline/intf_viewer/latency_talker.ini new file mode 100644 index 00000000..823ae3b9 --- /dev/null +++ b/lib/avtp_pipeline/intf_viewer/latency_talker.ini @@ -0,0 +1,168 @@ +##################################################################### +# The Latency Talker and Latency Listener configurations are designed +# to test the total latency from the talker to the listener. This +# includes both network tranmission and AVB stack overhead. The max +# latency reported at the listener over time can be used as a basis for +# setting up the max_transit_usec in a real configuration to allow for +# synchronization of multiple listeners. To fine tune this max_transit_usec +# setting the real talker should be used. +##################################################################### + +##################################################################### +# General Talker configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 00:25:64:48:ca:a8 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: destination multicast address for the stream. +# +# If using SRP and MAAP, dynamic destination addresses are generated +# automatically by the talker and passed to the listner, and don't +# need to be configured. +# +# Without MAAP, locally administered (static) addresses must be +# configured. Thouse addresses are in the range of: +# 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically use :00 for the first stream, :01 for the second, etc. +# +# When SRP is being used the static destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set (to the same value) in both the talker and listener. +# +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +#sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 0 + +# max_transmit_deficit_usec: Allows setting the maximum packet transmit rate deficit that will +# be recovered when a talker falls behind. This is only used on a talker side. When a talker +# can not keep up with the specified transmit rate it builds up a deficit and will attempt to +# make up for this deficit by sending more packets. There is normally some variability in the +# transmit rate because of other demands on the system so this is expected. However, without this +# bounding value the deficit could grew too large in cases such where more streams are started +# than the system can support and when the number of streams is reduced the remaining streams +# will attempt to recover this deficit by sending packets at a higher rate. This can cause a problem +# at the listener side and significantly delay the recovery time before media playback will return +# to normal. Typically this value can be set to the expected buffer size (in usec) that listeners are +# expected to be buffering. For low latency solutions this is normally a small value. For non-live +# media playback such as video playback the listener side buffers can often be large enough to held many +# seconds of data. +max_transmit_deficit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_pipe.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapPipeInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +# map_nv_tx_rate: Transmit rate. +# If not set default of the talker class will be used. +#map_nv_tx_rate = 4000 + +#map_nv_max_payload_size: The maximum payload size that the pipe will use. +map_nv_max_payload_size = 100; + +# map_nv_push_header +map_nv_push_header = 0 + +# map_nv_pull_header +map_nv_pull_header = 0 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +intf_lib = ./libopenavb_intf_echo.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfEchoInitialize + +# intf_nv_echo_string: Echo string used by the talker. +intf_nv_echo_string = Latency test: + +# intf_nv_echo_string_repeat: Number of copies of the string to send in each packet. +# The repeat setting if used must come after the intf_nv_echo_string in this file. +intf_nv_echo_string_repeat = 1 + +# intf_nv_echo_increment: Append an incrementing number to the string. +intf_nv_echo_increment = 1 + +# intf_nv_tx_local_echo: Output locally at the talker. +# intf_nv_tx_local_echo = 1 + +# intf_nv_echo_no_newline: Do not include a newline in the stdout output. +# intf_nv_echo_no_newline = 1 + + + + + + diff --git a/lib/avtp_pipeline/intf_viewer/openavb_intf_viewer.c b/lib/avtp_pipeline/intf_viewer/openavb_intf_viewer.c new file mode 100755 index 00000000..2f7139c9 --- /dev/null +++ b/lib/avtp_pipeline/intf_viewer/openavb_intf_viewer.c @@ -0,0 +1,520 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+/*
+* MODULE SUMMARY : Viewer interface module.
+*
+* This interface module is only a listener and is used to simply display
+* contents recieved in a number of different formats. Additionally it is
+* mapping type aware and can display header values for different mappings
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include "openavb_platform_pub.h"
+#include "openavb_types_pub.h"
+#include "openavb_trace_pub.h"
+#include "openavb_mediaq_pub.h"
+#include "openavb_intf_pub.h"
+
+#define AVB_LOG_COMPONENT "Viewer Interface"
+#include "openavb_log_pub.h"
+
+typedef enum {
+ VIEWER_MODE_DETAIL = 0,
+ VIEWER_MODE_MAPPING_AWARE = 1,
+ VIEWER_MODE_AVTP_TIMESTAMP = 2,
+ VIEWER_MODE_LATENCY = 3,
+ VIEWER_MODE_SELECTIVE_TIMESTAMP = 4,
+ VIEWER_MODE_LATE = 5,
+ VIEWER_MODE_GAP = 6,
+} viewer_mode_t;
+
+typedef struct {
+ /////////////
+ // Config data
+ /////////////
+ viewer_mode_t viewType;
+
+ // Frequency of output
+ U32 viewInterval;
+
+ // Offest into the raw frame to output
+ U32 rawOffset;
+
+ // Length of the raw frame to output
+ U32 rawLength;
+
+ // Ignore timestamp at listener.
+ bool ignoreTimestamp;
+
+ /////////////
+ // Variable data
+ /////////////
+ U32 servicedCount;
+
+ S64 accumLateNS;
+
+ S32 maxLateNS;
+
+ U64 accumGapNS;
+
+ U32 maxGapNS;
+
+ U64 prevNowTime;
+
+ S64 accumAvtpDeltaNS;
+
+ S32 maxAvtpDeltaNS;
+
+ U64 prevAvtpTimestampTime;
+
+ U32 skipCountdown;
+
+ float jitter;
+
+ S32 avgForJitter;
+
+} pvt_data_t;
+
+// Each configuration name value pair for this mapping will result in this callback being called.
+void openavbIntfViewerCfgCB(media_q_t *pMediaQ, const char *name, const char *value)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+
+ if (pMediaQ) {
+ char *pEnd;
+ long tmp;
+
+ pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo;
+ if (!pPvtData) {
+ AVB_LOG_ERROR("Private interface module data not allocated.");
+ return;
+ }
+
+ // intf_nv_view_type: Type of viewing output. 0 = raw, 1 = mapping aware, 2 = timestamps only, 3 = latency output, 4 = Selective timestamp error reporting
+ if (strcmp(name, "intf_nv_view_type") == 0) {
+ pPvtData->viewType = (viewer_mode_t)strtol(value, &pEnd, 10);
+ }
+
+ else if (strcmp(name, "intf_nv_view_interval") == 0) {
+ pPvtData->viewInterval = strtol(value, &pEnd, 10);
+ if (pPvtData->viewInterval == 0) {
+ pPvtData->viewInterval = 1000;
+ }
+ }
+
+ else if (strcmp(name, "intf_nv_raw_offset") == 0) {
+ pPvtData->rawOffset = strtol(value, &pEnd, 10);
+ }
+
+ else if (strcmp(name, "intf_nv_raw_length") == 0) {
+ pPvtData->rawLength = strtol(value, &pEnd, 10);
+ if (pPvtData->rawLength < 1)
+ pPvtData->rawLength = 1000;
+ }
+
+ else if (strcmp(name, "intf_nv_ignore_timestamp") == 0) {
+ tmp = strtol(value, &pEnd, 10);
+ if (*pEnd == '\0' && tmp == 1) {
+ pPvtData->ignoreTimestamp = (tmp == 1);
+ }
+ }
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+void openavbIntfViewerGenInitCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+// No talker functionality in this interface
+void openavbIntfViewerTxInitCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+// No talker functionality in this interface
+bool openavbIntfViewerTxCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL);
+ return FALSE;
+}
+
+// A call to this callback indicates that this interface module will be
+// a listener. Any listener initialization can be done in this function.
+void openavbIntfViewerRxInitCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+
+ if (pMediaQ) {
+ pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo;
+ if (!pPvtData) {
+ AVB_LOG_ERROR("Private interface module data not allocated.");
+ return;
+ }
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+// This callback is called when acting as a listener.
+bool openavbIntfViewerRxCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL);
+ if (pMediaQ) {
+ pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo;
+ if (!pPvtData) {
+ AVB_LOG_ERROR("Private interface module data not allocated.");
+ return FALSE;
+ }
+
+ media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, pPvtData->ignoreTimestamp);
+ if (pMediaQItem) {
+
+ // The skip countdown allow the viewer modes to set a number of packets to ignore
+ // after logging to reduce or eliminate the logging from affecting the stats.
+ if (pPvtData->skipCountdown)
+ pPvtData->skipCountdown--;
+
+ if (pMediaQItem->dataLen && !pPvtData->skipCountdown) {
+ pPvtData->servicedCount++;
+
+ if (pPvtData->viewType == VIEWER_MODE_DETAIL) {
+ U32 avtpTimestamp;
+ U64 avtpTimestampTime;
+ bool avtpTimestampValid;
+ U32 nowTimestamp;
+ U64 nowTimestampTime;
+ bool nowTimestampValid;
+ U64 nowTime;
+ S32 lateNS = 0;
+ U64 gapNS = 0;
+
+ avtpTimestamp = openavbAvtpTimeGetAvtpTimestamp(pMediaQItem->pAvtpTime);
+ avtpTimestampTime = openavbAvtpTimeGetAvtpTimeNS(pMediaQItem->pAvtpTime);
+ avtpTimestampValid = openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime);
+
+ openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime);
+ nowTimestamp = openavbAvtpTimeGetAvtpTimestamp(pMediaQItem->pAvtpTime);
+ nowTimestampTime = openavbAvtpTimeGetAvtpTimeNS(pMediaQItem->pAvtpTime);
+ nowTimestampValid = openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime);
+
+ CLOCK_GETTIME64(OPENAVB_CLOCK_REALTIME, &nowTime);
+
+ if (avtpTimestampValid && nowTimestampValid) {
+ lateNS = nowTimestampTime - avtpTimestampTime;
+ if (lateNS > pPvtData->maxLateNS) {
+ pPvtData->maxLateNS = lateNS;
+ }
+ pPvtData->accumLateNS += lateNS;
+
+ if (pPvtData->servicedCount > 1) {
+ gapNS = nowTime - pPvtData->prevNowTime;
+ if (gapNS > pPvtData->maxGapNS) {
+ pPvtData->maxGapNS = gapNS;
+ }
+ pPvtData->accumGapNS += gapNS;
+ }
+ pPvtData->prevNowTime = nowTime;
+
+ if ((pPvtData->servicedCount % pPvtData->viewInterval) == 0) {
+ S32 lateAvg = pPvtData->accumLateNS / pPvtData->servicedCount;
+ S32 gapAvg = pPvtData->accumGapNS / (pPvtData->servicedCount - 1);
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "****************************", LOG_RT_DATATYPE_CONST_STR, NULL);
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Packets: %u", LOG_RT_DATATYPE_U32, &pPvtData->servicedCount);
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "AVTP Timestamp: %u NS", LOG_RT_DATATYPE_U32, &avtpTimestamp);
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Now Timestamp: %u NS", LOG_RT_DATATYPE_U32, &nowTimestamp);
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Late: %d NS", LOG_RT_DATATYPE_S32, &lateNS);
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Late Avg: %d NS", LOG_RT_DATATYPE_S32, &lateAvg);
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Late Max: %d NS", LOG_RT_DATATYPE_S32, &pPvtData->maxLateNS);
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Gap: %u NS", LOG_RT_DATATYPE_U32, &gapNS);
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Gap Avg: %u NS", LOG_RT_DATATYPE_U32, &gapAvg);
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Gap Max: %u NS", LOG_RT_DATATYPE_U32, &pPvtData->maxGapNS);
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Data length: %u", LOG_RT_DATATYPE_U32, &pMediaQItem->dataLen);
+
+ pPvtData->accumLateNS = 0;
+ pPvtData->maxLateNS = 0;
+ pPvtData->accumGapNS = 0;
+ pPvtData->maxGapNS = 0;
+ pPvtData->prevNowTime = 0;
+ pPvtData->servicedCount = 0;
+ pPvtData->skipCountdown = 10;
+ }
+ }
+ }
+
+ else if (pPvtData->viewType == VIEWER_MODE_MAPPING_AWARE) {
+ }
+
+ else if (pPvtData->viewType == VIEWER_MODE_AVTP_TIMESTAMP) {
+ U64 avtpTimestampTime;
+ bool avtpTimestampValid;
+ S32 deltaNS = 0;
+
+ avtpTimestampTime = openavbAvtpTimeGetAvtpTimeNS(pMediaQItem->pAvtpTime);
+ avtpTimestampValid = openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime);
+
+ if (avtpTimestampValid) {
+ if (pPvtData->servicedCount > 1) {
+ deltaNS = avtpTimestampTime - pPvtData->prevAvtpTimestampTime;
+ if (deltaNS > pPvtData->maxAvtpDeltaNS) {
+ pPvtData->maxAvtpDeltaNS = deltaNS;
+ }
+ pPvtData->accumAvtpDeltaNS += deltaNS;
+
+ if (pPvtData->avgForJitter != 0) {
+ S32 deltaJitter = pPvtData->avgForJitter - deltaNS;
+ if (deltaJitter < 0) deltaJitter = -deltaJitter;
+ pPvtData->jitter += (1.0/16.0) * ((float)deltaJitter - pPvtData->jitter);
+ }
+ }
+ pPvtData->prevAvtpTimestampTime = avtpTimestampTime;
+
+ if ((pPvtData->servicedCount % pPvtData->viewInterval) == 0) {
+ S32 deltaAvg = pPvtData->accumAvtpDeltaNS / (pPvtData->servicedCount - 1);
+ U32 jitter = (U32)(pPvtData->jitter);
+
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, FALSE, "AVTP Timestamp Delta: %d NS ", LOG_RT_DATATYPE_S32, &deltaNS);
+ AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "AVTP Timestamp Delta Avg: %d NS ", LOG_RT_DATATYPE_S32, &deltaAvg);
+ AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "AVTP Timestamp Delta Max: %d NS ", LOG_RT_DATATYPE_S32, &pPvtData->maxAvtpDeltaNS);
+ AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, LOG_RT_END, "Jitter: %d", LOG_RT_DATATYPE_S32, &jitter);
+
+ pPvtData->accumAvtpDeltaNS = 0;
+ pPvtData->maxAvtpDeltaNS = 0;
+ pPvtData->servicedCount = 0;
+ pPvtData->prevAvtpTimestampTime = 0;
+ pPvtData->skipCountdown = 10;
+ pPvtData->jitter = 0.0;
+ pPvtData->avgForJitter = deltaAvg;
+ }
+ }
+ }
+
+ else if (pPvtData->viewType == VIEWER_MODE_LATENCY) {
+ U64 avtpTimestampTime;
+ bool avtpTimestampValid;
+ U64 nowTimestampTime;
+ bool nowTimestampValid;
+ S32 lateNS = 0;
+
+ avtpTimestampTime = openavbAvtpTimeGetAvtpTimeNS(pMediaQItem->pAvtpTime);
+ avtpTimestampValid = openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime);
+
+ openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime);
+ nowTimestampTime = openavbAvtpTimeGetAvtpTimeNS(pMediaQItem->pAvtpTime);
+ nowTimestampValid = openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime);
+
+ if (avtpTimestampValid && nowTimestampValid) {
+ lateNS = nowTimestampTime - avtpTimestampTime;
+ if (lateNS > pPvtData->maxLateNS) {
+ pPvtData->maxLateNS = lateNS;
+ }
+ pPvtData->accumLateNS += lateNS;
+
+ if (pPvtData->avgForJitter != 0) {
+ S32 lateJitter = pPvtData->avgForJitter - lateNS;
+ if (lateJitter < 0) lateJitter = -lateJitter;
+ pPvtData->jitter += (1.0/16.0) * ((float)lateJitter - pPvtData->jitter);
+ }
+
+ if ((pPvtData->servicedCount % pPvtData->viewInterval) == 0) {
+ S32 lateAvg = pPvtData->accumLateNS / pPvtData->servicedCount;
+ U32 jitter = (U32)(pPvtData->jitter);
+
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, FALSE, "Latency: %d NS ", LOG_RT_DATATYPE_S32, &lateNS);
+ AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "Latency Avg: %d NS ", LOG_RT_DATATYPE_S32, &lateAvg);
+ AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "Latency Max: %d NS ", LOG_RT_DATATYPE_S32, &pPvtData->maxLateNS);
+ AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, LOG_RT_END, "Jitter: %d", LOG_RT_DATATYPE_S32, &jitter);
+
+ pPvtData->accumLateNS = 0;
+ pPvtData->maxLateNS = 0;
+ pPvtData->servicedCount = 0;
+ pPvtData->skipCountdown = 10;
+ pPvtData->jitter = 0.0;
+ pPvtData->avgForJitter = lateAvg;
+ }
+ }
+ }
+
+ else if (pPvtData->viewType == VIEWER_MODE_SELECTIVE_TIMESTAMP) {
+ }
+
+ else if (pPvtData->viewType == VIEWER_MODE_LATE) {
+ U64 avtpTimestampTime;
+ bool avtpTimestampValid;
+ U64 nowTimestampTime;
+ bool nowTimestampValid;
+ S32 lateNS = 0;
+
+ avtpTimestampTime = openavbAvtpTimeGetAvtpTimeNS(pMediaQItem->pAvtpTime);
+ avtpTimestampValid = openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime);
+
+ openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime);
+ nowTimestampTime = openavbAvtpTimeGetAvtpTimeNS(pMediaQItem->pAvtpTime);
+ nowTimestampValid = openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime);
+
+ if (avtpTimestampValid && nowTimestampValid) {
+ lateNS = nowTimestampTime - avtpTimestampTime;
+ if (lateNS > pPvtData->maxLateNS) {
+ pPvtData->maxLateNS = lateNS;
+ }
+ pPvtData->accumLateNS += lateNS;
+
+ if (pPvtData->avgForJitter != 0) {
+ S32 lateJitter = pPvtData->avgForJitter - lateNS;
+ if (lateJitter < 0) lateJitter = -lateJitter;
+ pPvtData->jitter += (1.0/16.0) * ((float)lateJitter - pPvtData->jitter);
+ }
+
+ if ((pPvtData->servicedCount % pPvtData->viewInterval) == 0) {
+ S32 lateAvg = pPvtData->accumLateNS / pPvtData->servicedCount;
+ U32 jitter = (U32)(pPvtData->jitter);
+
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, FALSE, "Late: %d NS ", LOG_RT_DATATYPE_S32, &lateNS);
+ AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "Late Avg: %d NS ", LOG_RT_DATATYPE_S32, &lateAvg);
+ AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "Late Max: %d NS ", LOG_RT_DATATYPE_S32, &pPvtData->maxLateNS);
+ AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, LOG_RT_END, "Jitter: %d", LOG_RT_DATATYPE_S32, &jitter);
+
+ pPvtData->accumLateNS = 0;
+ pPvtData->maxLateNS = 0;
+ pPvtData->servicedCount = 0;
+ pPvtData->skipCountdown = 10;
+ pPvtData->jitter = 0.0;
+ pPvtData->avgForJitter = lateAvg;
+ }
+ }
+ }
+
+ else if (pPvtData->viewType == VIEWER_MODE_GAP) {
+ U64 nowTime;
+ U64 gapNS = 0;
+
+ CLOCK_GETTIME64(OPENAVB_CLOCK_REALTIME, &nowTime);
+
+ if (pPvtData->servicedCount > 1) {
+ gapNS = nowTime - pPvtData->prevNowTime;
+ if (gapNS > pPvtData->maxGapNS) {
+ pPvtData->maxGapNS = gapNS;
+ }
+ pPvtData->accumGapNS += gapNS;
+
+ if (pPvtData->avgForJitter != 0) {
+ S32 gapJitter = pPvtData->avgForJitter - gapNS;
+ if (gapJitter < 0) gapJitter = -gapJitter;
+ pPvtData->jitter += (1.0/16.0) * ((float)gapJitter - pPvtData->jitter);
+ }
+ }
+ pPvtData->prevNowTime = nowTime;
+
+ if ((pPvtData->servicedCount % pPvtData->viewInterval) == 0) {
+ S32 gapAvg = pPvtData->accumGapNS / (pPvtData->servicedCount - 1);
+ U32 jitter = (U32)(pPvtData->jitter);
+
+ AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, FALSE, "Gap: %d NS ", LOG_RT_DATATYPE_S32, &gapNS);
+ AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "Gap Avg: %d NS ", LOG_RT_DATATYPE_S32, &gapAvg);
+ AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "Gap Max: %d NS ", LOG_RT_DATATYPE_S32, &pPvtData->maxGapNS);
+ AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, LOG_RT_END, "Jitter: %d", LOG_RT_DATATYPE_S32, &jitter);
+
+ pPvtData->accumGapNS = 0;
+ pPvtData->maxGapNS = 0;
+ pPvtData->prevNowTime = 0;
+ pPvtData->servicedCount = 0;
+ pPvtData->skipCountdown = 10;
+ pPvtData->jitter = 0.0;
+ pPvtData->avgForJitter = gapAvg;
+ }
+ }
+
+ }
+ openavbMediaQTailPull(pMediaQ);
+ }
+ }
+ AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL);
+ return TRUE;
+}
+
+
+// This callback will be called when the interface needs to be closed. All shutdown should
+// occur in this function.
+void openavbIntfViewerEndCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+void openavbIntfViewerGenEndCB(media_q_t *pMediaQ)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+}
+
+// Main initialization entry point into the interface module
+extern DLL_EXPORT bool openavbIntfViewerInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+
+ if (pMediaQ) {
+ pMediaQ->pPvtIntfInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed.
+
+ if (!pMediaQ->pPvtIntfInfo) {
+ AVB_LOG_ERROR("Unable to allocate memory for AVTP interface module.");
+ return FALSE;
+ }
+
+ pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo;
+
+ pIntfCB->intf_cfg_cb = openavbIntfViewerCfgCB;
+ pIntfCB->intf_gen_init_cb = openavbIntfViewerGenInitCB;
+ pIntfCB->intf_tx_init_cb = openavbIntfViewerTxInitCB;
+ pIntfCB->intf_tx_cb = openavbIntfViewerTxCB;
+ pIntfCB->intf_rx_init_cb = openavbIntfViewerRxInitCB;
+ pIntfCB->intf_rx_cb = openavbIntfViewerRxCB;
+ pIntfCB->intf_end_cb = openavbIntfViewerEndCB;
+ pIntfCB->intf_gen_end_cb = openavbIntfViewerGenEndCB;
+
+ pPvtData->viewType = VIEWER_MODE_DETAIL;
+ pPvtData->viewInterval = 1000;
+ pPvtData->rawOffset = 0;
+ pPvtData->rawLength = 20;
+ pPvtData->ignoreTimestamp = FALSE;
+ pPvtData->skipCountdown = 0;
+ pPvtData->jitter = 0.0;
+ pPvtData->avgForJitter = 0;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+ return TRUE;
+}
diff --git a/lib/avtp_pipeline/intf_viewer/viewer_intf.md b/lib/avtp_pipeline/intf_viewer/viewer_intf.md new file mode 100644 index 00000000..734823dc --- /dev/null +++ b/lib/avtp_pipeline/intf_viewer/viewer_intf.md @@ -0,0 +1,31 @@ +Viewer interface {#viewer_intf} +================ + +# Description + +Viewer interface module. + +The viewer interface module is a listener only module. It is designed +for testing purposes and will evaluate and display AVTP stream data in a +number of formats. + + +<br> +# Interface module configuration parameters + +Name | Description +--------------------------|--------------------------- +intf_nv_view_type | Mode of operation. Acceptable values are<ul> \ + <li>0 - Full details</li> \ + <li>1 - mapping aware (not implemented)</li> \ + <li>2 - Timestamp mode</li> \ + <li>3 - Latency measurement</li> \ + <li>4 - Selective timestamp mode</li> \ + <li>5 - Late packet measurement</li> \ + <li>6 - Packet gap measurement</li></ul> +intf_nv_view_interval | Frequency of output (in packet count) +intf_nv_raw_offset | Offset into the raw frame to output +intf_nv_raw_length | Length of the raw frame to output (0 = all) +intf_nv_ignore_timestamp | If set to 1 timestamps will be ignored during \ + processing of frames. This also means stale (old) \ + Media Queue items will not be purged. diff --git a/lib/avtp_pipeline/intf_viewer/viewer_listener.ini b/lib/avtp_pipeline/intf_viewer/viewer_listener.ini new file mode 100644 index 00000000..01e4f726 --- /dev/null +++ b/lib/avtp_pipeline/intf_viewer/viewer_listener.ini @@ -0,0 +1,123 @@ +##################################################################### +# General Listener configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = listener + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +stream_addr = 00:0c:29:f8:3e:c6 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: see description in talker.ini +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +#max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +#sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +#max_transit_usec = 2000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_pipe.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapPipeInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +#map_nv_max_payload_size: The maximum payload size that the pipe will use. +map_nv_max_payload_size = 1000; + +# map_nv_push_header +map_nv_push_header = 1 + +# map_nv_pull_header +map_nv_pull_header = 1 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +intf_lib = ./libopenavb_intf_viewer.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfViewerInitialize + +# intf_nv_view_type: Type of viewing output. +# VIEWER_MODE_DETAIL = 0 +# VIEWER_MODE_MAPPING_AWARE = 1 (Not implemented) +# VIEWER_MODE_AVTP_TIMESTAMP = 2 +# VIEWER_MODE_LATENCY = 3 +# VIEWER_MODE_SELECTIVE_TIMESTAMP = 4 +# VIEWER_MODE_LATE = 5 +# VIEWER_MODE_GAP = 6 +intf_nv_view_type = 0 + +# intf_nv_view_interval: Frequency of output (in packet count) +intf_nv_view_interval = 4000 + +# intf_nv_raw_offset: Offest into the raw frame to output +intf_nv_raw_offset = 0 + +# intf_nv_raw_length: Length of the raw frame to output. 0 = all. +intf_nv_raw_length = 0 + +# intf_nv_ignore_timestamp: If set the listener will ignore the timestamp on media queue items. +#intf_nv_ignore_timestamp = 1 + + diff --git a/lib/avtp_pipeline/map_aaf_audio/CMakeLists.txt b/lib/avtp_pipeline/map_aaf_audio/CMakeLists.txt new file mode 100644 index 00000000..26d7e5f1 --- /dev/null +++ b/lib/avtp_pipeline/map_aaf_audio/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/map_aaf_audio/openavb_map_aaf_audio.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/map_aaf_audio/README.TXT b/lib/avtp_pipeline/map_aaf_audio/README.TXT new file mode 100644 index 00000000..f59b628d --- /dev/null +++ b/lib/avtp_pipeline/map_aaf_audio/README.TXT @@ -0,0 +1,42 @@ +Notes on the AVTP Audio Format mapping module:
+
+Implements AAF (or "AVTP Audio Format") as described in IEEE-1722a (Draft 11).
+
+Works with the ALSA interface and the WAV file interface.
+
+The transmit rate (map_nv_tx_rate) for the mapping module
+should be set according to the sample rate, so that the transmit
+interval meets the suggested values in 1722a:
+
+For audio sample rates which are a multiple of 8000hz: use 8000 for class A, 4000 for class B
+For audio sample rates which are a multiple of 44100hz: use 7350 for class A, 3675 for class B
+
+AAF supports the following ALSA sample parameters:
+
+- sample rate:
+ intf_nv_audio_rate = 8, 16, 32, 44.1, 48, 88.2, 96, 176.4 or 192Khz
+- signed integer, unsigned integer or floating point samples
+ intf_nv_audio_type = int, uint or float
+- bit depth
+ intf_nv_audio_bit_depth = 16, 24 or 32 for integers; 32 for float
+- number of audio channels
+ intf_nv_audio_channels = 1 - 8
+- sample endian-ness
+ intf_nv_audio_end = big or little
+
+The ALSA library on various platforms will only support certain
+combinations of the sample parameters. Typically, only sample rate and
+bit depth need to be configured, and the other parameters may be left
+to their defaults.
+
+The WAV file interface will send data that matches the encoding of the
+WAV file.
+
+Both the talker and listner must be configured with matching sample
+parameters. If the received data does not match the configured
+parameters on the listener, the stream will still be setup and data
+will still flow - but no audio will be played.
+
+Sample talker and listener ini files are provided in aaf_talker.ini
+and aaf_listener.ini.
+
diff --git a/lib/avtp_pipeline/map_aaf_audio/aaf_audio_map.md b/lib/avtp_pipeline/map_aaf_audio/aaf_audio_map.md new file mode 100644 index 00000000..6b067a62 --- /dev/null +++ b/lib/avtp_pipeline/map_aaf_audio/aaf_audio_map.md @@ -0,0 +1,58 @@ +AAF audio Mapping {#aaf_audio_map} +================= + +# Description + +Implements AAF (or "AVTP Audio Format") as described in IEEE-1722a. + +Works with the ALSA interface and the WAV file interface. + +# Mapping module configuration parameters + +Name | Description +--------------------|--------------------------- +map_nv_item_count |The number of Media Queue items to hold. +map_nv_tx_rate or map_nv_tx_interval | Transmit interval in frames per second. \ + 0 = default for talker class. <br> The transmit rate for \ + the mapping module should be set according to the sample \ + rate, so that the transmit interval meets the suggested \ + values in 1722a <ul><li>sample rate which are multiple of \ + 8000Hz <ul><li>8000 for class <b>A</b></li><li>4000 for \ + class <b>B</b></li></ul></li><li>sample rate which are \ + multiple of 44100Hz<ul><li>7350 for class <b>A</b></li> \ + <li>3675 for class <b>B</b></li></ul></li></ul> +map_nv_packing_factor|How many AVTP packets worth of audio data to accept in one Media Queue item + +<br> +# Notes + +There are additional parameters that have to be set by the interface module +during the configuration process to ensure all sizes and rates calculate +properly inside the mapping module. Those variables have to be set before +*map_gen_init_cb* is being called. Commonly these additional values are set +during the interface module configuration which does occur before the +gen_init_cb. + +These are the fields of the @ref media_q_pub_map_uncmp_audio_info_t structure +that have to be set during the interface module configuration: + +Name | Description +-------------------|---------------------------- +audioRate |Rate of the audio @ref avb_audio_rate_t +audioType |How the data is organized - what is the data type of \ + samples @ref avb_audio_type_t +audioBitDepth |What is the bit depth of audio @ref avb_audio_bit_depth_t +audioChannels |How many channels there are @ref avb_audio_channels_t +sparseMode |Timestamping mode @ref avb_audio_sparse_mode_t + +Below you can find description of how to set up those variables in interfaces +* [wav file interface](@ref wav_file_intf) +* [alsa interface](@ref alsa_intf) + +**Note**: If any of these fields are not set correct the mapping module will not +configure the Media Queue correctly. + +**Note**: Both the talker and listener must be configured with matching audio +parameters. If the received data on the listener does not match the configured +parameters the stream will still be started and data +will still flow but no audio will be played. diff --git a/lib/avtp_pipeline/map_aaf_audio/openavb_map_aaf_audio.c b/lib/avtp_pipeline/map_aaf_audio/openavb_map_aaf_audio.c new file mode 100755 index 00000000..2cd4a82c --- /dev/null +++ b/lib/avtp_pipeline/map_aaf_audio/openavb_map_aaf_audio.c @@ -0,0 +1,791 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* + * MODULE SUMMARY : Implementation for AAF mapping module + * + * AAF is defined in IEEE 1722-rev1/D12 (still in draft as of Feb 2015). + */ + +#include <stdlib.h> +#include <string.h> +#include "openavb_mcr_hal_pub.h" +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_avtp_time_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_map_pub.h" +#include "openavb_map_aaf_audio_pub.h" + +#define AVB_LOG_COMPONENT "AAF Mapping" +#include "openavb_log_pub.h" + +#define AVTP_SUBTYPE_AAF 2 + +// Header sizes (bytes) +#define AVTP_V0_HEADER_SIZE 12 +#define AAF_HEADER_SIZE 12 +#define TOTAL_HEADER_SIZE (AVTP_V0_HEADER_SIZE + AAF_HEADER_SIZE) + +// - 1 Byte - TV bit (timestamp valid) +#define HIDX_AVTP_HIDE7_TV1 1 + +// - 1 Byte - TU bit (timestamp uncertain) +#define HIDX_AVTP_HIDE7_TU1 3 + +// - 1 Byte - SP bit (sparse mode) +#define HIDX_AVTP_HIDE7_SP 22 +#define SP_M0_BIT (1 << 4) + +typedef enum { + AAF_RATE_UNSPEC = 0, + AAF_RATE_8K, + AAF_RATE_16K, + AAF_RATE_32K, + AAF_RATE_44K1, + AAF_RATE_48K, + AAF_RATE_88K2, + AAF_RATE_96K, + AAF_RATE_176K4, + AAF_RATE_192K, +} aaf_nominal_sample_rate_t; + +typedef enum { + AAF_FORMAT_UNSPEC = 0, + AAF_FORMAT_FLOAT_32, + AAF_FORMAT_INT_32, + AAF_FORMAT_INT_24, + AAF_FORMAT_INT_16, +} aaf_sample_format_t; + +typedef enum { + AAF_STATIC_CHANNELS_LAYOUT = 0, + AAF_MONO_CHANNELS_LAYOUT = 1, + AAF_STEREO_CHANNELS_LAYOUT = 2, + AAF_5_1_CHANNELS_LAYOUT = 3, + AAF_7_1_CHANNELS_LAYOUT = 4, + AAF_MAX_CHANNELS_LAYOUT = 15, +} aaf_automotive_channels_layout_t; + +typedef struct { + ///////////// + // Config data + ///////////// + // map_nv_item_count + U32 itemCount; + + // Transmit interval in frames per second. 0 = default for talker class. + U32 txInterval; + + // A multiple of how many frames of audio to accept in an media queue item and + // into the AVTP payload above the minimal needed. + U32 packingFactor; + + // MCR mode + avb_audio_mcr_t audioMcr; + + // MCR timestamp interval + U32 mcrTimestampInterval; + + // MCR clock recovery interval + U32 mcrRecoveryInterval; + + ///////////// + // Variable data + ///////////// + U32 maxTransitUsec; // In microseconds + + aaf_nominal_sample_rate_t aaf_rate; + aaf_sample_format_t aaf_format; + U8 aaf_bit_depth; + U32 payloadSize; + + U8 aaf_event_field; + + bool dataValid; + + U32 intervalCounter; + + U32 sparseMode; + + bool mediaQItemSyncTS; + +} pvt_data_t; + +static void x_calculateSizes(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + media_q_pub_map_aaf_audio_info_t *pPubMapInfo = pMediaQ->pPubMapInfo; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + switch (pPubMapInfo->audioRate) { + case AVB_AUDIO_RATE_8KHZ: + pPvtData->aaf_rate = AAF_RATE_8K; + break; + case AVB_AUDIO_RATE_16KHZ: + pPvtData->aaf_rate = AAF_RATE_16K; + break; + case AVB_AUDIO_RATE_32KHZ: + pPvtData->aaf_rate = AAF_RATE_32K; + break; + case AVB_AUDIO_RATE_44_1KHZ: + pPvtData->aaf_rate = AAF_RATE_44K1; + break; + case AVB_AUDIO_RATE_48KHZ: + pPvtData->aaf_rate = AAF_RATE_48K; + break; + case AVB_AUDIO_RATE_88_2KHZ: + pPvtData->aaf_rate = AAF_RATE_88K2; + break; + case AVB_AUDIO_RATE_96KHZ: + pPvtData->aaf_rate = AAF_RATE_96K; + break; + case AVB_AUDIO_RATE_176_4KHZ: + pPvtData->aaf_rate = AAF_RATE_176K4; + break; + case AVB_AUDIO_RATE_192KHZ: + pPvtData->aaf_rate = AAF_RATE_192K; + break; + default: + AVB_LOG_ERROR("Invalid audio frequency configured"); + pPvtData->aaf_rate = AAF_RATE_UNSPEC; + break; + } + AVB_LOGF_INFO("aaf_rate=%d (%dKhz)", pPvtData->aaf_rate, pPubMapInfo->audioRate); + + char *typeStr = "int"; + if (pPubMapInfo->audioType == AVB_AUDIO_TYPE_FLOAT) { + typeStr = "float"; + switch (pPubMapInfo->audioBitDepth) { + case AVB_AUDIO_BIT_DEPTH_32BIT: + pPvtData->aaf_format = AAF_FORMAT_FLOAT_32; + pPubMapInfo->itemSampleSizeBytes = 4; + pPubMapInfo->packetSampleSizeBytes = 4; + pPvtData->aaf_bit_depth = 32; + break; + default: + AVB_LOG_ERROR("Invalid audio bit-depth configured for float"); + pPvtData->aaf_format = AAF_FORMAT_UNSPEC; + break; + } + } + else { + switch (pPubMapInfo->audioBitDepth) { + case AVB_AUDIO_BIT_DEPTH_32BIT: + pPvtData->aaf_format = AAF_FORMAT_INT_32; + pPubMapInfo->itemSampleSizeBytes = 4; + pPubMapInfo->packetSampleSizeBytes = 4; + pPvtData->aaf_bit_depth = 32; + break; + case AVB_AUDIO_BIT_DEPTH_24BIT: + pPvtData->aaf_format = AAF_FORMAT_INT_24; + pPubMapInfo->itemSampleSizeBytes = 3; + pPubMapInfo->packetSampleSizeBytes = 3; + pPvtData->aaf_bit_depth = 24; + break; + case AVB_AUDIO_BIT_DEPTH_16BIT: + pPvtData->aaf_format = AAF_FORMAT_INT_16; + pPubMapInfo->itemSampleSizeBytes = 2; + pPubMapInfo->packetSampleSizeBytes = 2; + pPvtData->aaf_bit_depth = 16; + break; +#if 0 + // should work - test content? + case AVB_AUDIO_BIT_DEPTH_20BIT: + pPvtData->aaf_format = AAF_FORMAT_INT_24; + pPubMapInfo->itemSampleSizeBytes = 3; + pPubMapInfo->packetSampleSizeBytes = 3; + pPvtData->aaf_bit_depth = 20; + break; + // would require byte-by-byte copy + case AVB_AUDIO_BIT_DEPTH_8BIT: + pPvtData->aaf_format = AAF_FORMAT_INT_24; + pPubMapInfo->itemSampleSizeBytes = 1; + pPubMapInfo->packetSampleSizeBytes = 2; + pPvtData->aaf_bit_depth = 8; + break; +#endif + default: + AVB_LOG_ERROR("Invalid audio bit-depth configured"); + pPvtData->aaf_format = AAF_FORMAT_UNSPEC; + break; + } + } + AVB_LOGF_INFO("aaf_format=%d (%s%d)", + pPvtData->aaf_format, typeStr, pPubMapInfo->audioBitDepth); + + // Audio frames per packet + pPubMapInfo->framesPerPacket = (pPubMapInfo->audioRate / pPvtData->txInterval); + if (pPubMapInfo->audioRate % pPvtData->txInterval != 0) { + AVB_LOGF_WARNING("Audio rate (%d) is not integer multiple of TX interval (%d)", + pPubMapInfo->audioRate, pPvtData->txInterval); + pPubMapInfo->framesPerPacket += 1; + } + AVB_LOGF_INFO("Frames/packet = %d", pPubMapInfo->framesPerPacket); + + // AAF packet size calculations + pPubMapInfo->packetFrameSizeBytes = pPubMapInfo->packetSampleSizeBytes * pPubMapInfo->audioChannels; + pPvtData->payloadSize = pPubMapInfo->framesPerPacket * pPubMapInfo->packetFrameSizeBytes; + AVB_LOGF_INFO("packet: sampleSz=%d * channels=%d => frameSz=%d * %d => payloadSz=%d", + pPubMapInfo->packetSampleSizeBytes, + pPubMapInfo->audioChannels, + pPubMapInfo->packetFrameSizeBytes, + pPubMapInfo->framesPerPacket, + pPvtData->payloadSize); + + // MediaQ item size calculations + pPubMapInfo->packingFactor = pPvtData->packingFactor; + pPubMapInfo->framesPerItem = pPubMapInfo->framesPerPacket * pPvtData->packingFactor; + pPubMapInfo->itemFrameSizeBytes = pPubMapInfo->itemSampleSizeBytes * pPubMapInfo->audioChannels; + pPubMapInfo->itemSize = pPubMapInfo->itemFrameSizeBytes * pPubMapInfo->framesPerItem; + AVB_LOGF_INFO("item: sampleSz=%d * channels=%d => frameSz=%d * %d * packing=%d => itemSz=%d", + pPubMapInfo->itemSampleSizeBytes, + pPubMapInfo->audioChannels, + pPubMapInfo->itemFrameSizeBytes, + pPubMapInfo->framesPerPacket, + pPubMapInfo->packingFactor, + pPubMapInfo->itemSize); + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbMapAVTPAudioCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + if (strcmp(name, "map_nv_item_count") == 0) { + char *pEnd; + pPvtData->itemCount = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_packing_factor") == 0) { + char *pEnd; + pPvtData->packingFactor = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_tx_rate") == 0 + || strcmp(name, "map_nv_tx_interval") == 0) { + char *pEnd; + pPvtData->txInterval = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_sparse_mode") == 0) { + char* pEnd; + U32 tmp; + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->sparseMode = TS_SPARSE_MODE_ENABLED; + } + else if (*pEnd == '\0' && tmp == 0) { + pPvtData->sparseMode = TS_SPARSE_MODE_DISABLED; + } + } + else if (strcmp(name, "map_nv_audio_mcr") == 0) { + char *pEnd; + pPvtData->audioMcr = (avb_audio_mcr_t)strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_mcr_timestamp_interval") == 0) { + char *pEnd; + pPvtData->mcrTimestampInterval = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_mcr_recovery_interval") == 0) { + char *pEnd; + pPvtData->mcrRecoveryInterval = strtol(value, &pEnd, 10); + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +U8 openavbMapAVTPAudioSubtypeCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return AVTP_SUBTYPE_AAF; // AAF AVB subtype +} + +// Returns the AVTP version used by this mapping +U8 openavbMapAVTPAudioAvtpVersionCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return 0x00; // Version 0 +} + +U16 openavbMapAVTPAudioMaxDataSizeCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return pPvtData->payloadSize + TOTAL_HEADER_SIZE; + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0; +} + +// Returns the intended transmit interval (in frames per second). 0 = default for talker / class. +U32 openavbMapAVTPAudioTransmitIntervalCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return pPvtData->txInterval; + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0; +} + +void openavbMapAVTPAudioGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + media_q_pub_map_uncmp_audio_info_t *pPubMapInfo = pMediaQ->pPubMapInfo; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + x_calculateSizes(pMediaQ); + openavbMediaQSetSize(pMediaQ, pPvtData->itemCount, pPubMapInfo->itemSize); + + pPvtData->dataValid = TRUE; + + pPubMapInfo->sparseMode = pPvtData->sparseMode; + if (pPubMapInfo->sparseMode == TS_SPARSE_MODE_ENABLED) { + AVB_LOG_INFO("Sparse timestamping mode: enabled"); + } else { + AVB_LOG_INFO("Sparse timestamping mode: disabled"); + } + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// A call to this callback indicates that this mapping module will be +// a talker. Any talker initialization can be done in this function. +void openavbMapAVTPAudioTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// CORE_TODO: This callback should be updated to work in a similiar way the uncompressed audio mapping. With allowing AVTP packets to be built +// from multiple media queue items. This allows interface to set into the media queue blocks of audio frames to properly correspond to +// a SYT_INTERVAL. Additionally the public data member sytInterval needs to be set in the same way the uncompressed audio mapping does. +// This talker callback will be called for each AVB observation interval. +tx_cb_ret_t openavbMapAVTPAudioTxCB(media_q_t *pMediaQ, U8 *pData, U32 *dataLen) +{ + media_q_item_t *pMediaQItem = NULL; + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + + if (!pData || !dataLen) { + AVB_LOG_ERROR("Mapping module data or data length argument incorrect."); + return TX_CB_RET_PACKET_NOT_READY; + } + + if (pMediaQ) + pMediaQItem = openavbMediaQTailLock(pMediaQ, TRUE); + + if (pMediaQItem) { + if (pMediaQItem->dataLen > 0) { + + U32 tmp32; + U8 *pHdrV0 = pData; + U32 *pHdr = (U32 *)(pData + AVTP_V0_HEADER_SIZE); + U8 *pPayload = pData + TOTAL_HEADER_SIZE; + media_q_pub_map_aaf_audio_info_t *pPubMapInfo = pMediaQ->pPubMapInfo; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return TX_CB_RET_PACKET_NOT_READY; + } + // timestamp set in the interface module, here just validate + if (openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime)) { + + // Add the max transit time. + openavbAvtpTimeAddUSec(pMediaQItem->pAvtpTime, pPvtData->maxTransitUsec); + + // Set timestamp valid flag + pHdrV0[HIDX_AVTP_HIDE7_TV1] |= 0x01; + + // Set (clear) timestamp uncertain flag + if (openavbAvtpTimeTimestampIsUncertain(pMediaQItem->pAvtpTime)) + pHdrV0[HIDX_AVTP_HIDE7_TU1] |= 0x01; + else pHdrV0[HIDX_AVTP_HIDE7_TU1] &= ~0x01; + + // - 4 bytes avtp_timestamp + *pHdr++ = htonl(openavbAvtpTimeGetAvtpTimestamp(pMediaQItem->pAvtpTime)); + + openavbAvtpTimeSetTimestampValid(pMediaQItem->pAvtpTime, FALSE); + } + else { + // Clear timestamp valid flag + pHdrV0[HIDX_AVTP_HIDE7_TV1] &= ~0x01; + pHdr++; // Move past the timestamp field + } + + // - 4 bytes format info (format, sample rate, channels per frame, bit depth) + tmp32 = pPvtData->aaf_format << 24; + tmp32 |= pPvtData->aaf_rate << 20; + tmp32 |= pPubMapInfo->audioChannels << 8; + tmp32 |= pPvtData->aaf_bit_depth; + *pHdr++ = htonl(tmp32); + + // - 4 bytes packet info (data length, evt field) + tmp32 = pPvtData->payloadSize << 16; + tmp32 |= pPvtData->aaf_event_field << 8; + *pHdr++ = htonl(tmp32); + + // Set (clear) sparse mode flag + if (pPvtData->sparseMode == TS_SPARSE_MODE_ENABLED) { + pHdrV0[HIDX_AVTP_HIDE7_SP] |= SP_M0_BIT; + } else { + pHdrV0[HIDX_AVTP_HIDE7_SP] &= ~SP_M0_BIT; + } + + if ((*dataLen - TOTAL_HEADER_SIZE) < pPvtData->payloadSize) { + AVB_LOG_ERROR("Not enough room in packet for payload"); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; + } + + if ((pMediaQItem->dataLen - pMediaQItem->readIdx) < pPvtData->payloadSize) { + // This should not happen so we will just toss it away. + AVB_LOG_ERROR("Not enough data in media queue item for packet"); + openavbMediaQTailPull(pMediaQ); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; + } + + memcpy(pPayload, (uint8_t *)pMediaQItem->pPubData + pMediaQItem->readIdx, pPvtData->payloadSize); + + pMediaQItem->readIdx += pPvtData->payloadSize; + if (pMediaQItem->readIdx >= pMediaQItem->dataLen) { + // Finished reading the entire item + openavbMediaQTailPull(pMediaQ); + } + else { + // More to read next interval + openavbMediaQTailUnlock(pMediaQ); + } + + // Set outbound data length (entire packet length) + *dataLen = pPvtData->payloadSize + TOTAL_HEADER_SIZE; + + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_READY; + } + else { + openavbMediaQTailPull(pMediaQ); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; // No payload + } + + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; +} + +// A call to this callback indicates that this mapping module will be +// a listener. Any listener initialization can be done in this function. +void openavbMapAVTPAudioRxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + if (pPvtData->audioMcr != AVB_MCR_NONE) { + HAL_INIT_MCR_V2(pPvtData->txInterval, pPvtData->packingFactor, pPvtData->mcrTimestampInterval, pPvtData->mcrRecoveryInterval); + } + bool badPckFctrValue = FALSE; + if (pPvtData->sparseMode == TS_SPARSE_MODE_ENABLED) { + // sparse mode enabled so check packing factor + // listener should work correct for packing_factors: + // 1, 2, 4, 8, 16, 24, 32, 40, 48, (+8) ... + if (pPvtData->packingFactor < 8) { + // check if power of 2 + if ((pPvtData->packingFactor & (pPvtData->packingFactor - 1)) != 0) { + badPckFctrValue = TRUE; + } + } else { + // check if multiple of 8 + if (pPvtData->packingFactor % 8 != 0) { + badPckFctrValue = TRUE; + } + } + if (badPckFctrValue) { + AVB_LOGF_WARNING("Wrong packing factor value set (%d) for sparse timestamping mode", pPvtData->packingFactor); + } + } + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// This callback occurs when running as a listener and data is available. +bool openavbMapAVTPAudioRxCB(media_q_t *pMediaQ, U8 *pData, U32 dataLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + if (pMediaQ && pData) { + U8 *pHdrV0 = pData; + U32 *pHdr = (U32 *)(pData + AVTP_V0_HEADER_SIZE); + U8 *pPayload = pData + TOTAL_HEADER_SIZE; + media_q_pub_map_aaf_audio_info_t *pPubMapInfo = pMediaQ->pPubMapInfo; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return FALSE; + } + + int tmp; + bool dataValid = TRUE; + + U32 timestamp = ntohl(*pHdr++); + U32 format_info = ntohl(*pHdr++); + U32 packet_info = ntohl(*pHdr++); + + bool listenerSparseMode = (pPvtData->sparseMode == TS_SPARSE_MODE_ENABLED) ? TRUE : FALSE; + bool streamSparseMode = (pHdrV0[HIDX_AVTP_HIDE7_SP] & SP_M0_BIT) ? TRUE : FALSE; + + if ((tmp = ((format_info >> 24) & 0xFF)) != pPvtData->aaf_format) { + if (pPvtData->dataValid) + AVB_LOGF_ERROR("Listener format %d doesn't match received data (%d)", + pPvtData->aaf_format, tmp); + dataValid = FALSE; + } + if ((tmp = ((format_info >> 20) & 0x0F)) != pPvtData->aaf_rate) { + if (pPvtData->dataValid) + AVB_LOGF_ERROR("Listener sample rate (%d) doesn't match received data (%d)", + pPvtData->aaf_rate, tmp); + dataValid = FALSE; + } + if ((tmp = ((format_info >> 8) & 0x3FF)) != pPubMapInfo->audioChannels) { + if (pPvtData->dataValid) + AVB_LOGF_ERROR("Listener channel count (%d) doesn't match received data (%d)", + pPubMapInfo->audioChannels, tmp); + dataValid = FALSE; + } + if ((tmp = (format_info & 0xFF)) != pPvtData->aaf_bit_depth) { + if (pPvtData->dataValid) + AVB_LOGF_ERROR("Listener bit depth (%d) doesn't match received data (%d)", + pPvtData->aaf_bit_depth, tmp); + dataValid = FALSE; + } + if ((tmp = ((packet_info >> 16) & 0xFFFF)) != pPvtData->payloadSize) { + if (pPvtData->dataValid) + AVB_LOGF_ERROR("Listener payload size (%d) doesn't match received data (%d)", + pPvtData->payloadSize, tmp); + dataValid = FALSE; + } + if ((tmp = ((packet_info >> 8) & 0x0F)) != pPvtData->aaf_event_field) { + if (pPvtData->dataValid) + AVB_LOGF_ERROR("Listener event field (%d) doesn't match received data (%d)", + pPvtData->aaf_event_field, tmp); + } + if (streamSparseMode != listenerSparseMode) { + if (pPvtData->dataValid) + AVB_LOGF_ERROR("Listener sparse mode (%d) doesn't match stream sparse mode (%d)", + listenerSparseMode, streamSparseMode); + dataValid = FALSE; + } + + if (dataValid) { + if (!pPvtData->dataValid) { + AVB_LOG_INFO("RX data valid, stream un-muted"); + pPvtData->dataValid = TRUE; + } + + // Get item pointer in media queue + media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (pMediaQItem) { + // set timestamp if first data written to item + if (pMediaQItem->dataLen == 0) { + + // Set timestamp valid flag + openavbAvtpTimeSetTimestampValid(pMediaQItem->pAvtpTime, (pHdrV0[HIDX_AVTP_HIDE7_TV1] & 0x01) ? TRUE : FALSE); + + if (openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime)) { + // Get the timestamp and place it in the media queue item. + openavbAvtpTimeSetToTimestamp(pMediaQItem->pAvtpTime, timestamp); + + openavbAvtpTimeSubUSec(pMediaQItem->pAvtpTime, pPubMapInfo->presentationLatencyUSec); + + // Set timestamp uncertain flag + openavbAvtpTimeSetTimestampUncertain(pMediaQItem->pAvtpTime, (pHdrV0[HIDX_AVTP_HIDE7_TU1] & 0x01) ? TRUE : FALSE); + // Set flag to inform that MediaQ is synchronized with timestamped packets + pPvtData->mediaQItemSyncTS = TRUE; + } + else if (!pPvtData->mediaQItemSyncTS) { + //we need packet with valid TS for first data written to item + AVB_LOG_DEBUG("Timestamp not valid for MediaQItem - initial packets dropped"); + dataValid = FALSE; + } + } + if (dataValid) { + if (pPubMapInfo->intf_rx_translate_cb) { + pPubMapInfo->intf_rx_translate_cb(pMediaQ, pPayload, pPvtData->payloadSize); + } + + memcpy((uint8_t *)pMediaQItem->pPubData + pMediaQItem->dataLen, pPayload, pPvtData->payloadSize); + pMediaQItem->dataLen += pPvtData->payloadSize; + } + + if (pMediaQItem->dataLen < pMediaQItem->itemSize) { + // More data can be written to the item + openavbMediaQHeadUnlock(pMediaQ); + } + else { + // The item is full push it. + openavbMediaQHeadPush(pMediaQ); + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TRUE; // Normal exit + } + else { + IF_LOG_INTERVAL(1000) AVB_LOG_INFO("Media queue full"); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; // Media queue full + } + } + else { + if (pPvtData->dataValid) { + AVB_LOG_INFO("RX data invalid, stream muted"); + pPvtData->dataValid = FALSE; + } + } + } + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; +} + +// This callback will be called when the mapping module needs to be closed. +// All cleanup should occur in this function. +void openavbMapAVTPAudioEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + if (pPvtData->audioMcr != AVB_MCR_NONE) { + HAL_CLOSE_MCR_V2(); + } + + pPvtData->mediaQItemSyncTS = FALSE; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +void openavbMapAVTPAudioGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Initialization entry point into the mapping module. Will need to be included in the .ini file. +extern DLL_EXPORT bool openavbMapAVTPAudioInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pMediaQ->pMediaQDataFormat = strdup(MapAVTPAudioMediaQDataFormat); + pMediaQ->pPubMapInfo = calloc(1, sizeof(media_q_pub_map_aaf_audio_info_t)); // Memory freed by the media queue when the media queue is destroyed. + pMediaQ->pPvtMapInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pMediaQDataFormat || !pMediaQ->pPubMapInfo || !pMediaQ->pPvtMapInfo) { + AVB_LOG_ERROR("Unable to allocate memory for mapping module"); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + + pMapCB->map_cfg_cb = openavbMapAVTPAudioCfgCB; + pMapCB->map_subtype_cb = openavbMapAVTPAudioSubtypeCB; + pMapCB->map_avtp_version_cb = openavbMapAVTPAudioAvtpVersionCB; + pMapCB->map_max_data_size_cb = openavbMapAVTPAudioMaxDataSizeCB; + pMapCB->map_transmit_interval_cb = openavbMapAVTPAudioTransmitIntervalCB; + pMapCB->map_gen_init_cb = openavbMapAVTPAudioGenInitCB; + pMapCB->map_tx_init_cb = openavbMapAVTPAudioTxInitCB; + pMapCB->map_tx_cb = openavbMapAVTPAudioTxCB; + pMapCB->map_rx_init_cb = openavbMapAVTPAudioRxInitCB; + pMapCB->map_rx_cb = openavbMapAVTPAudioRxCB; + pMapCB->map_end_cb = openavbMapAVTPAudioEndCB; + pMapCB->map_gen_end_cb = openavbMapAVTPAudioGenEndCB; + + pPvtData->itemCount = 20; + pPvtData->txInterval = 4000; // default to something that wont cause divide by zero + pPvtData->packingFactor = 1; + pPvtData->maxTransitUsec = inMaxTransitUsec; + pPvtData->sparseMode = TS_SPARSE_MODE_DISABLED; + pPvtData->mcrTimestampInterval = 144; + pPvtData->mcrRecoveryInterval = 512; + pPvtData->aaf_event_field = AAF_STATIC_CHANNELS_LAYOUT; + pPvtData->intervalCounter = 0; + pPvtData->mediaQItemSyncTS = FALSE; + openavbMediaQSetMaxLatency(pMediaQ, inMaxTransitUsec); + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return TRUE; +} diff --git a/lib/avtp_pipeline/map_aaf_audio/openavb_map_aaf_audio_pub.h b/lib/avtp_pipeline/map_aaf_audio/openavb_map_aaf_audio_pub.h new file mode 100755 index 00000000..b0db72cf --- /dev/null +++ b/lib/avtp_pipeline/map_aaf_audio/openavb_map_aaf_audio_pub.h @@ -0,0 +1,52 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* + * HEADER SUMMARY : AVTP Audio Format mapping module public interface + * + * AAF is defined in IEEE 1722a (still in draft as of Feb 2015). + * + * map_nv_tx_rate must be set in the .ini file. + */ + +#ifndef OPENAVB_MAP_AVTP_AUDIO_PUB_H +#define OPENAVB_MAP_AVTP_AUDIO_PUB_H 1 + +// For now, use the same data format as the uncompressed audio mapping +// We will need to change the WAV and ALSA interface modules if we change this +// +#include "openavb_map_uncmp_audio_pub.h" +#define media_q_pub_map_aaf_audio_info_t media_q_pub_map_uncmp_audio_info_t + +// NOTE: A define is used for the MediaQDataFormat identifier because it is needed in separate execution units (static / dynamic libraries) +// that is why a single static (static/extern pattern) definition can not be used. +#define MapAVTPAudioMediaQDataFormat "AVTPAudioFormat" + +#endif // OPENAVB_MAP_AVTP_AUDIO_PUB_H diff --git a/lib/avtp_pipeline/map_ctrl/CMakeLists.txt b/lib/avtp_pipeline/map_ctrl/CMakeLists.txt new file mode 100644 index 00000000..9c84c7c2 --- /dev/null +++ b/lib/avtp_pipeline/map_ctrl/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/map_ctrl/openavb_map_ctrl.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/map_ctrl/ctrl_map.md b/lib/avtp_pipeline/map_ctrl/ctrl_map.md new file mode 100644 index 00000000..b38e1ad1 --- /dev/null +++ b/lib/avtp_pipeline/map_ctrl/ctrl_map.md @@ -0,0 +1,19 @@ +Ctrl Mapping {#ctrl_map} +============ + +# Description + +Control mapping module. + +The Control mapping is an AVTP control subtype with an vender specific +type defined to carry control command payloads between custom interface +modules. This is compliant with 1722a-D6 + +# Mapping module configuration parameters + +Name | Description +--------------------|--------------------------- +map_nv_item_count |The number of media queue elements to hold. +map_nv_tx_rate or map_nv_tx_interval | Transmit interval in frames per second. \ + 0 = default for talker class +map_nv_max_payload_size| Maximum payload that will be send in one ethernet frame diff --git a/lib/avtp_pipeline/map_ctrl/openavb_map_ctrl.c b/lib/avtp_pipeline/map_ctrl/openavb_map_ctrl.c new file mode 100755 index 00000000..a4047290 --- /dev/null +++ b/lib/avtp_pipeline/map_ctrl/openavb_map_ctrl.c @@ -0,0 +1,484 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Control mapping module. +* +* The Control mapping is an AVTP control subtype with an vender specific +* type defined to carry control command payloads between custom interface +* modules. This is compliant with 1722a-D6 +* +*----------------------------------------------------------------* +* +* HEADERS +* +* -+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+- +* |C| |S| |M| |F|T| | |T| +* |D|subtype |V|vers |R|R|V|V|sequence number|reserved |U| +* --+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-- +* | | +* | | +* - - +* | | +* |stream id | +* --+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-- +* | | +* |AVTP timestamp | +* --+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-- +* | | +* |format info | +* --+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-- +* | |proto | |number of | +* |stream data length |type |reserve|messages | +* --+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-- +* |OPENAVB control | | | +* |format |OPENAVB data length |OPENAVB reserved | +* --+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-- +* +******* Standard AVTP header fields for the AVTP_CONTROL subtype IEEE 1722a Section 10. +* +* CD : Standard AVTP +* Subtype : AVTP Control 0x04 +* SV : Standard AVTP +* Ver` : Standard AVTP +* MR : Standard AVTP +* R : Standard AVTP +* FV : Standard AVTP Set to zero (0) +* TV : Standard AVTP +* Sequence number : Standard AVTP +* Reserved : Standard AVTP +* TU : Standard AVTP +* Stream ID : Standard AVTP +* AVTP timestamp : Standard AVTP +* Format info : Standard AVTP (unused for AVTP control streams) +* Data Length : Length of the data payload +* Protocol type : Set to "F" for CTL_PROPRIETARY +* Reserved : Standard AVTP +* Number of messages: This mapping module will always transport just 1 message. +* +******* OPENAVB Vendor headers +* +* OPENAVB Ctrl format : OPENAVB specific control format. 0x01 for this mapping. +* OPENAVB data length : Data length of the OPENAVB control payload. +* OPENAVB reserved : reserved +* +*/ + +#include <stdlib.h> +#include <string.h> +#include <arpa/inet.h> +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_avtp_time_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_map_pub.h" +#include "openavb_map_ctrl_pub.h" + +#define AVB_LOG_COMPONENT "Control Mapping" +#include "openavb_log_pub.h" + +#define AVTP_V0_HEADER_SIZE 12 +#define SUBTYPE_HEADER_SIZE 12 +#define OPENAVB_FORMAT_HEADER_SIZE 4 + +#define AVTP_HEADER_SIZE (AVTP_V0_HEADER_SIZE + SUBTYPE_HEADER_SIZE) +#define TOTAL_HEADER_SIZE (AVTP_HEADER_SIZE + OPENAVB_FORMAT_HEADER_SIZE) + +////// +// AVTP Version 0 Header +////// + +#define HIDX_AVTP_VERZERO96 0 + +// - 1 Byte - TV bit (timestamp valid) +#define HIDX_AVTP_HIDE7_TV1 1 + +// - 1 Byte - TU bit (timestamp uncertain) +#define HIDX_AVTP_HIDE7_TU1 3 + + +////// +// Mapping specific header +////// + +// - 4 bytes avtp_timestamp +#define HIDX_AVTP_TIMESPAMP32 12 + +// - 4 bytes format information +#define HIDX_AVTP_FORMAT_INFO32 16 + +// - 2 bytes data length +#define HIDX_AVTP_DATA_LENGTH16 20 + +// - 1 byte protocol type | Reserved +#define HIDX_PROTO4_RESERVED4 22 + +// - 1 byte Number of messages +#define HIDX_NUMMESS8 23 + + +////// +// OPENAVB specific header +////// + +// - 1 byte OPENAVB format +#define HIDX_OPENAVB_CTRL_FORMAT8 24 + +// - 2 bytes stream_data_len +#define HIDX_OPENAVB_CTRL_DATALEN16 25 + +// - 1 bytes OPENAVB reserved +#define HIDX_OPENAVB_RESERVEDA8 27 + + +typedef struct { + ///////////// + // Config data + ///////////// + // map_nv_item_count + U32 itemCount; + + // Transmit interval in frames per second. 0 = default for talker class. + U32 txInterval; + + + ///////////// + // Variable data + ///////////// + // Max payload size + U32 maxPayloadSize; + + // Maximum data size + U32 maxDataSize; + + // Maximum media queue item size + U32 itemSize; + + // Maximum transit time + U32 maxTransitUsec; // In microseconds + +} pvt_data_t; + + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbMapCtrlCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + char *pEnd; + + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + if (strcmp(name, "map_nv_item_count") == 0) { + pPvtData->itemCount = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_tx_rate") == 0 + || strcmp(name, "map_nv_tx_interval") == 0) { + pPvtData->txInterval = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_max_payload_size") == 0) { + pPvtData->maxPayloadSize = strtol(value, &pEnd, 10); + pPvtData->maxDataSize = (pPvtData->maxPayloadSize + TOTAL_HEADER_SIZE); + pPvtData->itemSize = pPvtData->maxDataSize; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +U8 openavbMapCtrlSubtypeCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0x04; // AVTP Control subtype 1722a 5.2.1.2 subtype field +} + +// Returns the AVTP version used by this mapping +U8 openavbMapCtrlAvtpVersionCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return 0x00; // Version 0 +} + +U16 openavbMapCtrlMaxDataSizeCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return pPvtData->maxDataSize; + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0; +} + +// Returns the intended transmit interval (in frames per second). 0 = default for talker / class. +U32 openavbMapCtrlTransmitIntervalCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return pPvtData->txInterval; + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0; +} + +void openavbMapCtrlGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + openavbMediaQSetSize(pMediaQ, pPvtData->itemCount, pPvtData->itemSize); + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// A call to this callback indicates that this mapping module will be +// a talker. Any talker initialization can be done in this function. +void openavbMapCtrlTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// This talker callback will be called for each AVB observation interval. +tx_cb_ret_t openavbMapCtrlTxCB(media_q_t *pMediaQ, U8 *pData, U32 *dataLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + if (pMediaQ && pData && dataLen) { + U8 *pHdr = pData; + U8 *pPayload = pData + TOTAL_HEADER_SIZE; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return TX_CB_RET_PACKET_NOT_READY; + } + + media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, TRUE); + + if (pMediaQItem) { + if (pMediaQItem->dataLen > 0) { + if (pMediaQItem->dataLen > pPvtData->maxPayloadSize) { + AVB_LOGF_ERROR("Media queue data item size too large. Reported size: %d Max Size: %d", pMediaQItem->dataLen, pPvtData->maxPayloadSize); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + openavbMediaQTailPull(pMediaQ); + return TX_CB_RET_PACKET_NOT_READY; + } + + // PTP walltime already set in the interface module. Just add the max transit time. + openavbAvtpTimeAddUSec(pMediaQItem->pAvtpTime, pPvtData->maxTransitUsec); + + // Set timestamp valid flag + if (openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime)) + pHdr[HIDX_AVTP_HIDE7_TV1] |= 0x01; // Set + else { + pHdr[HIDX_AVTP_HIDE7_TV1] &= ~0x01; // Clear + } + + // Set timestamp uncertain flag + if (openavbAvtpTimeTimestampIsUncertain(pMediaQItem->pAvtpTime)) + pHdr[HIDX_AVTP_HIDE7_TU1] |= 0x01; // Set + else + pHdr[HIDX_AVTP_HIDE7_TU1] &= ~0x01; // Clear + + *(U32 *)(&pHdr[HIDX_AVTP_TIMESPAMP32]) = htonl(openavbAvtpTimeGetAvtpTimestamp(pMediaQItem->pAvtpTime)); + *(U32 *)(&pHdr[HIDX_AVTP_FORMAT_INFO32]) = 0x00000000; + *(U16 *)(&pHdr[HIDX_AVTP_DATA_LENGTH16]) = htons(pMediaQItem->dataLen + OPENAVB_FORMAT_HEADER_SIZE); + pHdr[HIDX_PROTO4_RESERVED4] = 0xF0; // 1722a 10.2.2 protocol_type field. F for CTL_PROPRIETARY + pHdr[HIDX_NUMMESS8] = 0x01; // Always 1 control message + + pHdr[HIDX_OPENAVB_CTRL_FORMAT8] = MAP_CTRL_OPENAVB_FORMAT; + *(U16 *)(&pHdr[HIDX_OPENAVB_CTRL_DATALEN16]) = htons(pMediaQItem->dataLen); + pHdr[HIDX_OPENAVB_RESERVEDA8] = 0x00; + + memcpy(pPayload, pMediaQItem->pPubData, pMediaQItem->dataLen); + *dataLen = pMediaQItem->dataLen + TOTAL_HEADER_SIZE; + + openavbMediaQTailPull(pMediaQ); + AVB_TRACE_LINE(AVB_TRACE_MAP_LINE); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_READY; + } + else { + openavbMediaQTailPull(pMediaQ); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; // No payload + } + } + else { + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; // Media queue empty + } + } + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; +} + +// A call to this callback indicates that this mapping module will be +// a listener. Any listener initialization can be done in this function. +void openavbMapCtrlRxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// This callback occurs when running as a listener and data is available. +bool openavbMapCtrlRxCB(media_q_t *pMediaQ, U8 *pData, U32 dataLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + if (pMediaQ && pData) { + const U8 *pHdr = pData; + U8 *pPayload = pData + TOTAL_HEADER_SIZE; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return FALSE; + } + + media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (pMediaQItem) { + U32 timestamp = ntohl(*(U32 *)(&pHdr[HIDX_AVTP_TIMESPAMP32])); + openavbAvtpTimeSetToTimestamp(pMediaQItem->pAvtpTime, timestamp); + + // Set timestamp valid and timestamp uncertain flags + openavbAvtpTimeSetTimestampValid(pMediaQItem->pAvtpTime, (pHdr[HIDX_AVTP_HIDE7_TV1] & 0x01) ? TRUE : FALSE); + openavbAvtpTimeSetTimestampUncertain(pMediaQItem->pAvtpTime, (pHdr[HIDX_AVTP_HIDE7_TU1] & 0x01) ? TRUE : FALSE); + + if (pHdr[HIDX_OPENAVB_CTRL_FORMAT8] == MAP_CTRL_OPENAVB_FORMAT) { + U16 payloadLen = ntohs(*(U16 *)(&pHdr[HIDX_OPENAVB_CTRL_DATALEN16])); + if (pMediaQItem->itemSize >= payloadLen) { + memcpy(pMediaQItem->pPubData, pPayload, payloadLen); + pMediaQItem->dataLen = payloadLen; + } + else { + AVB_LOG_ERROR("Data to large for media queue."); + pMediaQItem->dataLen = 0; + } + } + else { + AVB_LOG_ERROR("Unexpected control stream format."); + pMediaQItem->dataLen = 0; + } + + openavbMediaQHeadPush(pMediaQ); + AVB_TRACE_LINE(AVB_TRACE_MAP_LINE); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TRUE; + } + else { + AVB_LOG_ERROR("Media queue full."); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; // Media queue full + } + } + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; +} + +// This callback will be called when the mapping module needs to be closed. +// All cleanup should occur in this function. +void openavbMapCtrlEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +void openavbMapCtrlGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Initialization entry point into the mapping module. Will need to be included in the .ini file. +extern DLL_EXPORT bool openavbMapCtrlInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pMediaQ->pMediaQDataFormat = strdup(MapCtrlMediaQDataFormat); + pMediaQ->pPvtMapInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pMediaQDataFormat || !pMediaQ->pPvtMapInfo) { + AVB_LOG_ERROR("Unable to allocate memory for mapping module."); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + + pMapCB->map_cfg_cb = openavbMapCtrlCfgCB; + pMapCB->map_subtype_cb = openavbMapCtrlSubtypeCB; + pMapCB->map_avtp_version_cb = openavbMapCtrlAvtpVersionCB; + pMapCB->map_max_data_size_cb = openavbMapCtrlMaxDataSizeCB; + pMapCB->map_transmit_interval_cb = openavbMapCtrlTransmitIntervalCB; + pMapCB->map_gen_init_cb = openavbMapCtrlGenInitCB; + pMapCB->map_tx_init_cb = openavbMapCtrlTxInitCB; + pMapCB->map_tx_cb = openavbMapCtrlTxCB; + pMapCB->map_rx_init_cb = openavbMapCtrlRxInitCB; + pMapCB->map_rx_cb = openavbMapCtrlRxCB; + pMapCB->map_end_cb = openavbMapCtrlEndCB; + pMapCB->map_gen_end_cb = openavbMapCtrlGenEndCB; + + pPvtData->itemCount = 20; + pPvtData->txInterval = 0; + pPvtData->maxTransitUsec = inMaxTransitUsec; + + pPvtData->maxPayloadSize = 1024; + pPvtData->maxDataSize = (pPvtData->maxPayloadSize + TOTAL_HEADER_SIZE); + pPvtData->itemSize = pPvtData->maxDataSize; + + openavbMediaQSetMaxLatency(pMediaQ, inMaxTransitUsec); + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return TRUE; +} + diff --git a/lib/avtp_pipeline/map_ctrl/openavb_map_ctrl_pub.h b/lib/avtp_pipeline/map_ctrl/openavb_map_ctrl_pub.h new file mode 100755 index 00000000..ff2b9a6f --- /dev/null +++ b/lib/avtp_pipeline/map_ctrl/openavb_map_ctrl_pub.h @@ -0,0 +1,44 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Control mapping module public interface +*/ + +#ifndef OPENAVB_MAP_CTRL_PUB_H +#define OPENAVB_MAP_CTRL_PUB_H 1 + +#include "openavb_types_pub.h" + +// NOTE: A define is used for the MapCtrlMediaQDataFormat identifier because it is needed in separate execution units (static / dynamic libraries) +// that is why a single static (static/extern pattern) definition can not be used. +#define MapCtrlMediaQDataFormat "OPENAVBControl" + +#endif // OPENAVB_MAP_CTRL_PUB_H diff --git a/lib/avtp_pipeline/map_h264/CMakeLists.txt b/lib/avtp_pipeline/map_h264/CMakeLists.txt new file mode 100644 index 00000000..c6f658b8 --- /dev/null +++ b/lib/avtp_pipeline/map_h264/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/map_h264/openavb_map_h264.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/map_h264/openavb_map_h264.c b/lib/avtp_pipeline/map_h264/openavb_map_h264.c new file mode 100755 index 00000000..32dbfe27 --- /dev/null +++ b/lib/avtp_pipeline/map_h264/openavb_map_h264.c @@ -0,0 +1,447 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : H.264 mapping module conforming to 1722A D6 9.4.3. +*/ + +////////////////////////////////////////////////////////////////// +// WORK IN PROGRESS +////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// +// WORK IN PROGRESS +////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// +// WORK IN PROGRESS +////////////////////////////////////////////////////////////////// + + +#include <stdlib.h> +#include <string.h> +#include <arpa/inet.h> +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_avtp_time_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_map_pub.h" +#include "openavb_map_h264_pub.h" + +#define AVB_LOG_COMPONENT "H.264 Mapping" +#include "openavb_log_pub.h" + +// Header sizes +#define AVTP_V0_HEADER_SIZE 12 +#define MAP_HEADER_SIZE 12 + +#define TOTAL_HEADER_SIZE (AVTP_V0_HEADER_SIZE + MAP_HEADER_SIZE) + +////// +// AVTP Version 0 Header +////// + +// This mapping does not directly set or read these. +#define HIDX_AVTP_VERZERO96 0 + +// - 1 Byte - TV bit (timestamp valid) +#define HIDX_AVTP_HIDE7_TV1 1 + +// - 1 Byte - TU bit (timestamp uncertain) +#define HIDX_AVTP_HIDE7_TU1 3 + + +////// +// Mapping specific header +////// + +// - 4 bytes avtp_timestamp +#define HIDX_AVTP_TIMESPAMP32 12 + +// - 1 bytes Format information = 0x02 RTP Video +#define HIDX_FORMAT8 16 + +// - 1 bytes Format subtype = 0x01 H.264 +#define HIDX_FORMAT_SUBTYPE8 17 + +// - 2 bytes Reserved = 0x0000 +#define HIDX_RESV16 18 + +// - 2 bytes Stream data length +#define HIDX_STREAM_DATA_LEN16 20 + +// 1 bit M3 = binary 0 +// 1 bit M2 = binary 0 +// 1 bit M1 = binary 0 +// 1 bit M0 = Set by interface module +// 2 bits EVT = binary 00 +// 2 bits Reserved = binary 00 +#define HIDX_M31_M21_M11_M01_EVT2_RESV2 22 + +// - 1 byte Reserved = binary 0x00 +#define HIDX_RESV8 23 + +typedef struct { + ///////////// + // Config data + ///////////// + // map_nv_item_count + U32 itemCount; + + // Transmit interval in frames per second. 0 = default for talker class. + U32 txInterval; + + // Max payload size + U32 maxPayloadSize; + + ///////////// + // Variable data + ///////////// + // Maximum transit time in microseconds + U32 maxTransitUsec; + + // Maximum data size. This is the RTP payload size. See RFC 6184 for details. + U32 maxDataSize; + + // Maximum media queue item size + U32 itemSize; + +} pvt_data_t; + + + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbMapH264CfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + char *pEnd; + + if (strcmp(name, "map_nv_item_count") == 0) { + pPvtData->itemCount = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_tx_rate") == 0 + || strcmp(name, "map_nv_tx_interval") == 0) { + pPvtData->txInterval = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_max_payload_size") == 0) { + pPvtData->maxPayloadSize = strtol(value, &pEnd, 10); + pPvtData->maxDataSize = (pPvtData->maxPayloadSize + TOTAL_HEADER_SIZE); + pPvtData->itemSize = pPvtData->maxDataSize; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +U8 openavbMapH264SubtypeCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0x03; // AVTP Video subtype +} + +// Returns the AVTP version used by this mapping +U8 openavbMapH264AvtpVersionCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return 0x00; // Version 0 +} + +U16 openavbMapH264MaxDataSizeCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return pPvtData->maxDataSize; + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0; +} + +// Returns the intended transmit interval (in frames per second). 0 = default for talker / class. +U32 openavbMapH264TransmitIntervalCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return pPvtData->txInterval; + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0; +} + +void openavbMapH264GenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + openavbMediaQSetSize(pMediaQ, pPvtData->itemCount, pPvtData->itemSize); + openavbMediaQAllocItemMapData(pMediaQ, sizeof(media_q_item_map_h264_pub_data_t), 0); + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// A call to this callback indicates that this mapping module will be +// a talker. Any talker initialization can be done in this function. +void openavbMapH264TxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// This talker callback will be called for each AVB observation interval. +tx_cb_ret_t openavbMapH264TxCB(media_q_t *pMediaQ, U8 *pData, U32 *dataLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + if (pMediaQ && pData && dataLen) { + U8 *pHdr = pData; + U8 *pPayload = pData + TOTAL_HEADER_SIZE; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return TX_CB_RET_PACKET_NOT_READY; + } + + //pHdr[HIDX_AVTP_TIMESPAMP32] = 0x00; // Set later + pHdr[HIDX_FORMAT8] = 0x02; // RTP Payload type + pHdr[HIDX_FORMAT_SUBTYPE8] = 0x01; // H.264 subtype + pHdr[HIDX_RESV16] = 0x0000; // Reserved + //pHdr[HIDX_STREAM_DATA_LEN16] = 0x0000; // Set later + //pHdr[HIDX_M31_M21_M11_M01_EVT2_RESV2] = 0x00; // M0 set later + pHdr[HIDX_RESV8] = 0x00; + + media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, TRUE); + if (pMediaQItem) { + if (pMediaQItem->dataLen > 0) { + if (pMediaQItem->dataLen > pPvtData->itemSize) { + AVB_LOGF_ERROR("Media queue data item size too large. Reported size: %d Max Size: %d", pMediaQItem->dataLen, pPvtData->itemSize); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + openavbMediaQTailPull(pMediaQ); + return TX_CB_RET_PACKET_NOT_READY; + } + + // PTP walltime already set in the interface module. Just add the max transit time. + openavbAvtpTimeAddUSec(pMediaQItem->pAvtpTime, pPvtData->maxTransitUsec); + + // Set timestamp valid flag + if (openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime)) + pHdr[HIDX_AVTP_HIDE7_TV1] |= 0x01; // Set + else { + pHdr[HIDX_AVTP_HIDE7_TV1] &= ~0x01; // Clear + } + + // Set timestamp uncertain flag + if (openavbAvtpTimeTimestampIsUncertain(pMediaQItem->pAvtpTime)) + pHdr[HIDX_AVTP_HIDE7_TU1] |= 0x01; // Set + else pHdr[HIDX_AVTP_HIDE7_TU1] &= ~0x01; // Clear + + // Set the timestamp. + *(U32 *)(&pHdr[HIDX_AVTP_TIMESPAMP32]) = htonl(openavbAvtpTimeGetAvtpTimestamp(pMediaQItem->pAvtpTime)); + + if (((media_q_item_map_h264_pub_data_t *)pMediaQItem->pPubMapData)->lastPacket) { + pHdr[HIDX_M31_M21_M11_M01_EVT2_RESV2] = 0x10;; + } + else { + pHdr[HIDX_M31_M21_M11_M01_EVT2_RESV2] = 0x00; + } + + // Copy the JPEG fragment into the outgoing avtp packet. + memcpy(pPayload, pMediaQItem->pPubData, pMediaQItem->dataLen); + + *(U16 *)(&pHdr[HIDX_STREAM_DATA_LEN16]) = htons(pMediaQItem->dataLen); + + // Set out bound data length (entire packet length) + *dataLen = pMediaQItem->dataLen + TOTAL_HEADER_SIZE; + + AVB_TRACE_LINE(AVB_TRACE_MAP_LINE); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + openavbMediaQTailPull(pMediaQ); + return TX_CB_RET_PACKET_READY; + } + openavbMediaQTailPull(pMediaQ); + } + } + + if (dataLen) { + // Set out bound data length (entire packet length) + *dataLen = 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; +} + +// A call to this callback indicates that this mapping module will be +// a listener. Any listener initialization can be done in this function. +void openavbMapH264RxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// This callback occurs when running as a listener and data is available. +bool openavbMapH264RxCB(media_q_t *pMediaQ, U8 *pData, U32 dataLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + if (pMediaQ && pData) { + U8 *pHdr = pData; + U8 *pPayload = pData + TOTAL_HEADER_SIZE; + + + //pHdr[HIDX_AVTP_TIMESPAMP32] + //pHdr[HIDX_FORMAT8] + //pHdr[HIDX_FORMAT_SUBTYPE8] + //pHdr[HIDX_RESV16] + //pHdr[HIDX_STREAM_DATA_LEN16] + U16 payloadLen = ntohs(*(U16 *)(&pHdr[HIDX_STREAM_DATA_LEN16])); + //pHdr[HIDX_M31_M21_M11_M01_EVT2_RESV2] + //pHdr[HIDX_RESV8] + + // Get item pointer in media queue + media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (pMediaQItem) { + // Get the timestamp and place it in the media queue item. + U32 timestamp = ntohl(*(U32 *)(&pHdr[HIDX_AVTP_TIMESPAMP32])); + openavbAvtpTimeSetToTimestamp(pMediaQItem->pAvtpTime, timestamp); + + // Set timestamp valid and timestamp uncertain flags + openavbAvtpTimeSetTimestampValid(pMediaQItem->pAvtpTime, (pHdr[HIDX_AVTP_HIDE7_TV1] & 0x01) ? TRUE : FALSE); + openavbAvtpTimeSetTimestampUncertain(pMediaQItem->pAvtpTime, (pHdr[HIDX_AVTP_HIDE7_TU1] & 0x01) ? TRUE : FALSE); + + if (pHdr[HIDX_M31_M21_M11_M01_EVT2_RESV2] & 0x10) + ((media_q_item_map_h264_pub_data_t *)pMediaQItem->pPubMapData)->lastPacket = TRUE; + else + ((media_q_item_map_h264_pub_data_t *)pMediaQItem->pPubMapData)->lastPacket = FALSE; + + if (pMediaQItem->itemSize >= dataLen - TOTAL_HEADER_SIZE) { + memcpy(pMediaQItem->pPubData, pPayload, payloadLen); + pMediaQItem->dataLen = dataLen - TOTAL_HEADER_SIZE; + } + else { + AVB_LOG_ERROR("Data to large for media queue."); + pMediaQItem->dataLen = 0; + } + + openavbMediaQHeadPush(pMediaQ); + AVB_TRACE_LINE(AVB_TRACE_MAP_LINE); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TRUE; + } + else { + AVB_LOG_ERROR("Media queue full."); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; // Media queue full + } + } + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; +} + +// This callback will be called when the mapping module needs to be closed. +// All cleanup should occur in this function. +void openavbMapH264EndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +void openavbMapH264GenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Initialization entry point into the mapping module. Will need to be included in the .ini file. +extern DLL_EXPORT bool openavbMapH264Initialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pMediaQ->pMediaQDataFormat = strdup(MapH264MediaQDataFormat); + pMediaQ->pPvtMapInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pMediaQDataFormat || !pMediaQ->pPvtMapInfo) { + AVB_LOG_ERROR("Unable to allocate memory for mapping module."); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + + pMapCB->map_cfg_cb = openavbMapH264CfgCB; + pMapCB->map_subtype_cb = openavbMapH264SubtypeCB; + pMapCB->map_avtp_version_cb = openavbMapH264AvtpVersionCB; + pMapCB->map_max_data_size_cb = openavbMapH264MaxDataSizeCB; + pMapCB->map_transmit_interval_cb = openavbMapH264TransmitIntervalCB; + pMapCB->map_gen_init_cb = openavbMapH264GenInitCB; + pMapCB->map_tx_init_cb = openavbMapH264TxInitCB; + pMapCB->map_tx_cb = openavbMapH264TxCB; + pMapCB->map_rx_init_cb = openavbMapH264RxInitCB; + pMapCB->map_rx_cb = openavbMapH264RxCB; + pMapCB->map_end_cb = openavbMapH264EndCB; + pMapCB->map_gen_end_cb = openavbMapH264GenEndCB; + + pPvtData->itemCount = 20; + pPvtData->txInterval = 0; + pPvtData->maxTransitUsec = inMaxTransitUsec; + + pPvtData->maxPayloadSize = 1412; + pPvtData->maxDataSize = (pPvtData->maxPayloadSize + TOTAL_HEADER_SIZE); + pPvtData->itemSize = pPvtData->maxDataSize; + + openavbMediaQSetMaxLatency(pMediaQ, inMaxTransitUsec); + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return TRUE; +} diff --git a/lib/avtp_pipeline/map_h264/openavb_map_h264_pub.h b/lib/avtp_pipeline/map_h264/openavb_map_h264_pub.h new file mode 100755 index 00000000..e70111ab --- /dev/null +++ b/lib/avtp_pipeline/map_h264/openavb_map_h264_pub.h @@ -0,0 +1,58 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Motion JPEG mapping module public interface conforming to 1722A RTP payload encapsulation. +* +* Refer to RFC 2435 for details of the fragment structure. +* +* As defined 1722a the timestamp must be the same for each fragment of a JPEG frame. This means the interface module +* must set this same timestamp in the media queue for each item tht is a JPEG fragment of the frame. In addition the +* Interface module must set the lastFragment flag of the item public map data structure for each fragment placed into +* the media queue (TRUE if not the last fragment of the frame, FALSE if it is the last fragment of a frame). +* +* The payload will be as defined in RFC 2435 and will include the JPEG headers as well as the JPEG data. +*/ + +#ifndef OPENAVB_MAP_H264_PUB_H +#define OPENAVB_MAP_H264_PUB_H 1 + +#include "openavb_types_pub.h" + +// NOTE: A define is used for the MapH264MediaQDataFormat identifier because it is needed in separate execution units (static / dynamic libraries) +// that is why a single static (static/extern pattern) definition can not be used. +#define MapH264MediaQDataFormat "H.264" + +typedef struct { + // Last fragment of frame flag. + bool lastPacket; // For details see 1722a 9.4.3.1.1 M0 field +} media_q_item_map_h264_pub_data_t; + +#endif // OPENAVB_MAP_H264_PUB_H diff --git a/lib/avtp_pipeline/map_mjpeg/CMakeLists.txt b/lib/avtp_pipeline/map_mjpeg/CMakeLists.txt new file mode 100644 index 00000000..f0765be0 --- /dev/null +++ b/lib/avtp_pipeline/map_mjpeg/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/map_mjpeg/openavb_map_mjpeg.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/map_mjpeg/mjpeg_map.md b/lib/avtp_pipeline/map_mjpeg/mjpeg_map.md new file mode 100644 index 00000000..103763a6 --- /dev/null +++ b/lib/avtp_pipeline/map_mjpeg/mjpeg_map.md @@ -0,0 +1,24 @@ +mjpeg Mapping {#mjpeg_map} +============= + +# Description + +Motion Jpeg mapping module conforming to 1722A RTP payload encapsulation. + +# Mapping module configuration parameters + +Name | Description +--------------------|--------------------------- +map_nv_item_count |The number of media queue elements to hold. +map_nv_tx_rate or map_nv_tx_interval | Transmit interval in frames per second. \ + 0 = default for talker class + +# Notes + +This module also uses the field media_q_item_map_mjpeg_pub_data_t::lastFragment +in both RX and TX. The usage depends on situation, for: +- TX - stores in the AVTP header information that this is the last fragment of +this frame. The interface module has to set this variable to correct value, +* RX - extracts from the AVTP header information if this fragment is the last one +of current video frame and sets field accordingly. The interface module might use +it later during frame composition. diff --git a/lib/avtp_pipeline/map_mjpeg/openavb_map_mjpeg.c b/lib/avtp_pipeline/map_mjpeg/openavb_map_mjpeg.c new file mode 100755 index 00000000..782a6d04 --- /dev/null +++ b/lib/avtp_pipeline/map_mjpeg/openavb_map_mjpeg.c @@ -0,0 +1,427 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Motion Jpeg mapping module conforming to 1722A RTP payload encapsulation. +*/ + +#include <stdlib.h> +#include <string.h> +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_avtp_time_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_map_pub.h" +#include "openavb_map_mjpeg_pub.h" + +#define AVB_LOG_COMPONENT "MJPEG Mapping" +#include "openavb_log_pub.h" + +// Header sizes +#define AVTP_V0_HEADER_SIZE 12 +#define MAP_HEADER_SIZE 12 + +#define TOTAL_HEADER_SIZE (AVTP_V0_HEADER_SIZE + MAP_HEADER_SIZE) + +// MJPEG Payload is a JPEG fragment per packet plus its headers +//#define MAX_JPEG_PAYLOAD_SIZE 1024 +#define MAX_JPEG_PAYLOAD_SIZE 1412 + +#define MAX_DATA_SIZE (MAX_JPEG_PAYLOAD_SIZE + TOTAL_HEADER_SIZE); + +#define ITEM_SIZE MAX_JPEG_PAYLOAD_SIZE + +////// +// AVTP Version 0 Header +////// + +// This mapping does not directly set or read these. +#define HIDX_AVTP_VERZERO96 0 + +// - 1 Byte - TV bit (timestamp valid) +#define HIDX_AVTP_HIDE7_TV1 1 + +// - 1 Byte - TU bit (timestamp uncertain) +#define HIDX_AVTP_HIDE7_TU1 3 + + +////// +// Mapping specific header +////// + +// - 4 bytes avtp_timestamp +#define HIDX_AVTP_TIMESTAMP32 12 + +// - 1 bytes Format information = 0x02 RTP Video +#define HIDX_FORMAT8 16 + +// - 1 byte Format subtype = 0x00 +#define HIDX_FORMAT_SUBTYPE8 17 + +// - 2 bytes Reserved = binary 0x0000 +#define HIDX_RESV16 18 + +// - 2 bytes Stream data length +#define HIDX_STREAM_DATA_LEN16 20 + +// 1 bit M3 = binary 0 +// 1 bit M2 = binary 0 +// 1 bit M1 = binary 0 +// 1 bit M0 = binary 0 +// 2 bit evt = binary 00 +// 2 bit Reserved = binary 00 +#define HIDX_M11_M01_EVT2_RESV2 22 + +// - 1 byte Reserved = binary 0x00 +#define HIDX_RESV8 23 + +typedef struct { + ///////////// + // Config data + ///////////// + // map_nv_item_count + U32 itemCount; + + // Transmit interval in frames per second. 0 = default for talker class. + U32 txInterval; + + ///////////// + // Variable data + ///////////// + U32 maxTransitUsec; // In microseconds + + U32 timestamp; + bool tsvalid; +} pvt_data_t; + + + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbMapMjpegCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + if (strcmp(name, "map_nv_item_count") == 0) { + char *pEnd; + pPvtData->itemCount = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_tx_rate") == 0 + || strcmp(name, "map_nv_tx_interval") == 0) { + char *pEnd; + pPvtData->txInterval = strtol(value, &pEnd, 10); + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +U8 openavbMapMjpegSubtypeCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0x03; // AVTP Video subtype +} + +// Returns the AVTP version used by this mapping +U8 openavbMapMjpegAvtpVersionCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return 0x00; // Version 0 +} + +U16 openavbMapMjpegMaxDataSizeCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return MAX_DATA_SIZE; +} + +// Returns the intended transmit interval (in frames per second). 0 = default for talker / class. +U32 openavbMapMjpegTransmitIntervalCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return pPvtData->txInterval; + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0; +} + +void openavbMapMjpegGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + pPvtData->timestamp = 0; + pPvtData->tsvalid = FALSE; + + openavbMediaQSetSize(pMediaQ, pPvtData->itemCount, ITEM_SIZE); + openavbMediaQAllocItemMapData(pMediaQ, sizeof(media_q_item_map_mjpeg_pub_data_t), 0); + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// A call to this callback indicates that this mapping module will be +// a talker. Any talker initialization can be done in this function. +void openavbMapMjpegTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// This talker callback will be called for each AVB observation interval. +tx_cb_ret_t openavbMapMjpegTxCB(media_q_t *pMediaQ, U8 *pData, U32 *dataLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + if (pMediaQ && pData && dataLen) { + U8 *pHdr = pData; + U8 *pPayload = pData + TOTAL_HEADER_SIZE; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return TX_CB_RET_PACKET_NOT_READY; + } + + //pHdr[HIDX_AVTP_TIMESTAMP32] = 0x00; // Set later + pHdr[HIDX_FORMAT8] = 0x02; // RTP Payload type + *(U16 *)(&pHdr[HIDX_FORMAT_SUBTYPE8]) = 0x00; // MJPEG format (RFC 2435) + //pHdr[HIDX_STREAM_DATA_LEN16] = 0x00; // Set later + pHdr[HIDX_RESV16] = 0x00; + pHdr[HIDX_RESV16+1] = 0x00; + pHdr[HIDX_M11_M01_EVT2_RESV2] = 0x00; + pHdr[HIDX_RESV8] = 0x00; + + media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, TRUE); + if (pMediaQItem) { + if (pMediaQItem->dataLen > 0) { + if (pMediaQItem->dataLen > ITEM_SIZE) { + AVB_LOGF_ERROR("Media queue data item size too large. Reported size: %d Max Size: %d", pMediaQItem->dataLen, ITEM_SIZE); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + openavbMediaQTailPull(pMediaQ); + return TX_CB_RET_PACKET_NOT_READY; + } + + // PTP walltime already set in the interface module. Just add the max transit time. + openavbAvtpTimeAddUSec(pMediaQItem->pAvtpTime, pPvtData->maxTransitUsec); + + // Set timestamp valid flag + if (openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime)) { + pHdr[HIDX_AVTP_HIDE7_TV1] |= 0x01; // Set + } + else { + pHdr[HIDX_AVTP_HIDE7_TV1] &= ~0x01; // Clear + } + + // Set timestamp uncertain flag + if (openavbAvtpTimeTimestampIsUncertain(pMediaQItem->pAvtpTime)) { + pHdr[HIDX_AVTP_HIDE7_TU1] |= 0x01; // Set + } + else { + pHdr[HIDX_AVTP_HIDE7_TU1] &= ~0x01; // Clear + } + + // Set the timestamp. + // 1722a-D6: The avtp_timestamp represents the presentation time associated with the given frame. The same avtp_timestamp + // shall appear in each fragment of a given frame. The M0 marker bit shall be set in the last packet of a frame. + if (!pPvtData->tsvalid) + { + pPvtData->timestamp = openavbAvtpTimeGetAvtpTimestamp(pMediaQItem->pAvtpTime); + pPvtData->tsvalid = TRUE; + } + *(U32 *)(&pHdr[HIDX_AVTP_TIMESTAMP32]) = htonl(pPvtData->timestamp); + + if (((media_q_item_map_mjpeg_pub_data_t *)pMediaQItem->pPubMapData)->lastFragment) { + pHdr[HIDX_M11_M01_EVT2_RESV2] = 0x00 | (1 << 4); + pPvtData->tsvalid = FALSE; + } + else { + pHdr[HIDX_M11_M01_EVT2_RESV2] = 0x00; + } + + // Copy the JPEG fragment into the outgoing avtp packet. + memcpy(pPayload, pMediaQItem->pPubData, pMediaQItem->dataLen); + + *(U16 *)(&pHdr[HIDX_STREAM_DATA_LEN16]) = pMediaQItem->dataLen; + + // Set out bound data length (entire packet length) + *dataLen = pMediaQItem->dataLen + TOTAL_HEADER_SIZE; + + AVB_TRACE_LINE(AVB_TRACE_MAP_LINE); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + openavbMediaQTailPull(pMediaQ); + return TX_CB_RET_PACKET_READY; + } + openavbMediaQTailPull(pMediaQ); + } + } + + if (dataLen) { + // Set out bound data length (entire packet length) + *dataLen = 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; +} + +// A call to this callback indicates that this mapping module will be +// a listener. Any listener initialization can be done in this function. +void openavbMapMjpegRxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// This callback occurs when running as a listener and data is available. +bool openavbMapMjpegRxCB(media_q_t *pMediaQ, U8 *pData, U32 dataLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + if (pMediaQ && pData) { + U8 *pHdr = pData; + U8 *pPayload = pData + TOTAL_HEADER_SIZE; + +// U16 payloadLen = ntohs(*(U16 *)(&pHdr[HIDX_STREAM_DATA_LEN16])); + U16 payloadLen = *(U16 *)(&pHdr[HIDX_STREAM_DATA_LEN16]); + + // Get item pointer in media queue + media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (pMediaQItem) { + // Get the timestamp and place it in the media queue item. + U32 timestamp = ntohl(*(U32 *)(&pHdr[HIDX_AVTP_TIMESTAMP32])); + openavbAvtpTimeSetToTimestamp(pMediaQItem->pAvtpTime, timestamp); + + // Set timestamp valid and timestamp uncertain flags + openavbAvtpTimeSetTimestampValid(pMediaQItem->pAvtpTime, (pHdr[HIDX_AVTP_HIDE7_TV1] & 0x01) ? TRUE : FALSE); + openavbAvtpTimeSetTimestampUncertain(pMediaQItem->pAvtpTime, (pHdr[HIDX_AVTP_HIDE7_TU1] & 0x01) ? TRUE : FALSE); + + if (pHdr[HIDX_M11_M01_EVT2_RESV2] & 0x10) { + ((media_q_item_map_mjpeg_pub_data_t *)pMediaQItem->pPubMapData)->lastFragment = TRUE; + } + else { + ((media_q_item_map_mjpeg_pub_data_t *)pMediaQItem->pPubMapData)->lastFragment = FALSE; + } + + if (pMediaQItem->itemSize >= dataLen - TOTAL_HEADER_SIZE) { + memcpy(pMediaQItem->pPubData, pPayload, payloadLen); + pMediaQItem->dataLen = dataLen - TOTAL_HEADER_SIZE; + } + else { + AVB_LOG_ERROR("Data to large for media queue."); + pMediaQItem->dataLen = 0; + } + + openavbMediaQHeadPush(pMediaQ); + AVB_TRACE_LINE(AVB_TRACE_MAP_LINE); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TRUE; + } + else { + AVB_LOG_ERROR("Media queue full."); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; // Media queue full + } + } + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; +} + +// This callback will be called when the mapping module needs to be closed. +// All cleanup should occur in this function. +void openavbMapMjpegEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +void openavbMapMjpegGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Initialization entry point into the mapping module. Will need to be included in the .ini file. +extern DLL_EXPORT bool openavbMapMjpegInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pMediaQ->pMediaQDataFormat = strdup(MapMjpegMediaQDataFormat); + pMediaQ->pPvtMapInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pMediaQDataFormat || !pMediaQ->pPvtMapInfo) { + AVB_LOG_ERROR("Unable to allocate memory for mapping module."); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + + pMapCB->map_cfg_cb = openavbMapMjpegCfgCB; + pMapCB->map_subtype_cb = openavbMapMjpegSubtypeCB; + pMapCB->map_avtp_version_cb = openavbMapMjpegAvtpVersionCB; + pMapCB->map_max_data_size_cb = openavbMapMjpegMaxDataSizeCB; + pMapCB->map_transmit_interval_cb = openavbMapMjpegTransmitIntervalCB; + pMapCB->map_gen_init_cb = openavbMapMjpegGenInitCB; + pMapCB->map_tx_init_cb = openavbMapMjpegTxInitCB; + pMapCB->map_tx_cb = openavbMapMjpegTxCB; + pMapCB->map_rx_init_cb = openavbMapMjpegRxInitCB; + pMapCB->map_rx_cb = openavbMapMjpegRxCB; + pMapCB->map_end_cb = openavbMapMjpegEndCB; + pMapCB->map_gen_end_cb = openavbMapMjpegGenEndCB; + + pPvtData->itemCount = 20; + pPvtData->txInterval = 0; + pPvtData->maxTransitUsec = inMaxTransitUsec; + + openavbMediaQSetMaxLatency(pMediaQ, inMaxTransitUsec); + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return TRUE; +} diff --git a/lib/avtp_pipeline/map_mjpeg/openavb_map_mjpeg_pub.h b/lib/avtp_pipeline/map_mjpeg/openavb_map_mjpeg_pub.h new file mode 100755 index 00000000..9af7180a --- /dev/null +++ b/lib/avtp_pipeline/map_mjpeg/openavb_map_mjpeg_pub.h @@ -0,0 +1,77 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Motion JPEG mapping module public interface conforming to 1722A RTP payload encapsulation. +* +* Refer to RFC 2435 for details of the fragment structure. +* +* As defined 1722a the timestamp must be the same for each fragment of a JPEG frame. This means the interface module +* must set this same timestamp in the media queue for each item tht is a JPEG fragment of the frame. In addition the +* Interface module must set the lastFragment flag of the item public map data structure for each fragment placed into +* the media queue (TRUE if not the last fragment of the frame, FALSE if it is the last fragment of a frame). +* +* The payload will be as defined in RFC 2435 and will include the JPEG headers as well as the JPEG data. +*/ + +#ifndef OPENAVB_MAP_MJPEG_PUB_H +#define OPENAVB_MAP_MJPEG_PUB_H 1 + +#include "openavb_types_pub.h" + +/** \file + * Motion JPEG mapping module public interface conforming to 1722A RTP payload + * encapsulation. + * + * As defined 1722a the timestamp must be the same for each fragment of a JPEG + * frame. This means the interface module must set this same timestamp in the + * media queue for each item tht is a JPEG fragment of the frame. + * In addition the Interface module must set the lastFragment flag of the item + * public map data structure for each fragment placed into the media queue + * (FALSE if not the last fragment of the frame, TRUE if it is the last + * fragment of a frame). + * + * The payload will be as defined in RFC 2435 and will include the JPEG header + * as well as the JPEG data. + */ + +/** \note A define is used for the MediaQDataFormat identifier because it is + * needed in separate execution units (static / dynamic libraries) that is why + * a single static (static/extern pattern) definition can not be used. + */ +#define MapMjpegMediaQDataFormat "Mjpeg" + +/// Additional public map data structure +typedef struct { + /// Is this fragment the last one of this frame. + bool lastFragment; +} media_q_item_map_mjpeg_pub_data_t; + +#endif // OPENAVB_MAP_MJPEG_PUB_H diff --git a/lib/avtp_pipeline/map_mpeg2ts/CMakeLists.txt b/lib/avtp_pipeline/map_mpeg2ts/CMakeLists.txt new file mode 100644 index 00000000..98d38e47 --- /dev/null +++ b/lib/avtp_pipeline/map_mpeg2ts/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/map_mpeg2ts/openavb_map_mpeg2ts.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/map_mpeg2ts/demo_listener.ini b/lib/avtp_pipeline/map_mpeg2ts/demo_listener.ini new file mode 100644 index 00000000..1f37d535 --- /dev/null +++ b/lib/avtp_pipeline/map_mpeg2ts/demo_listener.ini @@ -0,0 +1,89 @@ +##################################################################### +# General Listener configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = listener + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +stream_addr = 00:25:64:48:ca:a8 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: When SRP is being used the destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set in both side the talker and listener. +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# At this time they need to be locally administered and must be in the range +# of 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically :00 for the first stream, :01 for the second, etc. +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +#max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +#sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +#max_transit_usec = 2000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +raw_rx_buffers = 50000 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_mpeg2ts.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapMpeg2tsInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +##################################################################### +# Interface module configuration +##################################################################### +intf_lib = ./libopenavb_intf_mpeg2ts_file.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfMpeg2tsFileInitialize + +# Leave filename empty to use stdout +#intf_nv_file_name = listener.ts + +# intf_nv_ignore_timestamp: If set the listener will ignore the timestamp on media queue items. +intf_nv_ignore_timestamp = 1 diff --git a/lib/avtp_pipeline/map_mpeg2ts/demo_talker.ini b/lib/avtp_pipeline/map_mpeg2ts/demo_talker.ini new file mode 100644 index 00000000..77e6d698 --- /dev/null +++ b/lib/avtp_pipeline/map_mpeg2ts/demo_talker.ini @@ -0,0 +1,114 @@ +##################################################################### +# General Talker configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 00:25:64:48:ca:a8 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: destination multicast address for the stream. +# +# If using SRP and MAAP, dynamic destination addresses are generated +# automatically by the talker and passed to the listner, and don't +# need to be configured. +# +# Without MAAP, locally administered (static) addresses must be +# configured. Thouse addresses are in the range of: +# 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically use :00 for the first stream, :01 for the second, etc. +# +# When SRP is being used the static destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set (to the same value) in both the talker and listener. +# +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +#sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 2000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_mpeg2ts.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapMpeg2tsInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +# map_nv_tx_rate: Transmit rate +# If not set default of the talker class will be used. +#map_nv_tx_rate = 300 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +# intf_fn: The name of the initialize function in the interface. +intf_lib = ./libopenavb_intf_mpeg2ts_file.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfMpeg2tsFileInitialize + +# intf_nv_file_name: The fully qualified file name used both the talker and listener. +#intf_nv_file_name = input.ts +intf_nv_file_name = AMCTelematics2010.ts + +# intf_nv_repeat: Continually repeat the file stream when running as a talker. +intf_nv_repeat = 1 + +# Seconds before allowing rewind. Delays buffer overflow on RX side, +# at least for short files +intf_nv_repeat_seconds = 29 diff --git a/lib/avtp_pipeline/map_mpeg2ts/mpeg2ts_map.md b/lib/avtp_pipeline/map_mpeg2ts/mpeg2ts_map.md new file mode 100644 index 00000000..266b38e4 --- /dev/null +++ b/lib/avtp_pipeline/map_mpeg2ts/mpeg2ts_map.md @@ -0,0 +1,38 @@ +MPEG2 TS Mapping {#mpeg2ts_map} +================ + +# Description + +Mpeg2 TS mapping module conforming to AVB 61883-4 encapsulation. + +Refer to IEC 61883-4 for details of the "source packet" structure. + +This mapping module module as a talker requires an interface module to push +one source packet of 192 octets into the media queue along with a media queue +time value when the first data block of the source packet was obtained. +There is no need to place the timestamp within the source packet header, +this will be done within the mapping module when the maximum transit time is +added. The mapping module may bundle multiple source packets into one AVTP +packet before sending it. + +This mapping module module as a listener will parse source packets from the AVTP +packet and place each source packet of 192 octets into the media queue along +with the correct timestamp from the source packet header. The interface module +will pull each source packet from the media queue and present has needed. + +The protocol_specific_header, CIP header and the mpeg2 ts source packet header +are all taken care of in the mapping module. + +<br> +# Mapping module configuration parameters + +Name | Description +--------------------|--------------------------- +map_nv_item_count |The number of Media Queue items to hold. +map_nv_item_size |Size of data in each Media Queue item +map_nv_ts_packet_size|Size of transport stream packets passed to/from interface\ + module (188 or 192) +map_nv_num_source_packets|Number of source packets to send an in AVTP frame \ + (**Talker only**) +map_nv_tx_rate or map_nv_tx_interval | Transmit interval in frames per second. \ + 0 = default for talker class diff --git a/lib/avtp_pipeline/map_mpeg2ts/openavb_map_mpeg2ts.c b/lib/avtp_pipeline/map_mpeg2ts/openavb_map_mpeg2ts.c new file mode 100755 index 00000000..409a6090 --- /dev/null +++ b/lib/avtp_pipeline/map_mpeg2ts/openavb_map_mpeg2ts.c @@ -0,0 +1,734 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Mpeg2 TS mapping module conforming to AVB 61883-4 encapsulation. +*/ + +#include <stdlib.h> +#include <string.h> +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_avtp_time_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_map_pub.h" +#include "openavb_map_mpeg2ts_pub.h" +#include "openavb_types.h" +#include <assert.h> + +#define AVB_LOG_COMPONENT "MPEG2TS Mapping" +#include "openavb_log_pub.h" + +// Base MPEG2 Transport Stream packet size +#define MPEG2_TS_PKT_SIZE 188 +// MPEG2TS sync byte +#define MPEG2_TS_SYNC_BYTE 0x47 + +// GStreamer likes to pass 4096-byte buffers, so we want a buffer +// bigger than that. And, we're more efficient if the interface layer +// passes us whole TS packets, so let's use a default value that's a +// multiple of both 188 and 192 +#define MPEG2TS_MQITEM_SIZE 9024 +#define MPEG2TS_MQITEM_COUNT 20 + +// MPEG2TS packets with 4 byte timestamp (192 bytes) are called "source packets" in AVTP +#define MPEGTS_SRC_PKT_HDR_SIZE 4 +#define MPEGTS_SRC_PKT_SIZE (MPEG2_TS_PKT_SIZE + MPEGTS_SRC_PKT_HDR_SIZE) + +// AVTP payload: we allow for 5 source packets (192 * 5) plus the CIP header (8) = 968 +// TODO: make the number of packet configurable +#define AVTP_DFLT_SRC_PKTS 5 + +// AVTP Header sizes +#define AVTP_V0_HEADER_SIZE 12 +#define MAP_HEADER_SIZE 12 +#define CIP_HEADER_SIZE 8 + +#define TOTAL_HEADER_SIZE (AVTP_V0_HEADER_SIZE + MAP_HEADER_SIZE + CIP_HEADER_SIZE) + +#define BLOCKS_PER_SRC_PKT 8 +#define BLOCKS_PER_AVTP_PKT (BLOCKS_PER_SRC_PKT * AVTP_NUM_SRC_PKTS) + +////// +// AVTP Version 0 Header +////// + +// This mapping does not directly set or read these. +#define HIDX_AVTP_VERZERO96 0 + +// - 1 Byte - TV bit (timestamp valid) +#define HIDX_AVTP_HIDE7_TV1 1 + +// - 1 Byte - TU bit (timestamp uncertain) +#define HIDX_AVTP_HIDE7_TU1 3 + + +////// +// Mapping specific header +////// + +// - 4 bytes avtp_timestamp +#define HIDX_AVTP_TIMESTAMP32 12 + +// - 4 bytes gateway_info +#define HIDX_GATEWAY32 16 + +// - 2 bytes stream_data_len (save a pointer for later use) +#define HIDX_DATALEN16 20 + +// 2 bit tag = binary 01 (CIP header - included) +// 6 bit channel = 31 / 0x1F (Native AVB) +#define HIDX_TAG2_CHANNEL6 22 + +// 4 bits tcode = 0xA +// 4 bits sy (application-specific) = 0 +#define HIDX_TCODE4_SY4 23 + +////// +// CIP Header - 2 quadlets (8 bytes) +////// + +// 2 bits cip flag = binary 00 +// 6 bits sid (source identifier) = 63 / 0x3F (On the AVB network) +#define HIDX_CIP2_SID6 24 + +// 8 bits dbs (data block size) = 6 (6 quadlets per block) +#define HIDX_DBS8 25 + +// 2 bits FN (fraction number) = Bx11 (8 data blocks) +// 3 bits QPC (quadlet padding count) = Bx000 +// 1 bit SPH (source packet header) = Bx1 (using source packet headers) +// 2 bits RSV (reserved) = Bx00 +#define HIDX_FN2_QPC3_SPH1_RSV2 26 + +// 8 bits DBC (data block counter) = counter +#define HIDX_DBC8 27 + +// 2 bits cip flag (2nd quadlet) = binary 10 +// 6 bits fmt (stream format) = binary 100000 (MPEG2-TS) +#define HIDX_CIP2_FMT6 28 + +// 3 bytes fdf (format dependant) +// i.e. for MPEG2-TS +// 1 bit tsf (time shift flag) = 0 +// 23 bits reserved = 0 +#define HIDX_TSF1_RESA7 29 +#define HIDX_RESB8 30 +#define HIDX_RESC8 31 + +#define DEFAULT_SRC_PKTS_PER_AVTP_FRAME 5 + +typedef struct { + ///////////// + // Config data + ///////////// + + // map_nv_item_count, map_nv_item_size + // number of items to allocate in MediaQ, size of data in each item + unsigned itemCount, itemSize; + + // map_nv_ts_packet_size + // size of transport stream packets passed to/from interface module (188 or 192) + unsigned tsPacketSize; + + // Talker-only config + + // map_nv_num_source_packets + // Number of source packets to send in AVTP frame + unsigned numSourcePackets; + + // map_nv_tx_rate + // Transmit rate in frames per second. 0 = default for talker class. + unsigned txRate; + + ///////////// + // Variable data + ///////////// + unsigned maxTransitUsec; // In microseconds + + // Data block continuity counter + U32 DBC; + + // Saved partial source packet + char savedBytes[200]; + int nSavedBytes; + + // Is the input stream out of synch? + bool unsynched; + + unsigned int srcBitrate; + +} pvt_data_t; + + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbMapMpeg2tsCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + char *pEnd; + unsigned long tmp; + bool nameOK = TRUE, valueOK = FALSE; + + if (strcmp(name, "map_nv_item_count") == 0) { + tmp = strtoul(value, &pEnd, 10); + if (pEnd != value && *pEnd == '\0') { + pPvtData->itemCount = tmp; + valueOK = TRUE; + } + } + else if (strcmp(name, "map_nv_item_size") == 0) { + tmp = strtoul(value, &pEnd, 10); + if (pEnd != value && *pEnd == '\0') { + pPvtData->itemSize = tmp; + valueOK = TRUE; + } + } + else if (strcmp(name, "map_nv_ts_packet_size") == 0) { + tmp = strtoul(value, &pEnd, 10); + if (pEnd != value && *pEnd == '\0' && (tmp == 188 || tmp == 192)) { + pPvtData->tsPacketSize = tmp; + valueOK = TRUE; + } + } + else if (strcmp(name, "map_nv_num_source_packets") == 0) { + tmp = strtoul(value, &pEnd, 10); + if (pEnd != value && *pEnd == '\0') { + pPvtData->numSourcePackets = tmp; + valueOK = TRUE; + } + } + else if (strcmp(name, "map_nv_tx_rate") == 0 + || strcmp(name, "map_nv_tx_interval") == 0) { + tmp = strtoul(value, &pEnd, 10); + if (pEnd != value && *pEnd == '\0') { + pPvtData->txRate = tmp; + valueOK = TRUE; + } + } + else { + AVB_LOGF_WARNING("Unknown configuration item: %s", name); + nameOK = FALSE; + } + + if (nameOK && !valueOK) { + AVB_LOGF_WARNING("Bad value for configuration item: %s = %s", name, value); + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +U8 openavbMapMpeg2tsSubtypeCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0x00; // 61883 AVB subtype +} + +// Returns the AVTP version used by this mapping +U8 openavbMapMpeg2tsAvtpVersionCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return 0x00; // Version 0 +} + +U16 openavbMapMpeg2tsMaxDataSizeCB(media_q_t *pMediaQ) +{ + U16 retval = 0; + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return 0; + } + + retval = MPEGTS_SRC_PKT_SIZE * pPvtData->numSourcePackets + TOTAL_HEADER_SIZE; + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return retval; +} + +// Returns the intended transmit interval (in frames per second). 0 = default for talker / class. +U32 openavbMapMpeg2tsTransmitIntervalCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return 0; + } + + return pPvtData->txRate; + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0; +} + +void openavbMapMpeg2tsGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + openavbMediaQSetSize(pMediaQ, pPvtData->itemCount, pPvtData->itemSize); + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// A call to this callback indicates that this mapping module will be +// a talker. Any talker initialization can be done in this function. +void openavbMapMpeg2tsTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +static int syncScan(media_q_item_t *pMediaQItem, int startIdx) +{ + char *data = pMediaQItem->pPubData; + int offset; + for (offset = startIdx; offset < pMediaQItem->dataLen; offset++) { + if (data[offset] == MPEG2_TS_SYNC_BYTE) + break; + } + + AVB_LOGF_WARNING("Dropped %d bytes", offset - startIdx); + if (offset >= pMediaQItem->dataLen) + offset = -1; + + return offset; +} + +// This talker callback will be called for each AVB observation interval. +tx_cb_ret_t openavbMapMpeg2tsTxCB(media_q_t *pMediaQ, U8 *pData, U32 *dataLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + if (pMediaQ && pData && dataLen) { + U8 *pHdr = pData; + U8 *pPayload = pData + TOTAL_HEADER_SIZE; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return TX_CB_RET_PACKET_NOT_READY; + } + + //pHdr[HIDX_AVTP_TIMESTAMP32] = 0x00; // Set later + *(U32 *)(&pHdr[HIDX_GATEWAY32]) = 0x00000000; + //pHdr[HIDX_DATALEN16] = 0x00; // Set Later + pHdr[HIDX_TAG2_CHANNEL6] = (1 << 6) | 0x1f; + pHdr[HIDX_TCODE4_SY4] = (0x0a << 4) | 0; + + // Set the majority of the CIP header now. + pHdr[HIDX_CIP2_SID6] = (0x00 << 6) | 0x3f; + pHdr[HIDX_DBS8] = 0x06; + pHdr[HIDX_FN2_QPC3_SPH1_RSV2] = (0x03 << 6) | (0x00 << 3) | (0x01 << 2) | 0x00; + // pHdr[HIDX_DBC8] = 0; // This will be set later. + pHdr[HIDX_CIP2_FMT6] = (0x02 << 6) | 0xa0; + pHdr[HIDX_TSF1_RESA7] = (0x01 << 7) | 0x00; + pHdr[HIDX_RESB8] = 0x00; + pHdr[HIDX_RESC8] = 0x00; + + media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, TRUE); + int sourcePacketsAdded = 0; + int nItemBytes, nAvailBytes; + int offset, bytesNeeded; + U32 timestamp; + bool moreSourcePackets = TRUE; + + while (pMediaQItem && moreSourcePackets) { + + if (pPvtData->unsynched) { + // Scan forward, looking for next sync byte. + offset = syncScan(pMediaQItem, 0); + if (offset >= 0) + pMediaQItem->readIdx = offset; + else { + pPvtData->unsynched = TRUE; + pMediaQItem->dataLen = 0; + pMediaQItem->readIdx = 0; + } + } + + nItemBytes = pMediaQItem->dataLen - pMediaQItem->readIdx; + nAvailBytes = nItemBytes + pPvtData->nSavedBytes; + + if (nItemBytes == 0) { + // Empty MQ item, ignore + } + else if (nAvailBytes >= pPvtData->tsPacketSize) { + // PTP walltime already set in the interface module. Just add the max transit time. + // If this is a new mq item, add the AVTP transit time to the timestamp + if (pMediaQItem->readIdx == 0) { + openavbAvtpTimeAddUSec(pMediaQItem->pAvtpTime, pPvtData->maxTransitUsec); + } + + timestamp = openavbAvtpTimeGetAvtpTimestamp(pMediaQItem->pAvtpTime); + + // Set timestamp valid flag + if (openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime)) + pHdr[HIDX_AVTP_HIDE7_TV1] |= 0x01; // Set + else { + pHdr[HIDX_AVTP_HIDE7_TV1] &= ~0x01; // Clear + } + + // Set timestamp uncertain flag + if (openavbAvtpTimeTimestampIsUncertain(pMediaQItem->pAvtpTime)) + pHdr[HIDX_AVTP_HIDE7_TU1] |= 0x01; // Set + else pHdr[HIDX_AVTP_HIDE7_TU1] &= ~0x01; // Clear + + // Set the timestamp. + if (sourcePacketsAdded == 0) { + // TODO: I think this is wrong with source packets + *(U32 *)(&pHdr[HIDX_AVTP_TIMESTAMP32]) = htonl(timestamp); + } + + /* Copy the TS packets into the outgoing AVTP frame + */ + offset = 0, bytesNeeded = pPvtData->tsPacketSize; + + // If getting 188-byte packets from interface, need to add source packet header + if (pPvtData->tsPacketSize == MPEG2_TS_PKT_SIZE) { + // Set the timestamp on this source packet + *((U32 *)pPayload) = htonl(timestamp); + offset = MPEGTS_SRC_PKT_HDR_SIZE; + } + + // Use any leftover data from last MQ item + if (pPvtData->nSavedBytes) { + memcpy(pPayload + offset, pPvtData->savedBytes, pPvtData->nSavedBytes); + offset += pPvtData->nSavedBytes; + bytesNeeded -= pPvtData->nSavedBytes; + pPvtData->nSavedBytes = 0; + } + + // Now, copy data from current MQ item + memcpy(pPayload + offset, pMediaQItem->pPubData + pMediaQItem->readIdx, bytesNeeded); + + // Check that the data we've copied is synchronized + /// i.e. that the transport stream packet starts + // where we think it should + if (pPayload[4] == MPEG2_TS_SYNC_BYTE) { + // OK, now we can update the read index + pMediaQItem->readIdx += bytesNeeded; + // and move the payload ptr for the next source packet + pPayload += MPEGTS_SRC_PKT_SIZE; + + // Keep track of how many source packets have been added to the outgoing packet + sourcePacketsAdded++; + } + else { + AVB_LOG_WARNING("Alignment problem"); + + // Scan forward, looking for next sync byte. + // Start scan after 4-byte header if getting 192 byte packets. + // Ignore saved data if there was any, start from what's in current item. + offset = syncScan(pMediaQItem, pMediaQItem->readIdx + (pPvtData->tsPacketSize - MPEG2_TS_PKT_SIZE) + 1); + if (offset >= 0) + pMediaQItem->readIdx = offset; + else { + pPvtData->unsynched = TRUE; + pMediaQItem->dataLen = 0; + pMediaQItem->readIdx = 0; + } + } + } + else { + // Arghhh - a partial packet. + assert(pPvtData->nSavedBytes + nItemBytes <= MPEG2_TS_PKT_SIZE); + + memcpy(pPvtData->savedBytes + pPvtData->nSavedBytes, + pMediaQItem->pPubData + pMediaQItem->readIdx, + nItemBytes); + pPvtData->nSavedBytes += nItemBytes; + + pMediaQItem->dataLen = 0; + pMediaQItem->readIdx = 0; + } + + // Have we reached our target? + if (sourcePacketsAdded >= pPvtData->numSourcePackets) + moreSourcePackets = FALSE; + + // Have we used up the data in the MQ item? + if (pMediaQItem->dataLen - pMediaQItem->readIdx == 0) { + // release used-up item + openavbMediaQTailPull(pMediaQ); + + // and get a new one, if needed + if (moreSourcePackets) + pMediaQItem = openavbMediaQTailLock(pMediaQ, TRUE); + else pMediaQItem = NULL; + } + + } // while (pMediaQItem && moreSourcePackets) + + if (pMediaQItem) { + // holds more data, leave it in the queue + openavbMediaQTailUnlock(pMediaQ); + } + + if (sourcePacketsAdded > 0) { + + // Set the block continuity and data length + pHdr[HIDX_DBC8] = pPvtData->DBC; + pPvtData->DBC = (pPvtData->DBC + (sourcePacketsAdded * BLOCKS_PER_SRC_PKT)) & 0x000000ff; + + U16 datalen = (sourcePacketsAdded * MPEGTS_SRC_PKT_SIZE) + CIP_HEADER_SIZE; + *(U16 *)(pHdr + HIDX_DATALEN16) = htons(datalen); + + // Set out bound data length (entire packet length) + *dataLen = (sourcePacketsAdded * MPEGTS_SRC_PKT_SIZE) + TOTAL_HEADER_SIZE; + + AVB_TRACE_LINE(AVB_TRACE_MAP_LINE); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_READY; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; +} + +// A call to this callback indicates that this mapping module will be +// a listener. Any listener initialization can be done in this function. +void openavbMapMpeg2tsRxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// This callback occurs when running as a listener and data is available. +bool openavbMapMpeg2tsRxCB(media_q_t *pMediaQ, U8 *pData, U32 dataLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + if (pMediaQ && pData) { + U8 *pHdr = pData; + U8 *pPayload = pData + TOTAL_HEADER_SIZE; + U8 sourcePacketCount = 0; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return FALSE; + } + + //pHdr[HIDX_AVTP_TIMESTAMP32]; + //pHdr[HIDX_GATEWAY32]; + //pHdr[HIDX_DATALEN16]; + + //pHdr[HIDX_TAG2_CHANNEL6]; + //pHdr[HIDX_TCODE4_SY4]; + //pHdr[HIDX_CIP2_SID6]; + //pHdr[HIDX_DBS8]; + //pHdr[HIDX_FN2_QPC3_SPH1_RSV2]; + + // Only support full source packets. + // TODO: This mapper could be enhanced to support partial source packets and assemble the datablocks + // recieved across multiple avtp packets. + if ((pHdr[HIDX_DBC8] % 8) > 0) { + AVB_LOG_ERROR("Unsupported MPEG2TS block count received, payload discarded."); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; + } + + // TODO: validate DBC + + // Determine the number of source packets bundled into the AVTP payload. + U16 datalen = ntohs(*(U16 *)(pHdr + HIDX_DATALEN16)); + sourcePacketCount = (datalen - CIP_HEADER_SIZE) / MPEGTS_SRC_PKT_SIZE; + + //pHdr[HIDX_CIP2_FMT6]; + //pHdr[HIDX_TSF1_RESA7]; + //pHdr[HIDX_RESB8]; + //pHdr[HIDX_RESC8]; + + if (sourcePacketCount > 0) { + // Get item in media queue + media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (pMediaQItem) { + if (pMediaQItem->dataLen == 0) { + // Get the timestamp and place it in the media queue item. + U32 timestamp = ntohl(*(U32 *)(&pHdr[HIDX_AVTP_TIMESTAMP32])); + openavbAvtpTimeSetToTimestamp(pMediaQItem->pAvtpTime, timestamp); + + // Set timestamp valid and timestamp uncertain flags + openavbAvtpTimeSetTimestampValid(pMediaQItem->pAvtpTime, (pHdr[HIDX_AVTP_HIDE7_TV1] & 0x01) ? TRUE : FALSE); + openavbAvtpTimeSetTimestampUncertain(pMediaQItem->pAvtpTime, (pHdr[HIDX_AVTP_HIDE7_TU1] & 0x01) ? TRUE : FALSE); + } + + int i; + for (i = 0; i < sourcePacketCount; i++) { + if (pMediaQItem->itemSize - pMediaQItem->dataLen >= pPvtData->tsPacketSize) { + memcpy(pMediaQItem->pPubData + pMediaQItem->dataLen, + pPayload + MPEGTS_SRC_PKT_SIZE - pPvtData->tsPacketSize, + pPvtData->tsPacketSize); + pMediaQItem->dataLen += pPvtData->tsPacketSize; + pPayload += MPEGTS_SRC_PKT_SIZE; + } + else { + AVB_LOG_ERROR("Data too large for media queue"); + pMediaQItem->dataLen = 0; + break; + } + } + + // TODO: try to pack more data into MQ item? + openavbMediaQHeadPush(pMediaQ); + } + else { + // Media queue full, data dropped + AVB_LOG_ERROR("Media queue full"); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; + } + } + } + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; +} + +// This callback will be called when the mapping module needs to be closed. +// All cleanup should occur in this function. +void openavbMapMpeg2tsEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +void openavbMapMpeg2tsGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +void openavbMapMpeg2tsSetSrcBitrateCB(media_q_t *pMediaQ, unsigned int bitrate) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + ((pvt_data_t*)pMediaQ->pPvtMapInfo)->srcBitrate = (unsigned int)((double)bitrate * ((double)MPEGTS_SRC_PKT_SIZE)/((double)MPEG2_TS_PKT_SIZE)); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +unsigned int calculateBitrate(const int intervalsPerSecond, const int num_source_packets, const int number_of_frames) +{ + unsigned int bitrate = intervalsPerSecond * num_source_packets * number_of_frames * (MPEGTS_SRC_PKT_SIZE) * 8; + return bitrate; +} + +unsigned int openavbMapMpeg2tsGetMaxIntervalFramesCB(media_q_t *pMediaQ, SRClassIdx_t sr_class) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + unsigned int interval_frames = 1; + unsigned int classRate = 0; + switch (sr_class) { + case SR_CLASS_A: + classRate = 8000; + break; + case SR_CLASS_B: + classRate = 4000; + break; + case MAX_AVB_SR_CLASSES: // Case included to avoid warning on some toolchains + break; + } + + ((pvt_data_t*)pMediaQ->pPvtMapInfo)->numSourcePackets = 1; + while (calculateBitrate(classRate,((pvt_data_t*)pMediaQ->pPvtMapInfo)->numSourcePackets,interval_frames) < ((pvt_data_t*)pMediaQ->pPvtMapInfo)->srcBitrate) + { + ++((pvt_data_t*)pMediaQ->pPvtMapInfo)->numSourcePackets; + if (((pvt_data_t*)pMediaQ->pPvtMapInfo)->numSourcePackets > 7) + { + ((pvt_data_t*)pMediaQ->pPvtMapInfo)->numSourcePackets = 1; + ++interval_frames; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return interval_frames; +} + +// Initialization entry point into the mapping module. Will need to be included in the .ini file. +extern DLL_EXPORT bool openavbMapMpeg2tsInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pMediaQ->pMediaQDataFormat = strdup(MapMpeg2tsMediaQDataFormat); + pMediaQ->pPvtMapInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pMediaQDataFormat || !pMediaQ->pPvtMapInfo) { + AVB_LOG_ERROR("Unable to allocate memory for mapping module."); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + + pMapCB->map_cfg_cb = openavbMapMpeg2tsCfgCB; + pMapCB->map_subtype_cb = openavbMapMpeg2tsSubtypeCB; + pMapCB->map_avtp_version_cb = openavbMapMpeg2tsAvtpVersionCB; + pMapCB->map_max_data_size_cb = openavbMapMpeg2tsMaxDataSizeCB; + pMapCB->map_transmit_interval_cb = openavbMapMpeg2tsTransmitIntervalCB; + pMapCB->map_gen_init_cb = openavbMapMpeg2tsGenInitCB; + pMapCB->map_tx_init_cb = openavbMapMpeg2tsTxInitCB; + pMapCB->map_tx_cb = openavbMapMpeg2tsTxCB; + pMapCB->map_rx_init_cb = openavbMapMpeg2tsRxInitCB; + pMapCB->map_rx_cb = openavbMapMpeg2tsRxCB; + pMapCB->map_end_cb = openavbMapMpeg2tsEndCB; + pMapCB->map_gen_end_cb = openavbMapMpeg2tsGenEndCB; + pMapCB->map_set_src_bitrate_cb = openavbMapMpeg2tsSetSrcBitrateCB; + pMapCB->map_get_max_interval_frames_cb = openavbMapMpeg2tsGetMaxIntervalFramesCB; + + pPvtData->tsPacketSize = MPEG2_TS_PKT_SIZE; + pPvtData->numSourcePackets = AVTP_DFLT_SRC_PKTS; + pPvtData->itemCount = MPEG2TS_MQITEM_COUNT; + pPvtData->itemSize = MPEG2TS_MQITEM_SIZE; + pPvtData->txRate = 0; + pPvtData->maxTransitUsec = inMaxTransitUsec; + pPvtData->DBC = 0; + + openavbMediaQSetMaxLatency(pMediaQ, inMaxTransitUsec); + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return TRUE; +} diff --git a/lib/avtp_pipeline/map_mpeg2ts/openavb_map_mpeg2ts_pub.h b/lib/avtp_pipeline/map_mpeg2ts/openavb_map_mpeg2ts_pub.h new file mode 100755 index 00000000..3d876708 --- /dev/null +++ b/lib/avtp_pipeline/map_mpeg2ts/openavb_map_mpeg2ts_pub.h @@ -0,0 +1,62 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Mpeg2 TS mapping module public interface +* +* Refer to IEC 61883-4 for details of the "source packet" structure. +* +* This mapping module module as a talker requires an interface module to push +* one source packet of 192 octets into the media queue along with a media queue +* time value when the first data block of the source packet was obtained. +* There is no need to place the timestamp witin the source packet header, +* this will be done within the mapping module were the max transit time will be +* added. The mapping module may bundle multiple source packets into one avtp packet +* before sending it. +* +* This mapping module module as a listener will parse source packets from the +* avtp packet and place each source packet of 192 octets into the media queue along +* with the correct time stamp from the source packet header. The interface module +* will pull each source packet from the media queue and present has needed. +* +* The protocol_specific_header, CIP header and the mpeg2 ts source packet header are +* all taken care of in the mapping module. +*/ + +#ifndef OPENAVB_MAP_MPEG2TS_PUB_H +#define OPENAVB_MAP_MPEG2TS_PUB_H 1 + +#include "openavb_types_pub.h" + +// NOTE: A define is used for the MediaQDataFormat identifier because it is needed in separate execution units (static / dynamic libraries) +// that is why a single static (static/extern pattern) definition can not be used. +#define MapMpeg2tsMediaQDataFormat "Mpeg2ts" + +#endif // OPENAVB_MAP_MPEG2TS_PUB_H diff --git a/lib/avtp_pipeline/map_mpeg2ts/ts_packet_size.c b/lib/avtp_pipeline/map_mpeg2ts/ts_packet_size.c new file mode 100644 index 00000000..4a2e9984 --- /dev/null +++ b/lib/avtp_pipeline/map_mpeg2ts/ts_packet_size.c @@ -0,0 +1,117 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : stand-alone program to detect packet size in file +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> + +#define BASE_TS_PACKET_SIZE 188 +#define SYNC_BYTE 0x47 + +#define SCAN_COUNT 300 +#define CHECK_COUNT 30 + +int find_first(FILE* pFile) +{ + int i; char b; + assert(pFile); + + // scan forward to first sync byte + for (i = 0; i < SCAN_COUNT; i++) { + b = fgetc(pFile); + if (b == SYNC_BYTE) { + fprintf(stderr, "first sync byte at position %d\n", i); + return i; + } + } + + return -1; +} + +// Check if packet size is SIZE by looking for the first N sync bytes +int check_size(FILE* pFile, int size, int first) +{ + int i; char b; + assert(pFile); + assert(size >= BASE_TS_PACKET_SIZE); + assert(first >= 0); + + for (i = 0; i < CHECK_COUNT; i++) { + fseek(pFile, first + i * size, 0); + b = fgetc(pFile); + if (b != SYNC_BYTE) + return 0; + } + + fprintf(stderr, "transport packet size is %d\n", size); + return size; +} + +int +main(int argc, char *argv[]) +{ + FILE *pFile = NULL; + + if (argc == 1) { + pFile = stdin; + } + else if (argc == 2) { + pFile = fopen(argv[1], "rb"); + if (!pFile) { + fprintf(stderr, "could not open input file: %s\n", argv[1]); + exit(-2); + } + } + else { + fprintf(stderr, "error: usage is:\n\t%s [filename]\n", argv[0]); + exit(-1); + } + + int first = find_first(pFile); + if (first < 0) { + fprintf(stderr, "could not find first sync byte\n"); + } + else { + if (check_size(pFile, 188, first)) + ; + else if (check_size(pFile, 192, first)) + ; + else + fprintf(stderr, "could not determine packet size of the input file\n"); + } + + fclose(pFile); + pFile = NULL; + exit(0); +} diff --git a/lib/avtp_pipeline/map_null/CMakeLists.txt b/lib/avtp_pipeline/map_null/CMakeLists.txt new file mode 100644 index 00000000..ce67f4bd --- /dev/null +++ b/lib/avtp_pipeline/map_null/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/map_null/openavb_map_null.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/map_null/null_map.md b/lib/avtp_pipeline/map_null/null_map.md new file mode 100644 index 00000000..33fb069a --- /dev/null +++ b/lib/avtp_pipeline/map_null/null_map.md @@ -0,0 +1,17 @@ +NULL Mapping {#null_map} +============ + +# Description + +This NULL mapping does not pack or unpack any Mmedia Queue data in AVB packer. +It does however exercise the various functions and callback and can be +used as an example or a template for new mapping modules. + +<br> +# Mapping module configuration parameters + +Name | Description +--------------------|--------------------------- +map_nv_item_count |The number of media queue elements to hold. +map_nv_tx_rate |Transmit interval in frames per second. \ + 0 = default for talker class diff --git a/lib/avtp_pipeline/map_null/openavb_map_null.c b/lib/avtp_pipeline/map_null/openavb_map_null.c new file mode 100755 index 00000000..f79b0424 --- /dev/null +++ b/lib/avtp_pipeline/map_null/openavb_map_null.c @@ -0,0 +1,386 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : NULL mapping module. +* +* This NULL mapping does not pack or unpack any media data in AVB packer. +* It does however exercise the various functions and callback and can be +* used as an example or a template for new mapping modules. +* +*----------------------------------------------------------------* +* +* HEADERS +* +* -+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+- +* |C| |S| |M| |G|T| | |T| +* |D|subtype |V|vers |R|R|V|V|sequence number|reserved |U| +* --+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-- +* | | +* | | +* - - +* | | +* |stream id | +* --+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-- +* | | +* |AVTP timestamp | +* --+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-- +* | | | +* |OPENAVB format |OPENAVB reserved | +* --+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-- +* | | +* |OPENAVB format specific | +* -+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+- +* +* CD : Standard AVTP +* Subtype : Vendor specific 0x6f (Introduced in 1722A(D) +* SV : Standard AVTP +* Ver` : Standard AVTP +* MR : Standard AVTP +* R : Standard AVTP +* GV : Standard AVTP +* TV : Standard AVTP +* Sequence number : Standard AVTP +* Reserved : Standard AVTP +* TU : Standard AVTP +* Stream ID : Standard AVTP +* AVTP timestamp : Standard AVTP +* OPENAVB format : Null Mapping 0x00 +* OPENAVB reserved : +* OPENAVB format spec. } +* +*/ + +#include <stdlib.h> +#include <string.h> +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_avtp_time_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_map_pub.h" +#include "openavb_map_null_pub.h" + +#define AVB_LOG_COMPONENT "Null Mapping" +#include "openavb_log_pub.h" + +#define AVTP_V0_HEADER_SIZE 12 +#define MAP_HEADER_SIZE 12 + +#define TOTAL_HEADER_SIZE (AVTP_V0_HEADER_SIZE + MAP_HEADER_SIZE) + +#define MAX_PAYLOAD_SIZE 14 +#define MAX_DATA_SIZE (MAX_PAYLOAD_SIZE + TOTAL_HEADER_SIZE) + +#define ITEM_SIZE MAX_DATA_SIZE + + +////// +// AVTP Version 0 Header +////// + +// This mapping does not directly set or read these. +#define HIDX_AVTP_VERZERO96 0 + +// - 1 Byte - TV bit (timestamp valid) +#define HIDX_AVTP_HIDE7_TV1 1 + +// - 1 Byte - TU bit (timestamp uncertain) +#define HIDX_AVTP_HIDE7_TU1 3 + +////// +// Mapping specific header +////// + +// - 4 bytes avtp_timestamp +#define HIDX_AVTP_TIMESPAMP32 12 + +// - 1 byte OPENAVB format +#define HIDX_OPENAVB_FORMAT8 16 + +// - 3 bytes OPENAVB reserved +#define HIDX_OPENAVB_RESERVEDA8 17 +#define HIDX_OPENAVB_RESERVEDB8 18 +#define HIDX_OPENAVB_RESERVEDC8 19 + +// - 4 bytes OPENAVB format specific +#define HIDX_OPENAVB_FORMAT_SPEC32 20 + +typedef struct { + ///////////// + // Config data + ///////////// + // map_nv_item_count + U32 itemCount; + + // Transmit interval in frames per second. 0 = default for talker class. + U32 txInterval; + + ///////////// + // Variable data + ///////////// + U32 maxTransitUsec; // In microseconds + +} pvt_data_t; + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbMapNullCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + if (strcmp(name, "map_nv_item_count") == 0) { + char *pEnd; + pPvtData->itemCount = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_tx_rate") == 0 + || strcmp(name, "map_nv_tx_interval") == 0) { + char *pEnd; + pPvtData->txInterval = strtol(value, &pEnd, 10); + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// Returns the AVB subtype for this mapping +U8 openavbMapNullSubtypeCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0x7F; // Experimental AVB subtype +} + +// Returns the AVTP version used by this mapping +U8 openavbMapNullAvtpVersionCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return 0x00; // Version 0 +} + +// Returns the max data size +U16 openavbMapNullMaxDataSizeCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return MAX_DATA_SIZE; +} + +// Returns the intended transmit interval (in frames per second). 0 = default for talker / class. +U32 openavbMapNullTransmitIntervalCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return pPvtData->txInterval; + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0; +} + +void openavbMapNullGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + openavbMediaQSetSize(pMediaQ, pPvtData->itemCount, ITEM_SIZE); + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// A call to this callback indicates that this mapping module will be +// a talker. Any talker initialization can be done in this function. +void openavbMapNullTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// This talker callback will be called for each AVB observation interval. +tx_cb_ret_t openavbMapNullTxCB(media_q_t *pMediaQ, U8 *pData, U32 *dataLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + if (pMediaQ && pData && dataLen) { + U8 *pHdr = pData; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return TX_CB_RET_PACKET_NOT_READY; + } + + media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, TRUE); + if (pMediaQItem) { + // PTP walltime already set in the interface module. Just add the max transit time. + openavbAvtpTimeAddUSec(pMediaQItem->pAvtpTime, pPvtData->maxTransitUsec); + + // Set timestamp valid flag + if (openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime)) + pHdr[HIDX_AVTP_HIDE7_TV1] |= 0x01; // Set + else { + pHdr[HIDX_AVTP_HIDE7_TV1] &= ~0x01; // Clear + } + + // Set timestamp uncertain flag + if (openavbAvtpTimeTimestampIsUncertain(pMediaQItem->pAvtpTime)) + pHdr[HIDX_AVTP_HIDE7_TU1] |= 0x01; // Set + else pHdr[HIDX_AVTP_HIDE7_TU1] &= ~0x01; // Clear + + *(U32 *)(&pHdr[HIDX_AVTP_TIMESPAMP32]) = htonl(openavbAvtpTimeGetAvtpTimestamp(pMediaQItem->pAvtpTime)); + pHdr[HIDX_OPENAVB_FORMAT8] = MAP_NULL_OPENAVB_FORMAT; + pHdr[HIDX_OPENAVB_RESERVEDA8] = 0x00; + pHdr[HIDX_OPENAVB_RESERVEDB8] = 0x00; + pHdr[HIDX_OPENAVB_RESERVEDC8] = 0x00; + *(U32 *)(&pHdr[HIDX_OPENAVB_FORMAT_SPEC32]) = 0x00000000; + + *dataLen = TOTAL_HEADER_SIZE; // No data + openavbMediaQTailPull(pMediaQ); + AVB_TRACE_LINE(AVB_TRACE_MAP_LINE); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_READY; + } + else { + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; // Media queue empty + } + } + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; +} + +// A call to this callback indicates that this mapping module will be +// a listener. Any listener initialization can be done in this function. +void openavbMapNullRxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// This callback occurs when running as a listener and data is available. +bool openavbMapNullRxCB(media_q_t *pMediaQ, U8 *pData, U32 dataLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + if (pMediaQ && pData) { + U8 *pHdr = pData; + + media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (pMediaQItem) { + // Get the timestamp and place it in the media queue item. + U32 timestamp = ntohl(*(U32 *)(&pHdr[HIDX_AVTP_TIMESPAMP32])); + openavbAvtpTimeSetToTimestamp(pMediaQItem->pAvtpTime, timestamp); + + // Set timestamp valid and timestamp uncertain flags + openavbAvtpTimeSetTimestampValid(pMediaQItem->pAvtpTime, (pHdr[HIDX_AVTP_HIDE7_TV1] & 0x01) ? TRUE : FALSE); + openavbAvtpTimeSetTimestampUncertain(pMediaQItem->pAvtpTime, (pHdr[HIDX_AVTP_HIDE7_TU1] & 0x01) ? TRUE : FALSE); + + pMediaQItem->dataLen = 0; // Regardless of what is sent nullify the data to the interface. + openavbMediaQHeadPush(pMediaQ); + AVB_TRACE_LINE(AVB_TRACE_MAP_LINE); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TRUE; + } + else { + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; // Media queue full + } + } + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; +} + +// This callback will be called when the mapping module needs to be closed. +// All cleanup should occur in this function. +void openavbMapNullEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +void openavbMapNullGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Initialization entry point into the mapping module. Will need to be included in the .ini file. +extern DLL_EXPORT bool openavbMapNullInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pMediaQ->pMediaQDataFormat = strdup(MapNullMediaQDataFormat); + pMediaQ->pPvtMapInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pMediaQDataFormat || !pMediaQ->pPvtMapInfo) { + AVB_LOG_ERROR("Unable to allocate memory for mapping module."); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + + pMapCB->map_cfg_cb = openavbMapNullCfgCB; + pMapCB->map_subtype_cb = openavbMapNullSubtypeCB; + pMapCB->map_avtp_version_cb = openavbMapNullAvtpVersionCB; + pMapCB->map_max_data_size_cb = openavbMapNullMaxDataSizeCB; + pMapCB->map_transmit_interval_cb = openavbMapNullTransmitIntervalCB; + pMapCB->map_gen_init_cb = openavbMapNullGenInitCB; + pMapCB->map_tx_init_cb = openavbMapNullTxInitCB; + pMapCB->map_tx_cb = openavbMapNullTxCB; + pMapCB->map_rx_init_cb = openavbMapNullRxInitCB; + pMapCB->map_rx_cb = openavbMapNullRxCB; + pMapCB->map_end_cb = openavbMapNullEndCB; + pMapCB->map_gen_end_cb = openavbMapNullGenEndCB; + + pPvtData->itemCount = 20; + pPvtData->txInterval = 0; + pPvtData->maxTransitUsec = inMaxTransitUsec; + + openavbMediaQSetMaxLatency(pMediaQ, inMaxTransitUsec); + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return TRUE; +} diff --git a/lib/avtp_pipeline/map_null/openavb_map_null_pub.h b/lib/avtp_pipeline/map_null/openavb_map_null_pub.h new file mode 100755 index 00000000..3252b9e7 --- /dev/null +++ b/lib/avtp_pipeline/map_null/openavb_map_null_pub.h @@ -0,0 +1,44 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : NULL mapping module public interface +*/ + +#ifndef OPENAVB_MAP_NULL_PUB_H +#define OPENAVB_MAP_NULL_PUB_H 1 + +#include "openavb_types_pub.h" + +// NOTE: A define is used for the MediaQDataFormat identifier because it is needed in separate execution units (static / dynamic libraries) +// that is why a single static (static/extern pattern) definition can not be used. +#define MapNullMediaQDataFormat "Null" + +#endif // OPENAVB_MAP_NULL_PUB_H diff --git a/lib/avtp_pipeline/map_pipe/CMakeLists.txt b/lib/avtp_pipeline/map_pipe/CMakeLists.txt new file mode 100644 index 00000000..1216a087 --- /dev/null +++ b/lib/avtp_pipeline/map_pipe/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/map_pipe/openavb_map_pipe.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/map_pipe/openavb_map_pipe.c b/lib/avtp_pipeline/map_pipe/openavb_map_pipe.c new file mode 100755 index 00000000..61fa7653 --- /dev/null +++ b/lib/avtp_pipeline/map_pipe/openavb_map_pipe.c @@ -0,0 +1,488 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Pipe mapping module. +* +* The Pipe mapping is an AVB "experimental" mapping format. It will +* pass throught any data from interface modules unchanged. +* This can be useful for development, testing or custom solutions. +* +*----------------------------------------------------------------* +* +* HEADERS +* +* -+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+- +* |C| |S| |M| |G|T| | |T| +* |D|subtype |V|vers |R|R|V|V|sequence number|reserved |U| +* --+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-- +* | | +* | | +* - - +* | | +* |stream id | +* --+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-- +* | | +* |AVTP timestamp | +* --+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-- +* | | | | +* | Vendor_eui_1 | +* --+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-- +* | | +* | Data Length | Vendor_eui_2 | +* -+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+- +* +* CD : Standard AVTP +* Subtype : Vendor specific 0x6f (Introduced in 1722A(D) +* SV : Standard AVTP +* Ver` : Standard AVTP +* MR : Standard AVTP +* R : Standard AVTP +* GV : Standard AVTP +* TV : Standard AVTP +* Sequence number : Standard AVTP +* Reserved : Standard AVTP +* TU : Standard AVTP +* Stream ID : Standard AVTP +* AVTP timestamp : Standard AVTP +* Vendor_eui_1 : Vendor specific (includes OPENAVB format : Pipe Mapping 0x01) +* Data Length : Length of the data payload +* Vendor_eui_2 : Vendor specific +* +*/ + +#include "openavb_platform_pub.h" +#include <stdlib.h> +#include <string.h> +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_avtp_time_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_map_pub.h" +#include "openavb_map_pipe_pub.h" + +#define AVB_LOG_COMPONENT "Pipe Mapping" +#include "openavb_log_pub.h" + +#define AVTP_V0_HEADER_SIZE 12 +#define MAP_HEADER_SIZE 12 + +#define TOTAL_HEADER_SIZE (AVTP_V0_HEADER_SIZE + MAP_HEADER_SIZE) + +////// +// AVTP Version 0 Header +////// + +#define HIDX_AVTP_VERZERO96 0 + +// - 1 Byte - TV bit (timestamp valid) +#define HIDX_AVTP_HIDE7_TV1 1 + +// - 1 Byte - TU bit (timestamp uncertain) +#define HIDX_AVTP_HIDE7_TU1 3 + +// - 4 bytes avtp_timestamp +#define HIDX_AVTP_TIMESPAMP32 12 + +// - 2 bytes stream_data_len +#define HIDX_AVTP_DATALEN16 20 + +////// +// Mapping specific header +////// + +// - 1 byte OPENAVB format +#define HIDX_OPENAVB_FORMAT8 16 + +// - 2 bytes vendor specific +#define HIDX_VENDOR2_EUI16 22 + +typedef struct { + ///////////// + // Config data + ///////////// + // map_nv_item_count + U32 itemCount; + + // Transmit interval in frames per second. 0 = default for talker class. + U32 txInterval; + + // map_nv_push_header + bool push_header; + + // map_nv_pull_header + bool pull_header; + + + ///////////// + // Variable data + ///////////// + // Max payload size + U32 maxPayloadSize; + + // Maximum data size + U32 maxDataSize; + + // Maximum media queue item size + U32 itemSize; + + // Maximum transit time + U32 maxTransitUsec; // In microseconds + +} pvt_data_t; + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbMapPipeCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + if (strcmp(name, "map_nv_item_count") == 0) { + char *pEnd; + pPvtData->itemCount = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_tx_rate") == 0 + || strcmp(name, "map_nv_tx_interval") == 0) { + char *pEnd; + pPvtData->txInterval = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_max_payload_size") == 0) { + char *pEnd; + pPvtData->maxPayloadSize = strtol(value, &pEnd, 10); + pPvtData->maxDataSize = (pPvtData->maxPayloadSize + TOTAL_HEADER_SIZE); + pPvtData->itemSize = pPvtData->maxDataSize; + } + else if (strcmp(name, "map_nv_push_header") == 0) { + char *pEnd; + long tmp; + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->push_header = (tmp == 1); + } + } + else if (strcmp(name, "map_nv_pull_header") == 0) { + char *pEnd; + long tmp; + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->pull_header = (tmp == 1); + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +U8 openavbMapPipeSubtypeCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0x6F; // Vendor Specific AVTP subtype +} + +// Returns the AVTP version used by this mapping +U8 openavbMapPipeAvtpVersionCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return 0x00; // Version 0 +} + +U16 openavbMapPipeMaxDataSizeCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return pPvtData->maxDataSize + TOTAL_HEADER_SIZE; + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0; +} + +// Returns the intended transmit interval (in frames per second). 0 = default for talker / class. +U32 openavbMapPipeTransmitIntervalCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return pPvtData->txInterval; + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0; +} + +void openavbMapPipeGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + openavbMediaQSetSize(pMediaQ, pPvtData->itemCount, pPvtData->itemSize); + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// A call to this callback indicates that this mapping module will be +// a talker. Any talker initialization can be done in this function. +void openavbMapPipeTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// This talker callback will be called for each AVB observation interval. +tx_cb_ret_t openavbMapPipeTxCB(media_q_t *pMediaQ, U8 *pData, U32 *dataLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + if (pMediaQ && pData && dataLen) { + U8 *pHdr = pData; + U8 *pPayload = pData + TOTAL_HEADER_SIZE; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return TX_CB_RET_PACKET_NOT_READY; + } + + media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, TRUE); + + if (pMediaQItem) { + if (pMediaQItem->dataLen > 0) { + if (pMediaQItem->dataLen > pPvtData->maxDataSize) { + AVB_LOGF_ERROR("Media queue data item size too large. Reported size: %d Max Size: %d", pMediaQItem->dataLen, pPvtData->maxDataSize); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + openavbMediaQTailPull(pMediaQ); + return TX_CB_RET_PACKET_NOT_READY; + } + + // PTP walltime already set in the interface module. Just add the max transit time. + openavbAvtpTimeAddUSec(pMediaQItem->pAvtpTime, pPvtData->maxTransitUsec); + + // Set timestamp valid flag + if (openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime)) + pHdr[HIDX_AVTP_HIDE7_TV1] |= 0x01; // Set + else { + pHdr[HIDX_AVTP_HIDE7_TV1] &= ~0x01; // Clear + } + + // Set timestamp uncertain flag + if (openavbAvtpTimeTimestampIsUncertain(pMediaQItem->pAvtpTime)) + pHdr[HIDX_AVTP_HIDE7_TU1] |= 0x01; // Set + else pHdr[HIDX_AVTP_HIDE7_TU1] &= ~0x01; // Clear + + *(U32 *)(&pHdr[HIDX_AVTP_TIMESPAMP32]) = htonl(openavbAvtpTimeGetAvtpTimestamp(pMediaQItem->pAvtpTime)); + *(U32 *)(&pHdr[HIDX_OPENAVB_FORMAT8]) = 0x00000000; + pHdr[HIDX_OPENAVB_FORMAT8] = MAP_PIPE_OPENAVB_FORMAT; + // for alignment + U16 payloadLen = htons(pMediaQItem->dataLen);; + memcpy(&pHdr[HIDX_AVTP_DATALEN16], &payloadLen, sizeof(U16)); + + *(U16 *)(&pHdr[HIDX_VENDOR2_EUI16]) = 0x0000; + + if (pPvtData->pull_header) { + memcpy(pData, pMediaQItem->pPubData, pMediaQItem->dataLen); + *dataLen = pMediaQItem->dataLen; + } + else { + memcpy(pPayload, pMediaQItem->pPubData, pMediaQItem->dataLen); + *dataLen = pMediaQItem->dataLen + TOTAL_HEADER_SIZE; + } + + openavbMediaQTailPull(pMediaQ); + AVB_TRACE_LINE(AVB_TRACE_MAP_LINE); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_READY; + } + else { + openavbMediaQTailPull(pMediaQ); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; // No payload + } + } + else { + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; // Media queue empty + } + } + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; +} + +// A call to this callback indicates that this mapping module will be +// a listener. Any listener initialization can be done in this function. +void openavbMapPipeRxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// This callback occurs when running as a listener and data is available. +bool openavbMapPipeRxCB(media_q_t *pMediaQ, U8 *pData, U32 dataLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + if (pMediaQ && pData) { + const U8 *pHdr = pData; + U8 *pPayload = pData + TOTAL_HEADER_SIZE; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return FALSE; + } + + media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (pMediaQItem) { + U32 timestamp = ntohl(*(U32 *)(&pHdr[HIDX_AVTP_TIMESPAMP32])); + openavbAvtpTimeSetToTimestamp(pMediaQItem->pAvtpTime, timestamp); + + // Set timestamp valid and timestamp uncertain flags + openavbAvtpTimeSetTimestampValid(pMediaQItem->pAvtpTime, (pHdr[HIDX_AVTP_HIDE7_TV1] & 0x01) ? TRUE : FALSE); + openavbAvtpTimeSetTimestampUncertain(pMediaQItem->pAvtpTime, (pHdr[HIDX_AVTP_HIDE7_TU1] & 0x01) ? TRUE : FALSE); + + if (pPvtData->push_header) { + if (pMediaQItem->itemSize >= dataLen) { + memcpy(pMediaQItem->pPubData, pData, dataLen); + pMediaQItem->dataLen = dataLen; + } + else { + AVB_LOG_ERROR("Data to large for media queue."); + pMediaQItem->dataLen = 0; + } + } + else { + // for alignment + U16 payloadLen; + memcpy(&payloadLen, &pHdr[HIDX_AVTP_DATALEN16], sizeof(U16)); + payloadLen = ntohs(payloadLen); + + if (pMediaQItem->itemSize >= payloadLen) { + memcpy(pMediaQItem->pPubData, pPayload, payloadLen); + pMediaQItem->dataLen = payloadLen; + } + else { + AVB_LOG_ERROR("Data to large for media queue."); + pMediaQItem->dataLen = 0; + } + } + + openavbMediaQHeadPush(pMediaQ); + AVB_TRACE_LINE(AVB_TRACE_MAP_LINE); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TRUE; + } + else { + IF_LOG_INTERVAL(1000) AVB_LOG_INFO("Media queue full"); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; // Media queue full + } + } + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; +} + +// This callback will be called when the mapping module needs to be closed. +// All cleanup should occur in this function. +void openavbMapPipeEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +void openavbMapPipeGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Initialization entry point into the mapping module. Will need to be included in the .ini file. +extern DLL_EXPORT bool openavbMapPipeInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pMediaQ->pMediaQDataFormat = strdup(MapPipeMediaQDataFormat); + pMediaQ->pPvtMapInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pMediaQDataFormat || !pMediaQ->pPvtMapInfo) { + AVB_LOG_ERROR("Unable to allocate memory for mapping module."); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + + pMapCB->map_cfg_cb = openavbMapPipeCfgCB; + pMapCB->map_subtype_cb = openavbMapPipeSubtypeCB; + pMapCB->map_avtp_version_cb = openavbMapPipeAvtpVersionCB; + pMapCB->map_max_data_size_cb = openavbMapPipeMaxDataSizeCB; + pMapCB->map_transmit_interval_cb = openavbMapPipeTransmitIntervalCB; + pMapCB->map_gen_init_cb = openavbMapPipeGenInitCB; + pMapCB->map_tx_init_cb = openavbMapPipeTxInitCB; + pMapCB->map_tx_cb = openavbMapPipeTxCB; + pMapCB->map_rx_init_cb = openavbMapPipeRxInitCB; + pMapCB->map_rx_cb = openavbMapPipeRxCB; + pMapCB->map_end_cb = openavbMapPipeEndCB; + pMapCB->map_gen_end_cb = openavbMapPipeGenEndCB; + + pPvtData->itemCount = 20; + pPvtData->txInterval = 0; + pPvtData->push_header = FALSE; + pPvtData->pull_header = FALSE; + pPvtData->maxTransitUsec = inMaxTransitUsec; + + pPvtData->maxPayloadSize = 1024; + pPvtData->maxDataSize = (pPvtData->maxPayloadSize + TOTAL_HEADER_SIZE); + pPvtData->itemSize = pPvtData->maxDataSize; + + openavbMediaQSetMaxLatency(pMediaQ, inMaxTransitUsec); + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return TRUE; +} + diff --git a/lib/avtp_pipeline/map_pipe/openavb_map_pipe_pub.h b/lib/avtp_pipeline/map_pipe/openavb_map_pipe_pub.h new file mode 100755 index 00000000..badb8c0b --- /dev/null +++ b/lib/avtp_pipeline/map_pipe/openavb_map_pipe_pub.h @@ -0,0 +1,44 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : NULL mapping module public interface +*/ + +#ifndef OPENAVB_MAP_PIPE_PUB_H +#define OPENAVB_MAP_PIPE_PUB_H 1 + +#include "openavb_types_pub.h" + +// NOTE: A define is used for the MediaQDataFormat identifier because it is needed in separate execution units (static / dynamic libraries) +// that is why a single static (static/extern pattern) definition can not be used. +#define MapPipeMediaQDataFormat "Pipe" + +#endif // OPENAVB_MAP_PIPE_PUB_H diff --git a/lib/avtp_pipeline/map_pipe/pipe_map.md b/lib/avtp_pipeline/map_pipe/pipe_map.md new file mode 100644 index 00000000..5b6f8d6d --- /dev/null +++ b/lib/avtp_pipeline/map_pipe/pipe_map.md @@ -0,0 +1,24 @@ +Pipe Mapping {#pipe_map} +============ + +# Description + +The Pipe Mapping module is an AVTP vendor specific mapping format. It will pass through +any data from interface modules unchanged. This can be useful for development, +testing or custom solutions + +<br> +# Mapping module configuration parameters + +Name | Description +--------------------|--------------------------- +map_nv_item_count |The number of Media Queue items to hold. +map_nv_tx_rate or map_nv_tx_interval | Transmit interval in frames per second. \ + 0 = default for talker class +map_nv_max_payload_size| Maximum payload that will be send in one Ethernet frame +map_nv_push_header |If set to 1 the Ethernet header should be pushed to the \ + Media Queue <br> \ + <b>Note</b>:RX side only - Listener +map_nv_pull_header |If set to 1 data in Media Queue is with Ethernet header \ + <br> \ + <b>Note</b>:TX side only - Talker diff --git a/lib/avtp_pipeline/map_uncmp_audio/CMakeLists.txt b/lib/avtp_pipeline/map_uncmp_audio/CMakeLists.txt new file mode 100644 index 00000000..1a2a21d2 --- /dev/null +++ b/lib/avtp_pipeline/map_uncmp_audio/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/map_uncmp_audio/openavb_map_uncmp_audio.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/map_uncmp_audio/openavb_map_uncmp_audio.c b/lib/avtp_pipeline/map_uncmp_audio/openavb_map_uncmp_audio.c new file mode 100755 index 00000000..20ce0c88 --- /dev/null +++ b/lib/avtp_pipeline/map_uncmp_audio/openavb_map_uncmp_audio.c @@ -0,0 +1,759 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Uncompressed audio mapping module conforming to AVB 61883-6 encapsulation. +*/ + +#include <stdlib.h> +#include <string.h> +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_avtp_time_pub.h" +#include "openavb_mediaq_pub.h" + +// TODO_OPENAVB : Is this needed? +//#include "openavb_avdecc_pub.h" + +#include "openavb_map_pub.h" +#include "openavb_map_uncmp_audio_pub.h" + +// DEBUG Uncomment to turn on logging for just this module. +#define AVB_LOG_ON 1 + +#define AVB_LOG_COMPONENT "61883-6 Mapping" +#include "openavb_log_pub.h" + +// Header sizes +#define AVTP_V0_HEADER_SIZE 12 +#define MAP_HEADER_SIZE 12 +#define CIP_HEADER_SIZE 8 + +#define TOTAL_HEADER_SIZE (AVTP_V0_HEADER_SIZE + MAP_HEADER_SIZE + CIP_HEADER_SIZE) + +////// +// AVTP Version 0 Header +////// + +// This mapping does not directly set or read these. +#define HIDX_AVTP_VERZERO96 0 + +// - 1 Byte - TV bit (timestamp valid) +#define HIDX_AVTP_HIDE7_TV1 1 + +// - 1 Byte - TU bit (timestamp uncertain) +#define HIDX_AVTP_HIDE7_TU1 3 + +////// +// Mapping specific header +////// + +// - 4 bytes avtp_timestamp +#define HIDX_AVTP_TIMESTAMP32 12 + +// - 4 bytes gateway_info +#define HIDX_GATEWAY32 16 + +// - 2 bytes stream_data_len (save a pointer for later use) +#define HIDX_DATALEN16 20 + +// 2 bit tag = binary 01 (CIP header - included) +// 6 bit channel = 31 / 0x1F (Native AVB) +#define HIDX_TAG2_CHANNEL6 22 + +// 4 bits tcode = 0xA +// 4 bits sy (application-specific) = 0 +#define HIDX_TCODE4_SY4 23 + +////// +// CIP Header - 2 quadlets (8 bytes) +////// + +// 2 bits cip flag = binary 00 +// 6 bits sid (source identifier) = 63 / 0x3F (On the AVB network) +#define HIDX_CIP2_SID6 24 + +// 8 bits dbs (data block size) = Same as audio frame size. Sample Size * channels +#define HIDX_DBS8 25 + +// 2 bits FN (fraction number) = Bx00 +// 3 bits QPC (quadlet padding count) = Bx000 +// 1 bit SPH (source packet header) = Bx0 (not using source packet headers) +// 2 bits RSV (reserved) = Bx00 +#define HIDX_FN2_QPC3_SPH1_RSV2 26 + +// 8 bits DBC (data block counter) = counter +#define HIDX_DBC8 27 + +// 2 bits cip flag (2nd quadlet) = binary 10 +// 6 bits fmt (stream format) = binary 010000 (61883-6) +#define HIDX_CIP2_FMT6 28 + +// 5 bits fdf (format dependant field) = 0 +// 3 bits SFC (sample frequency) = based on rate +#define HIDX_FDF5_SFC3 29 + +// 2 bytes syt (synchronization timing) Set to 0xffff according to 1722 +#define HIDX_SYT16 30 + +typedef struct { + ///////////// + // Config data + ///////////// + // map_nv_item_count + U32 itemCount; + + // Transmit interval in frames per second. 0 = default for talker class. + U32 txInterval; + + // A multiple of how many frames of audio to accept in an media queue item and into the AVTP payload above + // the minimal needed. + U32 packingFactor; + + ///////////// + // Variable data + ///////////// + U32 maxTransitUsec; // In microseconds + + U8 cip_sfc; + + U32 AM824_label; + + U32 maxPayloadSize; + + // Data block continuity counter + U8 DBC; + + avb_audio_mcr_t audioMcr; + +} pvt_data_t; + +static void x_calculateSizes(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + media_q_pub_map_uncmp_audio_info_t *pPubMapInfo = pMediaQ->pPubMapInfo; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + switch (pPubMapInfo->audioRate) { + case AVB_AUDIO_RATE_32KHZ: + pPvtData->cip_sfc = 0; + pPubMapInfo->sytInterval = 8; + break; + + case AVB_AUDIO_RATE_44_1KHZ: + pPvtData->cip_sfc = 1; + pPubMapInfo->sytInterval = 8; + break; + + case AVB_AUDIO_RATE_48KHZ: + pPvtData->cip_sfc = 2; + pPubMapInfo->sytInterval = 8; + break; + + case AVB_AUDIO_RATE_88_2KHZ: + pPvtData->cip_sfc = 3; + pPubMapInfo->sytInterval = 16; + break; + + case AVB_AUDIO_RATE_96KHZ: + pPvtData->cip_sfc = 4; + pPubMapInfo->sytInterval = 16; + break; + + case AVB_AUDIO_RATE_176_4KHZ: + pPvtData->cip_sfc = 5; + pPubMapInfo->sytInterval = 32; + break; + + case AVB_AUDIO_RATE_192KHZ: + pPvtData->cip_sfc = 6; + pPubMapInfo->sytInterval = 32; + break; + + default: + AVB_LOG_ERROR("Invalid audio frequency configured."); + pPvtData->cip_sfc = 2; + pPubMapInfo->sytInterval = 8; + break; + } + + pPubMapInfo->framesPerPacket = (pPubMapInfo->audioRate / pPvtData->txInterval); + if (pPubMapInfo->framesPerPacket < 1) { + pPubMapInfo->framesPerPacket = 1; + } + if (pPubMapInfo->audioRate % pPvtData->txInterval != 0) { + AVB_LOGF_WARNING("audio rate (%d) is not an integer multiple of TX rate (%d). Recommend TX rate of (%d)", + pPubMapInfo->audioRate, pPvtData->txInterval, pPubMapInfo->audioRate / (pPubMapInfo->framesPerPacket + 1)); + pPubMapInfo->framesPerPacket += 1; + } + + pPubMapInfo->packingFactor = pPvtData->packingFactor; + if (pPubMapInfo->packingFactor > 1) { + // Packing multiple packets of sampling into a media queue item + pPubMapInfo->framesPerItem = pPubMapInfo->framesPerPacket * pPvtData->packingFactor; + if (pPubMapInfo->framesPerItem < 1) { + pPubMapInfo->framesPerItem = 1; + } + } + else { + // No packing. SYT_INTERVAL is used for media queue item size. + pPubMapInfo->framesPerItem = pPubMapInfo->sytInterval; + if (pPubMapInfo->framesPerItem < 1) { + pPubMapInfo->framesPerItem = 1; + } + } + + pPubMapInfo->packetSampleSizeBytes = 4; + + AVB_LOGF_INFO("Rate:%d", pPubMapInfo->audioRate); + AVB_LOGF_INFO("Bits:%d", pPubMapInfo->audioBitDepth); + AVB_LOGF_INFO("Channels:%d", pPubMapInfo->audioChannels); + AVB_LOGF_INFO("Packet Interval:%d", pPvtData->txInterval); + AVB_LOGF_INFO("Frames per packet:%d", pPubMapInfo->framesPerPacket); + AVB_LOGF_INFO("Packing Factor:%d", pPvtData->packingFactor); + AVB_LOGF_INFO("Frames per MediaQ Item:%d", pPubMapInfo->framesPerItem); + AVB_LOGF_INFO("Sample Size Bytes:%d", pPubMapInfo->packetSampleSizeBytes); + + if (pPubMapInfo->audioBitDepth == AVB_AUDIO_BIT_DEPTH_16BIT || pPubMapInfo->audioBitDepth == AVB_AUDIO_BIT_DEPTH_20BIT) { + // TODO: The 20Bit format was downgraded to 16 bit and therefore 2 bytes + pPubMapInfo->itemSampleSizeBytes = 2; + } + else if (pPubMapInfo->audioBitDepth == AVB_AUDIO_BIT_DEPTH_24BIT) { + pPubMapInfo->itemSampleSizeBytes = 4; + } + else { + AVB_LOGF_ERROR("Invalid audio format configured: %u", pPubMapInfo->audioBitDepth); + pPubMapInfo->itemSampleSizeBytes = 1; + } + + pPubMapInfo->packetFrameSizeBytes = pPubMapInfo->packetSampleSizeBytes * pPubMapInfo->audioChannels; + pPubMapInfo->packetAudioDataSizeBytes = pPubMapInfo->framesPerPacket * pPubMapInfo->packetFrameSizeBytes; + pPvtData->maxPayloadSize = pPubMapInfo->packetAudioDataSizeBytes + TOTAL_HEADER_SIZE; + + pPubMapInfo->itemFrameSizeBytes = pPubMapInfo->itemSampleSizeBytes * pPubMapInfo->audioChannels; + pPubMapInfo->itemSize = pPubMapInfo->itemFrameSizeBytes * pPubMapInfo->framesPerItem; + + switch (pPubMapInfo->audioBitDepth) { + case AVB_AUDIO_BIT_DEPTH_16BIT: + pPvtData->AM824_label = 0x42000000; + break; + + case AVB_AUDIO_BIT_DEPTH_20BIT: + AVB_LOG_ERROR("20 bit not currently supported. Downgraded to 16 bit."); + pPvtData->AM824_label = 0x42000000; + break; + + case AVB_AUDIO_BIT_DEPTH_24BIT: + pPvtData->AM824_label = 0x40000000; + break; + + default: + AVB_LOG_ERROR("Invalid audio format configured."); + pPvtData->AM824_label = 0x40000000; + break; + } + + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbMapUncmpAudioCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + if (strcmp(name, "map_nv_item_count") == 0) { + char *pEnd; + pPvtData->itemCount = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_tx_rate") == 0 + || strcmp(name, "map_nv_tx_interval") == 0) { + char *pEnd; + pPvtData->txInterval = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_packing_factor") == 0) { + char *pEnd; + pPvtData->packingFactor = strtol(value, &pEnd, 10); + } + else if (strcmp(name, "map_nv_audio_mcr") == 0) { + char *pEnd; + pPvtData->audioMcr = (avb_audio_mcr_t)strtol(value, &pEnd, 10); + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +U8 openavbMapUncmpAudioSubtypeCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0x00; // 61883 AVB subtype +} + +// Returns the AVTP version used by this mapping +U8 openavbMapUncmpAudioAvtpVersionCB() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return 0x00; // Version 0 +} + +U16 openavbMapUncmpAudioMaxDataSizeCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return pPvtData->maxPayloadSize; + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0; +} + +// Returns the intended transmit interval (in frames per second). 0 = default for talker / class. +U32 openavbMapUncmpAudioTransmitIntervalCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return pPvtData->txInterval; + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return 0; +} + +void openavbMapUncmpAudioGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + if (pMediaQ) { + media_q_pub_map_uncmp_audio_info_t *pPubMapInfo = pMediaQ->pPubMapInfo; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + return; + } + + x_calculateSizes(pMediaQ); + openavbMediaQSetSize(pMediaQ, pPvtData->itemCount, pPubMapInfo->itemSize); + } + AVB_TRACE_EXIT(AVB_TRACE_MAP); + } + +void openavbMapUncmpAudioAVDECCInitCB(media_q_t *pMediaQ, U16 configIdx, U16 descriptorType, U16 descriptorIdx) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// A call to this callback indicates that this mapping module will be +// a talker. Any talker initialization can be done in this function. +void openavbMapUncmpAudioTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + // Check the media queue for proper allocations to avoid doing it on each tx callback. + if (!pMediaQ) { + AVB_LOG_ERROR("Media queue not allocated."); + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return; + } + + media_q_pub_map_uncmp_audio_info_t *pPubMapInfo = pMediaQ->pPubMapInfo; + if (!pPubMapInfo) { + AVB_LOG_ERROR("Public mapping module data not allocated."); + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private mapping module data not allocated."); + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return; + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// This talker callback will be called for each AVB observation interval. +tx_cb_ret_t openavbMapUncmpAudioTxCB(media_q_t *pMediaQ, U8 *pData, U32 *dataLen) +{ + media_q_item_t *pMediaQItem = NULL; + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + + if (!pData || !dataLen) { + AVB_LOG_ERROR("Mapping module data or data length argument incorrect."); + return TX_CB_RET_PACKET_NOT_READY; + } + + media_q_pub_map_uncmp_audio_info_t *pPubMapInfo = pMediaQ->pPubMapInfo; + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + + if (*dataLen - TOTAL_HEADER_SIZE < pPubMapInfo->packetAudioDataSizeBytes) { + AVB_LOG_ERROR("Not enough room in packet for audio frames."); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; + } + + if (openavbMediaQIsAvailableBytes(pMediaQ, pPubMapInfo->packetAudioDataSizeBytes, TRUE)) { + U8 *pHdr = pData; + U8 *pPayload = pData + TOTAL_HEADER_SIZE; + + //pHdr[HIDX_AVTP_TIMESTAMP32] = 0x00; // Set later + *(U32 *)(&pHdr[HIDX_GATEWAY32]) = 0x00000000; + //pHdr[HIDX_DATALEN16] = 0x00; // Set Later + pHdr[HIDX_TAG2_CHANNEL6] = (1 << 6) | 0x1f; + pHdr[HIDX_TCODE4_SY4] = (0x0a << 4) | 0; + + // Set the majority of the CIP header now. + pHdr[HIDX_CIP2_SID6] = (0x00 << 6) | 0x3f; + pHdr[HIDX_DBS8] = pPubMapInfo->audioChannels; + + pHdr[HIDX_FN2_QPC3_SPH1_RSV2] = (0x00 << 6) | (0x00 << 3) | (0x00 << 2) | 0x00; + // pHdr[HIDX_DBC8] = 0; // Set later + pHdr[HIDX_CIP2_FMT6] = (0x02 << 6) | 0x10; + pHdr[HIDX_FDF5_SFC3] = 0x00 << 3 | pPvtData->cip_sfc; + *(U16 *)(&pHdr[HIDX_SYT16]) = 0xffff; + + U32 framesProcessed = 0; + U8 *pAVTPDataUnit = pPayload; + bool timestampSet = FALSE; // index of the timestamp + while (framesProcessed < pPubMapInfo->framesPerPacket) { + pMediaQItem = openavbMediaQTailLock(pMediaQ, TRUE); + + if (pMediaQItem && pMediaQItem->dataLen > 0) { + if (pMediaQItem->readIdx == 0) { + // Timestamp from the media queue is always assoicated with the first data point. + + // Update time stamp + + // PTP walltime already set in the interface module. Just add the max transit time. + openavbAvtpTimeAddUSec(pMediaQItem->pAvtpTime, pPvtData->maxTransitUsec); + + // Set timestamp valid flag + if (openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime)) + pHdr[HIDX_AVTP_HIDE7_TV1] |= 0x01; // Set + else { + pHdr[HIDX_AVTP_HIDE7_TV1] &= ~0x01; // Clear + } + + // Set timestamp uncertain flag + if (openavbAvtpTimeTimestampIsUncertain(pMediaQItem->pAvtpTime)) + pHdr[HIDX_AVTP_HIDE7_TU1] |= 0x01; // Set + else pHdr[HIDX_AVTP_HIDE7_TU1] &= ~0x01; // Clear + + *(U32 *)(&pHdr[HIDX_AVTP_TIMESTAMP32]) = htonl(openavbAvtpTimeGetAvtpTimestamp(pMediaQItem->pAvtpTime)); + + timestampSet = TRUE; + } + + U8 *pItemData = (U8 *)pMediaQItem->pPubData + pMediaQItem->readIdx; + while (framesProcessed < pPubMapInfo->framesPerPacket && pMediaQItem->readIdx < pMediaQItem->dataLen) { + int i1; + for (i1 = 0; i1 < pPubMapInfo->audioChannels; i1++) { + if (pPubMapInfo->itemSampleSizeBytes == 2) { + S32 sample = *(S16 *)pItemData; + sample &= 0x0000ffff; + sample = sample << 8; + sample |= pPvtData->AM824_label; + sample = htonl(sample); + *(U32 *)(pAVTPDataUnit) = sample; + pAVTPDataUnit += 4; + pItemData += 2; + } + else { + S32 sample = *(S32 *)pItemData; + sample &= 0x00ffffff; + sample |= pPvtData->AM824_label; + sample = htonl(sample); + *(U32 *)(pAVTPDataUnit) = sample; + pAVTPDataUnit += 4; + pItemData += 4; + } + } + framesProcessed++; + pMediaQItem->readIdx += pPubMapInfo->itemFrameSizeBytes; + } + + if (pMediaQItem->readIdx >= pMediaQItem->dataLen) { + // Read the entire item + openavbMediaQTailPull(pMediaQ); + } + else { + // More to read next interval + openavbMediaQTailUnlock(pMediaQ); + } + } + else { + openavbMediaQTailPull(pMediaQ); + } + } + + // Check if timestamp was set + if (!timestampSet) { + // Timestamp wasn't set so mark it as invalid + pHdr[HIDX_AVTP_HIDE7_TV1] &= ~0x01; + } + + // Set the block continutity and data length + pHdr[HIDX_DBC8] = pPvtData->DBC; + pPvtData->DBC += pPubMapInfo->framesPerPacket; + + *(U16 *)(&pHdr[HIDX_DATALEN16]) = htons((pPubMapInfo->framesPerPacket * pPubMapInfo->packetFrameSizeBytes) + CIP_HEADER_SIZE); + + // Set out bound data length (entire packet length) + *dataLen = (pPubMapInfo->framesPerPacket * pPubMapInfo->packetFrameSizeBytes) + TOTAL_HEADER_SIZE; + + AVB_TRACE_LINE(AVB_TRACE_MAP_LINE); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_READY; + + } + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TX_CB_RET_PACKET_NOT_READY; +} + +// A call to this callback indicates that this mapping module will be +// a listener. Any listener initialization can be done in this function. +void openavbMapUncmpAudioRxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +// This callback occurs when running as a listener and data is available. +bool openavbMapUncmpAudioRxCB(media_q_t *pMediaQ, U8 *pData, U32 dataLen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP_DETAIL); + if (pMediaQ && pData) { + U8 *pHdr = pData; + U8 *pPayload = pData + TOTAL_HEADER_SIZE; + media_q_pub_map_uncmp_audio_info_t *pPubMapInfo = pMediaQ->pPubMapInfo; + + //pHdr[HIDX_AVTP_TIMESTAMP32]; + //pHdr[HIDX_GATEWAY32]; + U16 payloadLen = ntohs(*(U16 *)(&pHdr[HIDX_DATALEN16])); + + //pHdr[HIDX_TAG2_CHANNEL6]; + //pHdr[HIDX_TCODE4_SY4]; + //pHdr[HIDX_CIP2_SID6]; + //pHdr[HIDX_DBS8]; + //pHdr[HIDX_FN2_QPC3_SPH1_RSV2]; + U8 dbc = pHdr[HIDX_DBC8]; + //pHdr[HIDX_CIP2_FMT6]; + //pHdr[HIDX_TSF1_RESA7]; + //pHdr[HIDX_RESB8]; + //pHdr[HIDX_RESC8]; + bool tsValid = (pHdr[HIDX_AVTP_HIDE7_TV1] & 0x01) ? TRUE : FALSE; + bool tsUncertain = (pHdr[HIDX_AVTP_HIDE7_TU1] & 0x01) ? TRUE : FALSE; + + // Per iec61883-6 Secion 7.2 + // index = mod((SYT_INTERVAL - mod(DBC, SYT_INTERVAL)), SYT_INTERVAL) + // U8 dbcIdx = (pPubMapInfo->sytInterval - (dbc % pPubMapInfo->sytInterval)) % pPubMapInfo->sytInterval; + // The dbcIdx calculation isn't used from spec because our implmentation only needs to know when the timestamp index is at 0. + U8 dbcIdx = dbc % pPubMapInfo->sytInterval; + U8 *pAVTPDataUnit = pPayload; + U8 *pAVTPDataUnitEnd = pData + AVTP_V0_HEADER_SIZE + MAP_HEADER_SIZE + payloadLen; + + while (((pAVTPDataUnit + pPubMapInfo->packetFrameSizeBytes) <= pAVTPDataUnitEnd)) { + // Get item pointer in media queue + media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (pMediaQItem) { + + U32 itemSizeWritten = 0; + U8 *pItemData = (U8 *)pMediaQItem->pPubData + pMediaQItem->dataLen; + U8 *pItemDataEnd = (U8 *)pMediaQItem->pPubData + pMediaQItem->itemSize; + + // Get the timestamp + U32 timestamp = ntohl(*(U32 *)(&pHdr[HIDX_AVTP_TIMESTAMP32])); + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + if ((pPvtData->audioMcr != AVB_MCR_NONE) && tsValid && !tsUncertain) { + // MCR mode set and timestamp is valid, and timestamp uncertain is not set + openavbAvtpTimePushMCR(pMediaQItem->pAvtpTime, timestamp); + } + + if (pMediaQItem->dataLen == 0) { + // This is the first set of frames for the media queue item, must align based on SYT_INTERVAL for proper synchronization of listeners + if (dbcIdx > 0) { + // Failed to make alignment with this packet. This AVTP packet will be tossed. Once alignment is reached + // it is expected not to need to toss packets anymore. + openavbMediaQHeadUnlock(pMediaQ); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TRUE; + } + + // Set time stamp info on first data write to the media queue + // place it in the media queue item. + openavbAvtpTimeSetToTimestamp(pMediaQItem->pAvtpTime, timestamp); + + // Set timestamp valid and timestamp uncertain flags + openavbAvtpTimeSetTimestampValid(pMediaQItem->pAvtpTime, tsValid); + openavbAvtpTimeSetTimestampUncertain(pMediaQItem->pAvtpTime, tsUncertain); + } + + while (((pAVTPDataUnit + pPubMapInfo->packetFrameSizeBytes) <= pAVTPDataUnitEnd) && ((pItemData + pPubMapInfo->itemFrameSizeBytes) <= pItemDataEnd)) { + int i1; + for (i1 = 0; i1 < pPubMapInfo->audioChannels; i1++) { + if (pPubMapInfo->itemSampleSizeBytes == 2) { + S32 sample = ntohl(*(S32 *)pAVTPDataUnit); + sample = sample * 1; + *(S16 *)(pItemData) = (sample & 0x00ffffff) >> 8; + pAVTPDataUnit += 4; + pItemData += 2; + itemSizeWritten += 2; + } + else { + S32 sample = ntohl(*(S32 *)pAVTPDataUnit); + sample = sample * 1; + *(S32 *)(pItemData) = sample & 0x00ffffff; + pAVTPDataUnit += 4; + pItemData += 4; + itemSizeWritten += 4; + } + } + } + + pMediaQItem->dataLen += itemSizeWritten; + + if (pMediaQItem->dataLen < pMediaQItem->itemSize) { + // More data can be written to the item + openavbMediaQHeadUnlock(pMediaQ); + } + else { + // The item is full push it. + openavbMediaQHeadPush(pMediaQ); + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TRUE; // Normal exit + } + else { + IF_LOG_INTERVAL(1000) AVB_LOG_INFO("Media queue full"); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; // Media queue full + } + } + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return TRUE; // Normal exit + } + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; +} + +// This callback will be called when the mapping module needs to be closed. +// All cleanup should occur in this function. +void openavbMapUncmpAudioEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + AVB_TRACE_EXIT(AVB_TRACE_MAP); +} + +void openavbMapUncmpAudioGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Initialization entry point into the mapping module. Will need to be included in the .ini file. +extern DLL_EXPORT bool openavbMapUncmpAudioInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MAP); + + if (pMediaQ) { + pMediaQ->pMediaQDataFormat = strdup(MapUncmpAudioMediaQDataFormat); + pMediaQ->pPubMapInfo = calloc(1, sizeof(media_q_pub_map_uncmp_audio_info_t)); // Memory freed by the media queue when the media queue is destroyed. + pMediaQ->pPvtMapInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pMediaQDataFormat || !pMediaQ->pPubMapInfo || !pMediaQ->pPvtMapInfo) { + AVB_LOG_ERROR("Unable to allocate memory for mapping module."); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo; + media_q_pub_map_uncmp_audio_info_t *pPubMapInfo = pMediaQ->pPubMapInfo; + + pMapCB->map_cfg_cb = openavbMapUncmpAudioCfgCB; + pMapCB->map_subtype_cb = openavbMapUncmpAudioSubtypeCB; + pMapCB->map_avtp_version_cb = openavbMapUncmpAudioAvtpVersionCB; + pMapCB->map_max_data_size_cb = openavbMapUncmpAudioMaxDataSizeCB; + pMapCB->map_transmit_interval_cb = openavbMapUncmpAudioTransmitIntervalCB; + pMapCB->map_gen_init_cb = openavbMapUncmpAudioGenInitCB; + pMapCB->map_avdecc_init_cb = openavbMapUncmpAudioAVDECCInitCB; + pMapCB->map_tx_init_cb = openavbMapUncmpAudioTxInitCB; + pMapCB->map_tx_cb = openavbMapUncmpAudioTxCB; + pMapCB->map_rx_init_cb = openavbMapUncmpAudioRxInitCB; + pMapCB->map_rx_cb = openavbMapUncmpAudioRxCB; + pMapCB->map_end_cb = openavbMapUncmpAudioEndCB; + pMapCB->map_gen_end_cb = openavbMapUncmpAudioGenEndCB; + + pPvtData->itemCount = 20; + pPvtData->txInterval = 0; + pPvtData->packingFactor = 1; + pPvtData->maxTransitUsec = inMaxTransitUsec; + pPvtData->DBC = 0; + pPvtData->audioMcr = AVB_MCR_NONE; + + pPubMapInfo->sparseMode = TS_SPARSE_MODE_UNSPEC; + + openavbMediaQSetMaxLatency(pMediaQ, inMaxTransitUsec); + } + + AVB_TRACE_EXIT(AVB_TRACE_MAP); + return TRUE; +} diff --git a/lib/avtp_pipeline/map_uncmp_audio/openavb_map_uncmp_audio_pub.h b/lib/avtp_pipeline/map_uncmp_audio/openavb_map_uncmp_audio_pub.h new file mode 100755 index 00000000..56bc1dd2 --- /dev/null +++ b/lib/avtp_pipeline/map_uncmp_audio/openavb_map_uncmp_audio_pub.h @@ -0,0 +1,132 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Uncompressed Audio mapping module public interface +* +* Refer to IEC 61883-6 for details of the "source packet" structure. +* +* The protocol_specific_header and CIP header. +* +* map_nv_tx_rate must be set in the .ini file. +*/ + +#ifndef OPENAVB_MAP_UNCMP_AUDIO_PUB_H +#define OPENAVB_MAP_UNCMP_AUDIO_PUB_H 1 + +#include "openavb_types_pub.h" +#include "openavb_audio_pub.h" +#include "openavb_intf_pub.h" + +/** \file + * Uncompressed Audio mapping module public interface. + * + * Refer to IEC 61883-6 for details of the "source packet" structure. + * The protocol_specific_header and CIP header. + * map_nv_tx_rate must be set in the .ini file. + */ + +/** \note A define is used for the MediaQDataFormat identifier because it is + * needed in separate execution units (static / dynamic libraries) that is why + * a single static (static/extern pattern) definition can not be used. + */ +#define MapUncmpAudioMediaQDataFormat "UncmpAudio" + +/** Defines AAF timestamping mode: + * - TS_SPARSE_MODE_DISABLED - timestamp is valid in every avtp packet + * - TS_SPARSE_MODE_ENABLED - timestamp is valid in every 8th avtp packet + */ +typedef enum { + /// Unspecified + TS_SPARSE_MODE_UNSPEC = 0, + /// Disabled + TS_SPARSE_MODE_DISABLED = 1, + /// Enabled + TS_SPARSE_MODE_ENABLED = 8 +} avb_audio_sparse_mode_t; + +/** Contains detailed information of the audio format. + * \note Interface module has to set during the RX and TX init callbacks: + * - audioRate, + * - audioType, + * - audioBitDepth, + * - audioEndian, + * - audioChannels, + * - sparseMode. + * \note The rest of fields mapping module will set these during the RX and TX + * init callbacks. The interface module can use these during the RX and TX + * callbacks. + */ +typedef struct { + /// Rate of audio + avb_audio_rate_t audioRate; + /// Sample data type + avb_audio_type_t audioType; + /// Bit depth of audio + avb_audio_bit_depth_t audioBitDepth; + /// Sample endianess + avb_audio_endian_t audioEndian; + /// Number of channels + avb_audio_channels_t audioChannels; + /// Sparse timestamping mode + avb_audio_sparse_mode_t sparseMode; + + // The mapping module will set these during the RX and TX init callbacks + // The interface module can use these during the RX and TX callbacks. + /// Number of frames for one data packet + U32 framesPerPacket; + /// Size of one sample in bytes + U32 packetSampleSizeBytes; + /// Size of one frame in bytes (framesPerPacket * audioChannels) + U32 packetFrameSizeBytes; + /// Size of packet (packetFrameSizeBytes * framesPerPacket) + U32 packetAudioDataSizeBytes; + /// Number of frames of audio to accept in one Media Queue Item + U32 packingFactor; + /// Number of frames per one Media Queue Item + U32 framesPerItem; + /// Item sample size in bytes (usually the same as packetSampleSizeBytes) + U32 itemSampleSizeBytes; + /// Media Queue Item Frame size in bytes + U32 itemFrameSizeBytes; + /// Media Queue Item size + U32 itemSize; + /// synchronization time interval + U32 sytInterval; + + /// CB for interface modules to do translations in place before data is moved into the mediaQ on rx. + openavb_intf_rx_translate_cb_t intf_rx_translate_cb; + + /// Interface Module may set this presentation latency which listener mapping modules will use to adjust the presetnation time + S32 presentationLatencyUSec; + +} media_q_pub_map_uncmp_audio_info_t; + +#endif // OPENAVB_MAP_UNCMP_AUDIO_PUB_H diff --git a/lib/avtp_pipeline/map_uncmp_audio/uncmp_audio_map.md b/lib/avtp_pipeline/map_uncmp_audio/uncmp_audio_map.md new file mode 100644 index 00000000..f826249c --- /dev/null +++ b/lib/avtp_pipeline/map_uncmp_audio/uncmp_audio_map.md @@ -0,0 +1,57 @@ +Uncompressed audio Mapping {#uncmp_audio_map} +========================== + +# Description + +Uncompressed audio mapping module conforming to AVB 61883-6 encapsulation. + +# Mapping module configuration parameters + +Name | Description +--------------------|--------------------------- +map_nv_item_count |The number of media queue elements to hold. +map_nv_tx_rate or map_nv_tx_interval | Transmit interval in frames per second. \ + 0 = default for talker class. <br> The transmit rate for \ + the mapping module should be set according to the sample \ + rate, so that the transmit interval meets the suggested \ + values in 1722a <ul><li>sample rate which are multiple of \ + 8000Hz <ul><li>8000 for class <b>A</b></li><li>4000 for \ + class <b>B</b></li></ul></li><li>sample rate which are \ + multiple of 44100Hz<ul><li>7350 for class <b>A</b></li> \ + <li>3675 for class <b>B</b></li></ul></li></ul> +map_nv_packing_factor|How many frames of audio to accept in one media queue item +map_nv_audio_mcr |Media clock recovery,<ul><li>0 - No Media Clock Recovery \ + default option</li><li>1 - MCR done using AVTP timestamps\ + </li><li>2 - MCR using Clock Reference Stream</li></ul> + +# Notes + +There are additional parameters that have to be set by intf module during +configuration process to make everything calulated properly inside mapping. +Those variables have to be set before *map_gen_init_cb* is being called. +One of the approaches might be setting them during reading of the ini +configuration and setting of the interface parameters. + +Fields of a structure media_q_pub_map_uncmp_audio_info_t that have to be set +during interface configuration: +Name | Description +-------------------|---------------------------- +audioRate |Rate of the audio @ref avb_audio_rate_t +audioType |How the data is organized - what is the data type of \ + samples @ref avb_audio_type_t +audioBitDepth |What is the bit depth of audio @ref avb_audio_bit_depth_t +audioChannels |How many channels there are @ref avb_audio_channels_t +sparseMode |Timestamping mode @ref avb_audio_sparse_mode_t \ + (not used in this mapping module) + +Below you can find description of how to set up those variables in interfaces +* [wav file interface](@ref wav_file_intf) +* [alsa interface](@ref alsa_intf) + +**Note**: If one of those fields will not be set, mapping module will not +configure media queue correctly. + +**Note**: Both the talker and listner must be configured with matching sample +parameters. If the received data does not match the configured +parameters on the listener, the stream will still be setup and data +will still flow - but no audio will be played. diff --git a/lib/avtp_pipeline/mcr/CMakeLists.txt b/lib/avtp_pipeline/mcr/CMakeLists.txt new file mode 100644 index 00000000..3e3b4110 --- /dev/null +++ b/lib/avtp_pipeline/mcr/CMakeLists.txt @@ -0,0 +1 @@ +# No common code diff --git a/lib/avtp_pipeline/mcr/openavb_mcr_hal_pub.h b/lib/avtp_pipeline/mcr/openavb_mcr_hal_pub.h new file mode 100644 index 00000000..e163a308 --- /dev/null +++ b/lib/avtp_pipeline/mcr/openavb_mcr_hal_pub.h @@ -0,0 +1,65 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+/*
+* MODULE : Public interface for media clock recovery
+*/
+
+#ifndef OPENAVB_MCR_HAL_PUB_H
+#define OPENAVB_MCR_HAL_PUB_H
+
+#include "openavb_platform_pub.h"
+#include "openavb_types_base_pub.h"
+
+#define HAL_INIT_MCR_V2(packetRate, pushInterval, timestampInterval, recoveryInterval) halInitMCR(packetRate, pushInterval, timestampInterval, recoveryInterval)
+#define HAL_CLOSE_MCR_V2() halCloseMCR()
+#define HAL_PUSH_MCR_V2() halPushMCR()
+
+// Initialize HAL MCR
+bool halInitMCR(U32 packetRate, U32 pushInterval, U32 timeStampInterval, U32 recoveryInterval);
+
+// Close HAL MCR
+bool halCloseMCR(void);
+
+// Push MCR Event
+bool halPushMCR(void);
+
+// MCR timer adjustment. Negative value speed up the media clock. Positive values slow the media clock.
+// Will take effect during the next clock recovery interval. This is completely indepentant from pure MCR and
+// allows for adjustments based on media buffer levels. The value past in works as credit with each
+// MCR timer cycle bump up and down the clock temporarily.
+void halAdjustMCRNSec(S32 adjNSec);
+
+// The granularity is used to set coarseness of the values that will be passed into halAdjustMCRNSec.
+// This is used to balance if the timestamps or values from halAdjustMCRNSec are used to adjust the clock.
+void halAdjustMCRGranularityNSec(U32 adjGranularityNSec);
+
+
+#endif // OPENAVB_MCR_HAL_PUB_H
diff --git a/lib/avtp_pipeline/mediaq/CMakeLists.txt b/lib/avtp_pipeline/mediaq/CMakeLists.txt new file mode 100644 index 00000000..7bac6aa7 --- /dev/null +++ b/lib/avtp_pipeline/mediaq/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/mediaq/openavb_mediaq.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/mediaq/openavb_mediaq.c b/lib/avtp_pipeline/mediaq/openavb_mediaq.c new file mode 100644 index 00000000..2dc419e8 --- /dev/null +++ b/lib/avtp_pipeline/mediaq/openavb_mediaq.c @@ -0,0 +1,1073 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Media Queue for data transfer betting mapping modules and interface modules. +*/ + +#include "openavb_platform.h" + +#include <stdlib.h> +#include "openavb_types_pub.h" +#include "openavb_trace.h" +#include "openavb_mediaq.h" +#include "openavb_avtp_time_pub.h" + +#include "openavb_printbuf.h" + +#define AVB_LOG_COMPONENT "Media Queue" +#include "openavb_log.h" + +static MUTEX_HANDLE(gMediaQMutex); +#define MEDIAQ_LOCK() { MUTEX_CREATE_ERR(); MUTEX_LOCK(gMediaQMutex); MUTEX_LOG_ERR("Mutex Lock failure"); } +#define MEDIAQ_UNLOCK() { MUTEX_CREATE_ERR(); MUTEX_UNLOCK(gMediaQMutex); MUTEX_LOG_ERR("Mutex Unlock failure"); } + +// CORE_TODO : Currently this first future logic for purge the media queue and incoming items +// may be too aggressive and can result in never catching up. Therefore it is disabled for now +//#define ENABLE_FIRST_FUTURE 1 + +//#define DUMP_HEAD_PUSH 1 +//#define DUMP_TAIL_PULL 1 + +#if DUMP_HEAD_PUSH +FILE *pFileHeadPush = 0; +#endif +#if DUMP_TAIL_PULL +FILE *pFileTailPull = 0; +#endif + +typedef struct { + // Maximum number of items the queue can hold. + int itemCount; + + // The size in bytes of each item + int itemSize; + + // Pointer to the array of items. + media_q_item_t *pItems; + + // Next item to be filled + int head; + + // True if the head item is locked. + bool headLocked; + + // Next item to be pulled + int tail; + + // True is next item to be pulled is locked + bool tailLocked; + + // set if timestamp is ever in the future + bool firstFuture; + + // Maximum latency + U32 maxLatencyUsec; + + // Determines if mutuxes will be used for Head and Tail access. + bool threadSafeOn; + + // Maximum stale tail + U32 maxStaleTailUsec; + +} media_q_info_t; + +static void x_openavbMediaQIncrementHead(media_q_info_t *pMediaQInfo) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + + // Module internal function therefore not validating pMediaQInfo + + int startingHead = pMediaQInfo->head; + while (++pMediaQInfo->head != startingHead) + { + if (pMediaQInfo->head >= pMediaQInfo->itemCount) { + pMediaQInfo->head = 0; + } + + // If head catches up with tail deactivate the head. + if (pMediaQInfo->head == pMediaQInfo->tail) { + break; // Set head to pMediaQInfo->head = -1; + } + + if (!pMediaQInfo->pItems[pMediaQInfo->head].taken) { + // Found item + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return; + } + } + + // Deactivate the head + pMediaQInfo->head = -1; + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); +} + +static void x_openavbMediaQIncrementTail(media_q_info_t *pMediaQInfo) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + + // Module internal function therefore not validating pMediaQInfo + + int startingTail = pMediaQInfo->tail; + while (++pMediaQInfo->tail != startingTail) + { + if (pMediaQInfo->tail >= pMediaQInfo->itemCount) { + pMediaQInfo->tail = 0; + } + + // If tail catches up with head deactivate the tail. + if (pMediaQInfo->tail == pMediaQInfo->head) { + break; // Set head to pMediaQInfo->tail = -1; + } + + if (!pMediaQInfo->pItems[pMediaQInfo->tail].taken) { + // Found item + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return; + } + } + + // Deactivate the tail + pMediaQInfo->tail = -1; + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); +} + + +// CORE_TODO: May need to add mutex protection when merging with OSAL/HAL branch. +void x_openavbMediaQPurgeStaleTail(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + +#if 0 // debug + // Debug code to report delta from TS + static bool init = TRUE; + static U32 cnt = 0; + static openavb_printbuf_t printBuf; + if (init) { + printBuf = openavbPrintbufNew(2000, 20); + init = FALSE; + } +#endif + + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + + if (pMediaQInfo->maxStaleTailUsec > 0) { + bool bFirst = TRUE; + bool bMore = TRUE; + while (bMore) { + bMore = FALSE; + if (pMediaQInfo->itemCount > 0) { + if (pMediaQInfo->tail > -1) { + media_q_item_t *pTail = &pMediaQInfo->pItems[pMediaQInfo->tail]; + + if (pTail) { + pMediaQInfo->tailLocked = TRUE; + bool bPurge = FALSE; + +#ifdef ENABLE_FIRST_FUTURE + if (bFirst) { + S32 delta = openavbAvtpTimeUsecDelta(pTail->pAvtpTime); + S32 maxStale = (S32)(0 - pMediaQInfo->maxStaleTailUsec); + + if (delta < maxStale) { + IF_LOG_INTERVAL(100) AVB_LOGF_INFO("Purging stale MediaQ items: delta:%dus maxStale%dus", delta, maxStale); + + bPurge = TRUE; + } + bFirst = FALSE; + } + else { + // Once we have triggered a stale tail purge everything past presentation time. + if (openavbAvtpTimeIsPast(pTail->pAvtpTime)) { + bPurge = TRUE; + } + } + + if (bPurge) { + openavbMediaQTailPull(pMediaQ); + pTail = NULL; + bMore = TRUE; + } + else { + pMediaQInfo->tailLocked = FALSE; + pTail = NULL; + } +#else // ENABLE_FIRST_FUTURE + if (bFirst) { + S32 delta = openavbAvtpTimeUsecDelta(pTail->pAvtpTime); + S32 maxStale = (S32)(0 - pMediaQInfo->maxStaleTailUsec); + if(delta >= 0) { + pMediaQInfo->firstFuture = TRUE; + } + else if (delta < maxStale) { + bPurge = TRUE; + pMediaQInfo->firstFuture = FALSE; + } + + bFirst = FALSE; + } + else { + // Once we have triggered a stale tail purge everything past presentation time. + if (openavbAvtpTimeIsPast(pTail->pAvtpTime)) { +#if 0 // debug + if (!(++cnt % 1)) { + openavbPrintbufPrintf(printBuf, "Purge More\n"); + } +#endif + bPurge = TRUE; + } + else { + pMediaQInfo->firstFuture = TRUE; + } + } + + if (bPurge || (pMediaQInfo->firstFuture == FALSE)) { + openavbMediaQTailPull(pMediaQ); + pTail = NULL; + bMore = TRUE; + } + else { + pMediaQInfo->tailLocked = FALSE; + pTail = NULL; + } +#endif // ENABLE_FIRST_FUTURE + + } + } + } + } + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); +} + + +media_q_t* openavbMediaQCreate() +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ); + + media_q_t *pMediaQ = calloc(1, sizeof(media_q_t)); + + if (pMediaQ) { + pMediaQ->pPvtMediaQInfo = calloc(1, sizeof(media_q_info_t)); + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + pMediaQInfo->itemCount = 0; + pMediaQInfo->itemSize = 0; + pMediaQInfo->head = 0; + pMediaQInfo->headLocked = FALSE; + pMediaQInfo->tail = -1; + pMediaQInfo->tailLocked = FALSE; + pMediaQInfo->firstFuture = TRUE; + pMediaQInfo->maxLatencyUsec = 0; + pMediaQInfo->threadSafeOn = FALSE; + pMediaQInfo->maxStaleTailUsec = MICROSECONDS_PER_SECOND; + } + else { + openavbMediaQDelete(pMediaQ); + pMediaQ = NULL; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return pMediaQ; +} + +void openavbMediaQThreadSafeOn(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ); + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + pMediaQInfo->threadSafeOn = TRUE; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); +} + + + +bool openavbMediaQSetSize(media_q_t *pMediaQ, int itemCount, int itemSize) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ); + + if (pMediaQ) { + if (itemCount < 1 || itemSize < 1) { + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; + } + + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + + // Don't want to re-allocate new memory each time + if (!pMediaQInfo->pItems) + { + pMediaQInfo->pItems = calloc(itemCount, sizeof(media_q_item_t)); + if (pMediaQInfo->pItems) { + pMediaQInfo->itemCount = itemCount; + pMediaQInfo->itemSize = itemSize; + + int i1; + for (i1 = 0; i1 < itemCount; i1++) { + pMediaQInfo->pItems[i1].pAvtpTime = openavbAvtpTimeCreate(pMediaQInfo->maxLatencyUsec); + pMediaQInfo->pItems[i1].pPubData = calloc(1, itemSize); + pMediaQInfo->pItems[i1].dataLen = 0; + pMediaQInfo->pItems[i1].itemSize = itemSize; + if (!pMediaQInfo->pItems[i1].pPubData) { + AVB_LOG_ERROR("Out of memory creating MediaQ item"); + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; + } + } + } + else { + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; + } + } else if (itemCount != pMediaQInfo->itemCount || itemSize != pMediaQInfo->itemSize){ + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; + } + } + else { + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; + } + } + else { + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return TRUE; +} + +bool openavbMediaQAllocItemMapData(media_q_t *pMediaQ, int itemPubMapSize, int itemPvtMapSize) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ); + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->pItems) { + int i1; + for (i1 = 0; i1 < pMediaQInfo->itemCount; i1++) { + if (itemPubMapSize) { + if (!pMediaQInfo->pItems[i1].pPubMapData) { + pMediaQInfo->pItems[i1].pPubMapData = calloc(1, itemPubMapSize); + if (!pMediaQInfo->pItems[i1].pPubMapData) { + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; + } + } + else { + AVB_LOG_ERROR("Attemping to reallocate public map data"); + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; + } + } + if (itemPvtMapSize) { + if (!pMediaQInfo->pItems[i1].pPvtMapData) { + pMediaQInfo->pItems[i1].pPvtMapData = calloc(1, itemPvtMapSize); + if (!pMediaQInfo->pItems[i1].pPvtMapData) { + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; + } + } + else { + AVB_LOG_ERROR("Attemping to reallocate private map data"); + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return TRUE; + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; +} + + +bool openavbMediaQAllocItemIntfData(media_q_t *pMediaQ, int itemIntfSize) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ); + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->pItems) { + int i1; + for (i1 = 0; i1 < pMediaQInfo->itemCount; i1++) { + if (!pMediaQInfo->pItems[i1].pPvtIntfData) { + pMediaQInfo->pItems[i1].pPvtIntfData = calloc(1, itemIntfSize); + if (!pMediaQInfo->pItems[i1].pPvtIntfData) { + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; + } + } + else { + AVB_LOG_ERROR("Attemping to reallocate private interface data"); + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; + } + } + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return TRUE; + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; +} + +bool openavbMediaQDelete(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ); + + if (pMediaQ) { + +#if DUMP_HEAD_PUSH + if (pFileHeadPush) { + fflush(pFileHeadPush); + fclose(pFileHeadPush); + pFileHeadPush = NULL; + } +#endif + +#if DUMP_TAIL_PULL + if (pFileTailPull) { + fflush(pFileTailPull); + fclose(pFileTailPull); + pFileTailPull = NULL; + } +#endif + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->pItems) { + int i1; + for (i1 = 0; i1 < pMediaQInfo->itemCount; i1++) { + + if (pMediaQInfo->pItems[i1].taken) { + AVB_LOG_ERROR("Deleting MediaQ with an item TAKEN. The item will be orphaned."); + } + else { + openavbAvtpTimeDelete(pMediaQInfo->pItems[i1].pAvtpTime); + if (pMediaQInfo->pItems[i1].pPubData) { + free(pMediaQInfo->pItems[i1].pPubData); + pMediaQInfo->pItems[i1].pPubData = NULL; + } + if (pMediaQInfo->pItems[i1].pPubMapData) { + free(pMediaQInfo->pItems[i1].pPubMapData); + pMediaQInfo->pItems[i1].pPubMapData = NULL; + } + if (pMediaQInfo->pItems[i1].pPvtMapData) { + free(pMediaQInfo->pItems[i1].pPvtMapData); + pMediaQInfo->pItems[i1].pPvtMapData = NULL; + } + if (pMediaQInfo->pItems[i1].pPvtIntfData) { + free(pMediaQInfo->pItems[i1].pPvtIntfData); + pMediaQInfo->pItems[i1].pPvtIntfData = NULL; + } + } + } + free(pMediaQInfo->pItems); + pMediaQInfo->pItems = NULL; + } + free(pMediaQ->pPvtMediaQInfo); + pMediaQ->pPvtMediaQInfo = NULL; + + if (pMediaQ->pPubMapInfo) { + free(pMediaQ->pPubMapInfo); + pMediaQ->pPubMapInfo = NULL; + } + + if (pMediaQ->pPvtMapInfo) { + free(pMediaQ->pPvtMapInfo); + pMediaQ->pPvtMapInfo = NULL; + } + + if (pMediaQ->pPvtIntfInfo) { + free(pMediaQ->pPvtIntfInfo); + pMediaQ->pPvtIntfInfo = NULL; + } + } + + if (pMediaQ->pMediaQDataFormat) { + free(pMediaQ->pMediaQDataFormat); + pMediaQ->pMediaQDataFormat = NULL; + } + + free(pMediaQ); + pMediaQ = NULL; + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); + return FALSE; +} + +void openavbMediaQSetMaxLatency(media_q_t *pMediaQ, U32 maxLatencyUsec) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ); + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + pMediaQInfo->maxLatencyUsec = maxLatencyUsec; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); +} + +void openavbMediaQSetMaxStaleTail(media_q_t *pMediaQ, U32 maxStaleTailUsec) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ); + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + pMediaQInfo->maxStaleTailUsec = maxStaleTailUsec; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ); +} + +media_q_item_t *openavbMediaQHeadLock(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->threadSafeOn) { + MEDIAQ_LOCK(); + } + if (pMediaQInfo->itemCount > 0) { + if (pMediaQInfo->head > -1) { + pMediaQInfo->headLocked = TRUE; + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + // Mutex (LOCK()) if acquired stays locked + return &pMediaQInfo->pItems[pMediaQInfo->head]; + } + } + if (pMediaQInfo->threadSafeOn) { + MEDIAQ_UNLOCK(); + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return NULL; +} + +void openavbMediaQHeadUnlock(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->itemCount > 0) { + if (pMediaQInfo->head > -1) { + pMediaQInfo->headLocked = FALSE; + if (pMediaQInfo->threadSafeOn) { + MEDIAQ_UNLOCK(); + } + } + } + } + } + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); +} + +bool openavbMediaQHeadPush(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->itemCount > 0) { + if (pMediaQInfo->head > -1) { + media_q_item_t *pHead = &pMediaQInfo->pItems[pMediaQInfo->head]; + +#if DUMP_HEAD_PUSH + media_q_item_t *pMediaQItem = &pMediaQInfo->pItems[pMediaQInfo->head]; + if (!pFileHeadPush) { + char filename[128]; + sprintf(filename, "headpush_%5.5d.dat", GET_PID()); + pFileHeadPush = fopen(filename, "wb"); + } + if (pFileHeadPush) { + size_t result = fwrite(pMediaQItem->pPubData, 1, pMediaQItem->dataLen, pFileHeadPush); + if (result != pMediaQItem->dataLen) + printf("ERROR writing head push log"); + } +#endif + + // If tail not set, set it now + if (pMediaQInfo->tail == -1) { + pMediaQInfo->tail = pMediaQInfo->head; + } + + pHead->readIdx = 0; // Reset read index + + x_openavbMediaQIncrementHead(pMediaQInfo); + + pMediaQInfo->headLocked = FALSE; + if (pMediaQInfo->threadSafeOn) { + MEDIAQ_UNLOCK(); + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return TRUE; + } + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return FALSE; +} + +media_q_item_t* openavbMediaQTailLock(media_q_t *pMediaQ, bool ignoreTimestamp) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + + if (!ignoreTimestamp) { + x_openavbMediaQPurgeStaleTail(pMediaQ); + } + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->threadSafeOn) { + MEDIAQ_LOCK(); + } + if (pMediaQInfo->itemCount > 0) { + if (pMediaQInfo->tail > -1) { + media_q_item_t *pTail = &pMediaQInfo->pItems[pMediaQInfo->tail]; + + // Check if tail item is ready. + if (!ignoreTimestamp) { + if (!openavbAvtpTimeIsPast(pTail->pAvtpTime)) { + if (pMediaQInfo->threadSafeOn) { + MEDIAQ_UNLOCK(); + } + return NULL; + } + } + + pMediaQInfo->tailLocked = TRUE; + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + // Mutex (LOCK()) if acquired stays locked + return pTail; + } + } + if (pMediaQInfo->threadSafeOn) { + MEDIAQ_UNLOCK(); + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return NULL; +} + +void openavbMediaQTailUnlock(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->itemCount > 0) { + if (pMediaQInfo->tail > -1) { + pMediaQInfo->tailLocked = FALSE; + if (pMediaQInfo->threadSafeOn) { + MEDIAQ_UNLOCK(); + } + } + } + } + } + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); +} + +bool openavbMediaQTailPull(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->itemCount > 0) { + if (pMediaQInfo->tail > -1) { + media_q_item_t *pTail = &pMediaQInfo->pItems[pMediaQInfo->tail]; + +#if DUMP_TAIL_PULL + media_q_item_t *pMediaQItem = &pMediaQInfo->pItems[pMediaQInfo->tail]; + if (!pFileTailPull) { + char filename[128]; + sprintf(filename, "tailpull_%5.5d.dat", GET_PID()); + pFileTailPull = fopen(filename, "wb"); + } + if (pFileTailPull) { + size_t result = fwrite(pMediaQItem->pPubData, 1, pMediaQItem->dataLen, pFileTailPull); + if (result != pMediaQItem->dataLen) + printf("ERROR writing tail pull log"); + } +#endif + + // If head not set, set it now + if (pMediaQInfo->head == -1) { + pMediaQInfo->head = pMediaQInfo->tail; + } + + pTail->readIdx = 0; // Reset read index + pTail->dataLen = 0; // Clears out the data + + x_openavbMediaQIncrementTail(pMediaQInfo); + + pMediaQInfo->tailLocked = FALSE; + if (pMediaQInfo->threadSafeOn) { + MEDIAQ_UNLOCK(); + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return TRUE; + } + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return FALSE; +} + +bool openavbMediaQTailItemTake(media_q_t *pMediaQ, media_q_item_t* pItem) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + + if (pMediaQ && pItem) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->itemCount > 0) { + if (pMediaQInfo->tail > -1) { + + x_openavbMediaQIncrementTail(pMediaQInfo); + + pItem->taken = TRUE; + pMediaQInfo->tailLocked = FALSE; + if (pMediaQInfo->threadSafeOn) { + MEDIAQ_UNLOCK(); + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return TRUE; + } + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return FALSE; +} + +bool openavbMediaQTailItemGive(media_q_t *pMediaQ, media_q_item_t* pItem) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + + if (pItem) { + pItem->taken = FALSE; + pItem->readIdx = 0; // Reset read index + pItem->dataLen = 0; // Clears out the data + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->threadSafeOn) { + MEDIAQ_LOCK(); + } + if (pMediaQInfo->itemCount > 0) { + if (pMediaQInfo->head == -1) { + // Transition from full mediaq to an available item slot. Find this item that was just give back + int i1; + for (i1 = 0; i1 < pMediaQInfo->itemCount; i1++) + { + if (!pMediaQInfo->pItems[i1].taken) { + pMediaQInfo->head = i1; + break; + } + } + } + } + if (pMediaQInfo->threadSafeOn) { + MEDIAQ_UNLOCK(); + } + } + } + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return TRUE; + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return FALSE; +} + + +bool openavbMediaQUsecTillTail(media_q_t *pMediaQ, U32 *pUsecTill) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + + if (pMediaQ && pUsecTill) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->itemCount > 0) { + if (pMediaQInfo->tail > -1) { + media_q_item_t *pTail = &pMediaQInfo->pItems[pMediaQInfo->tail]; + + U32 usecTill; + + if (openavbAvtpTimeUsecTill(pTail->pAvtpTime, &usecTill)) { + *pUsecTill = usecTill; + return TRUE; + } + } + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return FALSE; +} + +bool openavbMediaQIsAvailableBytes(media_q_t *pMediaQ, U32 bytes, bool ignoreTimestamp) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + + if (!ignoreTimestamp) { + x_openavbMediaQPurgeStaleTail(pMediaQ); + } + + int byteCnt = 0; + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->itemCount > 0) { + if (pMediaQInfo->tail > -1) { + // Check if tail item is ready. + int tailIdx = pMediaQInfo->tail; + int endIdx = pMediaQInfo->head > -1 ? pMediaQInfo->head : pMediaQInfo->tail; + if (ignoreTimestamp) { + while (1) { + media_q_item_t *pTail = &pMediaQInfo->pItems[tailIdx]; + + if (!pTail->taken) { + byteCnt += pTail->dataLen - pTail->readIdx; + + if (byteCnt >= bytes) { + // Met the available byte count + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return TRUE; + } + } + + tailIdx++; + if (tailIdx >= pMediaQInfo->itemCount) + tailIdx = 0; + if (tailIdx == endIdx) + break; + } + } + else { + U64 nSecTime; + CLOCK_GETTIME64(OPENAVB_CLOCK_WALLTIME, &nSecTime); + while (1) { + media_q_item_t *pTail = &pMediaQInfo->pItems[tailIdx]; + assert(pTail); + + if (!pTail->taken) { + if (!openavbAvtpTimeIsPastTime(pTail->pAvtpTime, nSecTime)) + break; + + byteCnt += pTail->dataLen - pTail->readIdx; + + if (byteCnt >= bytes) { + // Met the available byte count + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return TRUE; + } + } + + tailIdx++; + if (tailIdx >= pMediaQInfo->itemCount) + tailIdx = 0; + if (tailIdx == endIdx) + break; + } + } + } + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return FALSE; +} + +U32 openavbMediaQCountItems(media_q_t *pMediaQ, bool ignoreTimestamp) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + + if (!ignoreTimestamp) { + x_openavbMediaQPurgeStaleTail(pMediaQ); + } + + U32 itemCnt = 0; + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->itemCount > 0) { + if (pMediaQInfo->tail > -1) { + // Check if tail item is ready. + int tailIdx = pMediaQInfo->tail; + if (ignoreTimestamp) { + while (1) { + media_q_item_t *pTail = &pMediaQInfo->pItems[tailIdx]; + + if (!pTail->taken) { + itemCnt++; + } + + tailIdx++; + if (tailIdx >= pMediaQInfo->itemCount) + tailIdx = 0; + if (tailIdx == pMediaQInfo->head) + break; + if (itemCnt >= pMediaQInfo->itemCount) + break; + } + } + else { + U64 nSecTime; + CLOCK_GETTIME64(OPENAVB_CLOCK_WALLTIME, &nSecTime); + while (1) { + media_q_item_t *pTail = &pMediaQInfo->pItems[tailIdx]; + + if (!pTail->taken) { + if (!openavbAvtpTimeIsPastTime(pTail->pAvtpTime, nSecTime)) + break; + + itemCnt++; + } + + tailIdx++; + if (tailIdx >= pMediaQInfo->itemCount) + tailIdx = 0; + if (tailIdx == pMediaQInfo->head) + break; + if (itemCnt >= pMediaQInfo->itemCount) + break; + } + } + } + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return itemCnt; +} + +bool openavbMediaQAnyReadyItems(media_q_t *pMediaQ, bool ignoreTimestamp) +{ + AVB_TRACE_ENTRY(AVB_TRACE_MEDIAQ_DETAIL); + + if (!ignoreTimestamp) { + x_openavbMediaQPurgeStaleTail(pMediaQ); + } + + if (pMediaQ) { + if (pMediaQ->pPvtMediaQInfo) { + media_q_info_t *pMediaQInfo = (media_q_info_t *)(pMediaQ->pPvtMediaQInfo); + if (pMediaQInfo->itemCount > 0) { + if (pMediaQInfo->tail > -1) { + // Check if tail item is ready. + int tailIdx = pMediaQInfo->tail; + if (ignoreTimestamp) { + media_q_item_t *pTail = &pMediaQInfo->pItems[tailIdx]; + + if (!pTail->taken) { + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return TRUE; + } + } + else { + U64 nSecTime; + CLOCK_GETTIME64(OPENAVB_CLOCK_WALLTIME, &nSecTime); + media_q_item_t *pTail = &pMediaQInfo->pItems[tailIdx]; + + if (!pTail->taken) { + if (openavbAvtpTimeIsPastTime(pTail->pAvtpTime, nSecTime)) { + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return TRUE; + } + } + } + } + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_MEDIAQ_DETAIL); + return FALSE; +} + diff --git a/lib/avtp_pipeline/mediaq/openavb_mediaq.h b/lib/avtp_pipeline/mediaq/openavb_mediaq.h new file mode 100644 index 00000000..b620a610 --- /dev/null +++ b/lib/avtp_pipeline/mediaq/openavb_mediaq.h @@ -0,0 +1,60 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Circular queue for passing data between interfaces +* and mappers. +*/ + +#ifndef OPENAVB_MEDIA_Q_H +#define OPENAVB_MEDIA_Q_H 1 + +#include "openavb_mediaq_pub.h" + +// These are Public APIs. Details in openavb_mediaq_pub.h +// However the declarations are included here for easy internal use. +media_q_t* openavbMediaQCreate(); +void openavbMediaQThreadSafeOn(media_q_t *pMediaQ); +bool openavbMediaQSetSize(media_q_t *pMediaQ, int itemCount, int itemSize); +bool openavbMediaQAllocItemMapData(media_q_t *pMediaQ, int itemPubMapSize, int itemPvtMapSize); +bool openavbMediaQAllocItemIntfData(media_q_t *pMediaQ, int itemIntfSize); +bool openavbMediaQDelete(media_q_t *pMediaQ); +void openavbMediaQSetMaxLatency(media_q_t *pMediaQ, U32 maxLatencyUsec); +void openavbMediaQSetMaxStaleTail(media_q_t *pMediaQ, U32 maxStaleTailUsec); +media_q_item_t *openavbMediaQHeadLock(media_q_t *pMediaQ); +void openavbMediaQHeadUnlock(media_q_t *pMediaQ); +bool openavbMediaQHeadPush(media_q_t *pMediaQ); +media_q_item_t* openavbMediaQTailLock(media_q_t *pMediaQ, bool ignoreTimestamp); +void openavbMediaQTailUnlock(media_q_t *pMediaQ); +bool openavbMediaQTailPull(media_q_t *pMediaQ); +bool openavbMediaQUsecTillTail(media_q_t *pMediaQ, U32 *pUsecTill); +bool openavbMediaQIsAvailableBytes(media_q_t *pMediaQ, U32 bytes, bool ignoreTimestamp); + +#endif // OPENAVB_MEDIA_Q_H diff --git a/lib/avtp_pipeline/mediaq/openavb_mediaq_pub.h b/lib/avtp_pipeline/mediaq/openavb_mediaq_pub.h new file mode 100644 index 00000000..405575ce --- /dev/null +++ b/lib/avtp_pipeline/mediaq/openavb_mediaq_pub.h @@ -0,0 +1,364 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Circular queue for passing data between interfaces +* and mappers. +*/ + +#ifndef OPENAVB_MEDIA_Q_PUB_H +#define OPENAVB_MEDIA_Q_PUB_H 1 + +#include "openavb_types_pub.h" +#include "openavb_avtp_time_pub.h" + +/** \file + * Media Queue. + * Circular queue for passing data between interfaces and mappers. + */ + +/** Media Queue Item structure. + */ +typedef struct { + /// In a talker process this is the capture time. In a listener process this + /// is the presentation time (AVTP timestamp). + avtp_time_t *pAvtpTime; + + /// The structure of this memory will be defined per mapper in a public + /// header. This is the common data payload format + /// that mappers and interfaces will share. + void *pPubData; + + /// Read index. User managed. It will be reset to zero when the item is + /// pushed and pulled. + U32 readIdx; + + /// Length of data in item. + U32 dataLen; + + /// Size of the data item + U32 itemSize; + + /// Flag indicating mediaQ item has been taken by a call to openavbMediaQTailItemTake() + bool taken; + + /// Public extra map data + void *pPubMapData; + + /// For use internally by mapping modules. Often may not be used. + void *pPvtMapData; + + /// For use internally by the interface. Often may not be used. + void *pPvtIntfData; +} media_q_item_t; + +/** Media Queue structure. + * The media queue is the conduit between interface modules and mapping modules. + * It is internally implemented as a circular FIFO container. + * \see \ref working_with_mediaq + */ +typedef struct { + /////////////////////////// + // Shared properties + /////////////////////////// + + /// Common name for mapping data format. Set by mapping modules and used by + /// interface modules to validate + /// the media queue data format compatibility. + char *pMediaQDataFormat; + + /// Defined per mapper in the public header. + /// The structure of this memory area will be used only the mapper module + /// and interface module. + void *pPubMapInfo; + + /////////////////////////// + // Private properties + /// \privatesection + /////////////////////////// + + /// For use internally in the media queue + void *pPvtMediaQInfo; + + /// For use internally by the mapper + void *pPvtMapInfo; + + /// For use internally by the interface + void *pPvtIntfInfo; +} media_q_t; + +/** Create a media queue. + * + * Allocate a media queue structure. Only mapping modules will use this call. + * + * \return A pointer to a media queue structure. NULL if the creation fails + */ +media_q_t* openavbMediaQCreate(); + +/** Enable thread safe access for this media queue. + * + * In the default case a media queue is only accessed from a single thread and + * therefore multi-threaded synchronication isn't needed. In situations where a + * media queue can be accessed from multiple threads calling this function will + * enable mutex protection on the head and tail related functions. Once enabled + * for a media queue it can not be disabled. + * + * \param pMediaQ A pointer to the media_q_t structure + */ +void openavbMediaQThreadSafeOn(media_q_t *pMediaQ); + +/** Set size of media queue. + * + * Pre-allocate all the items for the media queue. Once allocated the item + * storage will be reused as items are added and removed from the queue. Only + * mapping modules will use this call. This must be called before using the + * MediaQ. + * + * \param pMediaQ A pointer to the media_q_t structure + * \param itemCount Maximum number of items that the queue will hold. These are + * pre-allocated + * \param itemSize The pre-allocated size of a media queue item + * \return TRUE on success or FALSE on failure + * + * \warning This must be called before using the MediaQ + */ +bool openavbMediaQSetSize(media_q_t *pMediaQ, int itemCount, int itemSize); + +/** Alloc item map data. + * + * Items in the media queue may also have per-item data that is managed by the + * mapping modules. This function allows mapping modules to specify this + * storage. + * Only mapping modules will use this call. This must be called before using the + * media queue. + * + * \param pMediaQ A pointer to the media_q_t structure + * \param itemPubMapSize The size of the public (shared) per-items data that + * will be allocated. Typically this is the size of a structure that is + * declared in a public header file associated with the mapping module. + * \param itemPvtMapSize The size of the private per-items data that will be + * allocated. The structure of this area will not be shared outside of + * the mapping module + * \return TRUE on success or FALSE on failure + * + * \warning This must be called before using the MediaQ + */ +bool openavbMediaQAllocItemMapData(media_q_t *pMediaQ, int itemPubMapSize, int itemPvtMapSize); + +/** Alloc item interface data. + * + * Items in the media queue may also have per-item data that is managed by the + * interface modules. This function allows interface modules to specify this + * storage. This must be called before using the media queue. + * + * \param pMediaQ A pointer to the media_q_t structure + * \param itemIntfSize The size of the per-items data to allocate for use by the + * interface module + * \return TRUE on success or FALSE on failure + * + * \warning This must be called before using the MediaQ + */ +bool openavbMediaQAllocItemIntfData(media_q_t *pMediaQ, int itemIntfSize); + +/** Destroy the queue. + * + * The media queue passed in will be deleted. This includes all allocated memory + * both for mapping modules and interface modules. Only mapping modules will use + * this call. + * + * \param pMediaQ A pointer to the media_q_t structure + * \return TRUE on success or FALSE on failure + */ +bool openavbMediaQDelete(media_q_t *pMediaQ); + +/** Sets the maximum latency expected. + * + * The maximum latency will be set. This value is used by the media queue to + * help determine if a media queue item is ready to be released to the listener + * interface module for presentation. Typically the mapping module will call + * this function with a max latency value derived from the max_latency + * configuration value. + * + * \param pMediaQ A pointer to the media_q_t structure + * \param maxLatencyUsec The maximum latency. + */ +void openavbMediaQSetMaxLatency(media_q_t *pMediaQ, U32 maxLatencyUsec); + +/** Sets the maximum stale tail. + * + * Used to purge media queue items that are too old. + * + * \param pMediaQ A pointer to the media_q_t structure + * \param maxStaleTailUsec tail element purge threshold in microseconds + */ +void openavbMediaQSetMaxStaleTail(media_q_t *pMediaQ, U32 maxStaleTailUsec); + +/** Get pointer to the head item and lock it. + * + * Get the storage location for the next item that can be added to the circle + * queue. Once the function is called the item will remained locked until + * unlocked or pushed. The lock remains valid across callbacks. An interface + * module will use this function when running as a talker to add a new media + * element to the queue thereby making it available to the mapping module. + * + * \param pMediaQ A pointer to the media_q_t structure. + * \return A pointer to a media queue item. Returns NULL if head item storage + * isn't available. + */ +media_q_item_t *openavbMediaQHeadLock(media_q_t *pMediaQ); + +/** Unlock the head item. + * + * Unlock a locked media queue item from the head of the queue. The item will + * not become available for use in the queue and the data will not be cleared. + * Subsequent calls to openavbMediaQHeadLock will return the same item storage + * with the same data values. An interface module will use this function when + * running as a talker when it must release a previously locked media queue head + * item. + * + * \param pMediaQ A pointer to the media_q_t structure. + */ +void openavbMediaQHeadUnlock(media_q_t *pMediaQ); + +/** Unlock the head item and make it available. + * + * Unlock a locked media queue item from the head of the queue and make it + * available for use in the queue to be accessed with the tail function calls. + * An interface module will use this function when running as a talker after it + * has locked the head item and added data to the item storage area. + * + * \param pMediaQ A pointer to the media_q_t structure. + * \return Returns TRUE on success or FALSE on failure. + */ +bool openavbMediaQHeadPush(media_q_t *pMediaQ); + +/** Get pointer to the tail item and lock it. + * + * Lock the next available tail item in the media queue. Available is based on + * the timestamp that is associated with the item when it was a placed into the + * media queue. The interface module running on a listener uses this function + * to access the data items place into the media queue by the mapping module. + * At some point after this function call the item must be unlocked with either + * openavbMediaQTailUnlockor openavbMediaQTailPull on the same callback or a subsequent + * callback. + * + * \param pMediaQ A pointer to the media_q_t structure. + * \param ignoreTimestamp If TRUE ignore the tail item timestamp making the tail + * item immediately available. + * \return A pointer to a media queue item. Returns NULL if a tail item isn't + * available. + */ +media_q_item_t* openavbMediaQTailLock(media_q_t *pMediaQ, bool ignoreTimestamp); + +/** Unlock the tail item without removing it from the queue. + * + * Unlock a media queue item that was previously locked with openavbMediaQTailLock. + * The item will not be removed from the tail of the media queue. + * + * \param pMediaQ A pointer to the media_q_t structure. + */ +void openavbMediaQTailUnlock(media_q_t *pMediaQ); + +/** Unlock the tail item and remove it from the queue. + * + * Unlock a media queue item that was previously locked with openavbMediaQTailLock + * and remove it from the media queue. + * + * \param pMediaQ A pointer to the media_q_t structure. + * \return Returns TRUE on success or FALSE on failure. + */ +bool openavbMediaQTailPull(media_q_t *pMediaQ); + +/** Take ownership from the MediaQ of an item. + * + * Take ownership from the MediaQ of an item previously locked + * with openavbMediaQTailLock. Will advance the tail. Used in place + * of openavbMediaQTailPull() + * + * \param pMediaQ A pointer to the media_q_t structure. + * \param MediaQ item to take ownership of. + * \return Returns TRUE on success or FALSE on failure. + */ +bool openavbMediaQTailItemTake(media_q_t *pMediaQ, media_q_item_t* pItem); + +/** Give itme ownership back to the MediaQ. + * + * Give ownership back to the MediaQ of an item previously taken + * with openavbMediaQTailItemTake() + * + * \param pMediaQ A pointer to the media_q_t structure. + * \param MediaQ item to give back tot he MediaA. + * \return Returns TRUE on success or FALSE on failure. + */ +bool openavbMediaQTailItemGive(media_q_t *pMediaQ, media_q_item_t* pItem); + +/** Get microseconds until tail is ready. + * + * \param pMediaQ A pointer to the media_q_t structure. + * \param pUsecTill An output parameter that is set with the number of + * microseconds until the tail item will be available. + * \return Return FALSE if greater than 5 seconds otherwise TRUE. + */ +bool openavbMediaQUsecTillTail(media_q_t *pMediaQ, U32 *pUsecTill); + +/** Check if the number of bytes are available. + * + * Checks were the given media queue contains bytes, returns true if it does + * false otherwise. + * + * \param pMediaQ A pointer to the media_q_t structure. + * \param bytes Number of bytes expected in media queue + * \param ignoreTimestamp Ignore timestamp for byte accumulation. + * \return TRUE if bytes are available in pMediaQ; FALSE if bytes not available + * in pMediaQ. + */ +bool openavbMediaQIsAvailableBytes(media_q_t *pMediaQ, U32 bytes, bool ignoreTimestamp); + +/** Count number of available MediaQ items. + * + * Count the number of available MediaQ items. + * + * \param pMediaQ A pointer to the media_q_t structure. + * \param ignoreTimestamp Ignore timestamp for byte accumulation. + * \return The number of available MediaA items. + */ +U32 openavbMediaQCountItems(media_q_t *pMediaQ, bool ignoreTimestamp); + +/** Check if there are any ready MediaQ items. + * + * Check if there are any ready MediaQ items. + * + * \param pMediaQ A pointer to the media_q_t structure. + * \return TRUE if there is at least 1 MediaQ item available + * otherwise FALSE. + */ +bool openavbMediaQAnyReadyItems(media_q_t *pMediaQ, bool ignoreTimestamp); + +#endif // OPENAVB_MEDIA_Q_PUB_H diff --git a/lib/avtp_pipeline/openavb_common/README.TXT b/lib/avtp_pipeline/openavb_common/README.TXT new file mode 100644 index 00000000..23fef516 --- /dev/null +++ b/lib/avtp_pipeline/openavb_common/README.TXT @@ -0,0 +1,9 @@ +The files in this folder originated in examples/common + +These were copied here because of inconsistancies in implementation of these functions in different examples. + +See the each source file for the actual license. + +TODO_OPENAVB: It should be considered if these common support functions in avb.c, +listener_mrp_client.c and talker_mrp_client.c should be moved to a true commmon source +area in OpenAVB and outside of the examples folder. diff --git a/lib/avtp_pipeline/openavb_common/avb.c b/lib/avtp_pipeline/openavb_common/avb.c new file mode 100644 index 00000000..200489b6 --- /dev/null +++ b/lib/avtp_pipeline/openavb_common/avb.c @@ -0,0 +1,552 @@ + /* + * Copyright (c) <2013>, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <errno.h> +#include <fcntl.h> +#include <math.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <arpa/inet.h> + +#include <linux/if.h> + +#include <netinet/in.h> + +#include <pci/pci.h> + +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/socket.h> +#include <sys/stat.h> + +#include "avb.h" + +int pci_connect(device_t * igb_dev) +{ + char devpath[IGB_BIND_NAMESZ]; + struct pci_access *pacc; + struct pci_dev *dev; + int err; + + memset(igb_dev, 0, sizeof(device_t)); + + pacc = pci_alloc(); + + pci_init(pacc); + + pci_scan_bus(pacc); + + for (dev = pacc->devices; dev; dev = dev->next) { + pci_fill_info(dev, + PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); + igb_dev->pci_vendor_id = dev->vendor_id; + igb_dev->pci_device_id = dev->device_id; + igb_dev->domain = dev->domain; + igb_dev->bus = dev->bus; + igb_dev->dev = dev->dev; + igb_dev->func = dev->func; + snprintf(devpath, IGB_BIND_NAMESZ, "%04x:%02x:%02x.%d", + dev->domain, dev->bus, dev->dev, dev->func); + err = igb_probe(igb_dev); + if (err) + continue; + + printf("attaching to %s\n", devpath); + err = igb_attach(devpath, igb_dev); + if (err) { + printf("attach failed! (%s)\n", strerror(errno)); + continue; + } + + err = igb_attach_tx(igb_dev); + if (err) { + printf("attach_tx failed! (%s)\n", strerror(errno)); + continue; + } + + goto out; + } + + pci_cleanup(pacc); + return ENXIO; + +out: pci_cleanup(pacc); + + return 0; +} + +int gptpinit(int *shm_fd, char **memory_offset_buffer) +{ + *shm_fd = shm_open(SHM_NAME, O_RDWR, 0); + if (*shm_fd == -1) { + perror("shm_open()"); + return false; + } + *memory_offset_buffer = + (char *)mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, + *shm_fd, 0); + if (*memory_offset_buffer == (char *)-1) { + perror("mmap()"); + *memory_offset_buffer = NULL; + shm_unlink(SHM_NAME); + return false; + } + return true; +} + +void gptpdeinit(int shm_fd, char *memory_offset_buffer) +{ + if (memory_offset_buffer != NULL) { + munmap(memory_offset_buffer, SHM_SIZE); + } + if (shm_fd != -1) { + close(shm_fd); + } +} + +int gptpscaling(gPtpTimeData * td, char *memory_offset_buffer) +{ + if (td == NULL) + return true; + + pthread_mutex_lock((pthread_mutex_t *) memory_offset_buffer); + memcpy(td, memory_offset_buffer + sizeof(pthread_mutex_t), sizeof(*td)); + pthread_mutex_unlock((pthread_mutex_t *) memory_offset_buffer); + + return true; +} + +/* setters & getters for seventeen22_header */ +void avb_set_1722_cd_indicator(seventeen22_header *h1722, uint64_t cd_indicator) +{ + h1722->cd_indicator = cd_indicator; +} + +uint64_t avb_get_1722_cd_indicator(seventeen22_header *h1722) +{ + return h1722->cd_indicator; +} + +void avb_set_1722_subtype(seventeen22_header *h1722, uint64_t subtype) +{ + h1722->subtype = subtype; +} + +uint64_t avb_get_1722_subtype(seventeen22_header *h1722) +{ + return h1722->subtype; +} + +void avb_set_1722_sid_valid(seventeen22_header *h1722, uint64_t sid_valid) +{ + h1722->sid_valid = sid_valid; +} + +uint64_t avb_get_1722_sid_valid(seventeen22_header *h1722) +{ + return h1722->sid_valid; +} + +void avb_set_1722_version(seventeen22_header *h1722, uint64_t version) +{ + h1722->version = version; +} + +uint64_t avb_get_1722_version(seventeen22_header *h1722) +{ + return h1722->version; +} + +void avb_set_1722_reset(seventeen22_header *h1722, uint64_t reset) +{ + h1722->reset = reset; +} + +uint64_t avb_get_1722_reset(seventeen22_header *h1722) +{ + return h1722->reset; +} + +void avb_set_1722_reserved0(seventeen22_header *h1722, uint64_t reserved0) +{ + h1722->reserved0 = reserved0; +} + +uint64_t avb_get_1722_reserved0(seventeen22_header *h1722) +{ + return h1722->reserved0; +} + +void avb_set_1722_gateway_valid(seventeen22_header *h1722, uint64_t gateway_valid) +{ + h1722->gateway_valid = gateway_valid; +} + +uint64_t avb_get_1722_gateway_valid(seventeen22_header *h1722) +{ + return h1722->gateway_valid; +} + +void avb_set_1722_timestamp_valid(seventeen22_header *h1722, uint64_t timestamp_valid) +{ + h1722->timestamp_valid = timestamp_valid; +} + +uint64_t avb_get_1722_timestamp_valid(seventeen22_header *h1722) +{ + return h1722->timestamp_valid; +} + +void avb_set_1722_reserved1(seventeen22_header *h1722, uint64_t reserved1) +{ + h1722->reserved1 = reserved1; +} + +uint64_t avb_get_1722_reserved1(seventeen22_header *h1722) +{ + return h1722->reserved1; +} + +void avb_set_1722_stream_id(seventeen22_header *h1722, uint64_t stream_id) +{ + h1722->stream_id = stream_id; +} + +uint64_t avb_get_1722_stream_id(seventeen22_header *h1722) +{ + return h1722->stream_id; +} + +void avb_set_1722_seq_number(seventeen22_header *h1722, uint64_t seq_number) +{ + h1722->seq_number = seq_number; +} + +uint64_t avb_get_1722_seq_number(seventeen22_header *h1722) +{ + return h1722->seq_number; +} + +void avb_set_1722_timestamp_uncertain(seventeen22_header *h1722, uint64_t timestamp_uncertain) +{ + h1722->timestamp_uncertain = timestamp_uncertain; +} + +uint64_t avb_get_1722_timestamp_uncertain(seventeen22_header *h1722) +{ + return h1722->timestamp_uncertain; +} + +void avb_set_1722_timestamp(seventeen22_header *h1722, uint64_t timestamp) +{ + h1722->timestamp = timestamp; +} + +uint64_t avb_get_1722_timestamp(seventeen22_header *h1722) +{ + return h1722->timestamp; +} + +void avb_set_1722_gateway_info(seventeen22_header *h1722, uint64_t gateway_info) +{ + h1722->gateway_info = gateway_info; +} + +uint64_t avb_get_1722_gateway_info(seventeen22_header *h1722) +{ + return h1722->gateway_info; +} + +void avb_set_1722_length(seventeen22_header *h1722, uint64_t length) +{ + h1722->length = length; +} + +uint64_t avb_get_1722_length(seventeen22_header *h1722) +{ + return h1722->length; +} + +/* setters & getters for six1883_header */ + +void avb_set_61883_packet_channel(six1883_header *h61883, uint16_t packet_channel) +{ + h61883->packet_channel = packet_channel; +} + +uint16_t avb_get_61883_length(six1883_header *h61883) +{ + return h61883->packet_channel; +} + +void avb_set_61883_format_tag(six1883_header *h61883, uint16_t format_tag) +{ + h61883->format_tag = format_tag; +} + +uint16_t avb_get_61883_format_tag(six1883_header *h61883) +{ + return h61883->format_tag; +} + +void avb_set_61883_app_control(six1883_header *h61883, uint16_t app_control) +{ + h61883->app_control = app_control; +} + +uint16_t avb_get_61883_app_control(six1883_header *h61883) +{ + return h61883->app_control; +} + +void avb_set_61883_packet_tcode(six1883_header *h61883, uint16_t packet_tcode) +{ + h61883->packet_tcode = packet_tcode; +} + +uint16_t avb_get_61883_packet_tcode(six1883_header *h61883) +{ + return h61883->packet_tcode; +} + +void avb_set_61883_source_id(six1883_header *h61883, uint16_t source_id) +{ + h61883->source_id = source_id; +} + +uint16_t avb_get_61883_source_id(six1883_header *h61883) +{ + return h61883->source_id; +} + +void avb_set_61883_reserved0(six1883_header *h61883, uint16_t reserved0) +{ + h61883->reserved0 = reserved0; +} + +uint16_t avb_get_61883_reserved0(six1883_header *h61883) +{ + return h61883->reserved0; +} + +void avb_set_61883_data_block_size(six1883_header *h61883, uint16_t data_block_size) +{ + h61883->data_block_size = data_block_size; +} + +uint16_t avb_get_61883_data_block_size(six1883_header *h61883) +{ + return h61883->data_block_size; +} + +void avb_set_61883_reserved1(six1883_header *h61883, uint16_t reserved1) +{ + h61883->reserved1 = reserved1; +} + +uint16_t avb_get_61883_reserved1(six1883_header *h61883) +{ + return h61883->reserved1; +} + +void avb_set_61883_source_packet_header(six1883_header *h61883, uint16_t source_packet_header) +{ + h61883->source_packet_header = source_packet_header; +} + +uint16_t avb_get_61883_source_packet_header(six1883_header *h61883) +{ + return h61883->source_packet_header; +} + +void avb_set_61883_quadlet_padding_count(six1883_header *h61883, uint16_t quadlet_padding_count) +{ + h61883->quadlet_padding_count = quadlet_padding_count; +} + +uint16_t avb_get_61883_quadlet_padding_count(six1883_header *h61883) +{ + return h61883->quadlet_padding_count; +} + +void avb_set_61883_fraction_number(six1883_header *h61883, uint16_t fraction_number) +{ + h61883->fraction_number = fraction_number; +} + +uint16_t avb_get_61883_fraction_number(six1883_header *h61883) +{ + return h61883->fraction_number; +} + +void avb_set_61883_data_block_continuity(six1883_header *h61883, uint16_t data_block_continuity) +{ + h61883->data_block_continuity = data_block_continuity; +} + +uint16_t avb_get_61883_data_block_continuity(six1883_header *h61883) +{ + return h61883->data_block_continuity; +} + +void avb_set_61883_format_id(six1883_header *h61883, uint16_t format_id) +{ + h61883->format_id = format_id; +} + +uint16_t avb_get_61883_format_id(six1883_header *h61883) +{ + return h61883->format_id; +} + +void avb_set_61883_eoh(six1883_header *h61883, uint16_t eoh) +{ + h61883->eoh = eoh; +} + +uint16_t avb_get_61883_eoh(six1883_header *h61883) +{ + return h61883->eoh; +} + +void avb_set_61883_format_dependent_field(six1883_header *h61883, uint16_t format_dependent_field) +{ + h61883->format_dependent_field = format_dependent_field; +} + +uint16_t avb_get_61883_format_dependent_field(six1883_header *h61883) +{ + return h61883->format_dependent_field; +} + +void avb_set_61883_syt(six1883_header *h61883, uint16_t syt) +{ + h61883->syt = syt; +} + +uint16_t avb_get_61883_syt(six1883_header *h61883) +{ + return h61883->syt; +} + +void * avb_create_packet(uint32_t payload_len) +{ + void *avb_packet = NULL; + uint32_t size; + + size = sizeof(six1883_header); + size += sizeof(eth_header) + sizeof(seventeen22_header) + payload_len; + + avb_packet = calloc(size, sizeof(uint8_t)); + if (!avb_packet) + return NULL; + + return avb_packet; +} + +void avb_initialize_h1722_to_defaults(seventeen22_header *h1722) +{ + avb_set_1722_subtype(h1722, 0x0); + avb_set_1722_cd_indicator(h1722, 0x0); + avb_set_1722_timestamp_valid(h1722, 0x0); + avb_set_1722_gateway_valid(h1722, 0x0); + avb_set_1722_reserved0(h1722, 0x0); + avb_set_1722_sid_valid(h1722, 0x0); + avb_set_1722_reset(h1722, 0x0); + avb_set_1722_version(h1722, 0x0); + avb_set_1722_sid_valid(h1722, 0x0); + avb_set_1722_timestamp_uncertain(h1722, 0x0); + avb_set_1722_reserved1(h1722, 0x0); + avb_set_1722_timestamp(h1722, 0x0); + avb_set_1722_gateway_info(h1722, 0x0); + avb_set_1722_length(h1722, 0x0); +} + +void avb_initialize_61883_to_defaults(six1883_header *h61883) +{ + avb_set_61883_packet_channel(h61883, 0x0); + avb_set_61883_format_tag(h61883, 0x0); + avb_set_61883_app_control(h61883, 0x0); + avb_set_61883_packet_tcode(h61883, 0x0); + avb_set_61883_source_id(h61883, 0x0); + avb_set_61883_reserved0(h61883, 0x0); + avb_set_61883_data_block_size(h61883, 0x0); + avb_set_61883_reserved1(h61883, 0x0); + avb_set_61883_source_packet_header(h61883, 0x0); + avb_set_61883_quadlet_padding_count(h61883, 0x0); + avb_set_61883_fraction_number(h61883, 0x0); + avb_set_61883_data_block_continuity(h61883, 0x0); + avb_set_61883_format_id(h61883, 0x0); + avb_set_61883_eoh(h61883, 0x0); + avb_set_61883_format_dependent_field(h61883, 0x0); + avb_set_61883_syt(h61883, 0x0); +} + +int32_t avb_get_iface_mac_address(int8_t *iface, uint8_t *addr) +{ + struct ifreq ifreq; + int fd, ret; + + /* Create a socket */ + fd = socket(PF_PACKET, SOCK_RAW, htons(0x800)); + if (fd < 0) + return -1; + + memset(&ifreq, 0, sizeof(ifreq)); + + strncpy(ifreq.ifr_name, (const char*)iface, sizeof(ifreq.ifr_name)); + ret = ioctl(fd, SIOCGIFHWADDR, &ifreq); + if (ret < 0) { + close(fd); + return -1; + } + + memcpy(addr, ifreq.ifr_hwaddr.sa_data, ETH_ALEN); + close(fd); + + return 0; +} + +void avb_1722_set_eth_type(eth_header *eth_header) { + + eth_header->h_protocol[0] = 0x22; + eth_header->h_protocol[1] = 0xf0; + + return; +} + +int32_t +avb_eth_header_set_mac(eth_header *ethernet_header, uint8_t *addr, int8_t *iface) +{ + uint8_t source_mac[ETH_ALEN]; + + if (!addr || !iface) + return -EINVAL; + + if (avb_get_iface_mac_address(iface, source_mac)) + return -EINVAL; + + memcpy(ethernet_header->h_dest, addr, ETH_ALEN); + memcpy(ethernet_header->h_source, source_mac, ETH_ALEN); + + return 0; +} diff --git a/lib/avtp_pipeline/openavb_common/avb.h b/lib/avtp_pipeline/openavb_common/avb.h new file mode 100644 index 00000000..019c9d65 --- /dev/null +++ b/lib/avtp_pipeline/openavb_common/avb.h @@ -0,0 +1,206 @@ + /* + * Copyright (c) <2013>, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef __AVBTP_H__ +#define __AVBTP_H__ + +#include <inttypes.h> + +#include "igb.h" + +#define VALID 1 +#define INVALID 0 + +#define MAC_ADDR_LEN 6 + +#define IGB_BIND_NAMESZ 24 + +#define SHM_SIZE 4*8 + sizeof(pthread_mutex_t) /* 3 - 64 bit and 2 - 32 bits */ +#define SHM_NAME "/ptp" + +#define MAX_SAMPLE_VALUE ((1U << ((sizeof(int32_t)*8)-1))-1) + +#define IEEE_61883_IIDC_SUBTYPE 0x0 + +#define MRPD_PORT_DEFAULT 7500 + +#define STREAM_ID_SIZE 8 + +#define ETHER_TYPE_AVTP 0x22f0 + +typedef struct __attribute__ ((packed)) { + uint64_t subtype:7; + uint64_t cd_indicator:1; + uint64_t timestamp_valid:1; + uint64_t gateway_valid:1; + uint64_t reserved0:1; + uint64_t reset:1; + uint64_t version:3; + uint64_t sid_valid:1; + uint64_t seq_number:8; + uint64_t timestamp_uncertain:1; + uint64_t reserved1:7; + uint64_t stream_id; + uint64_t timestamp:32; + uint64_t gateway_info:32; + uint64_t length:16; + +} seventeen22_header; + +/* 61883 CIP with SYT Field */ +typedef struct { + uint16_t packet_channel:6; + uint16_t format_tag:2; + uint16_t app_control:4; + uint16_t packet_tcode:4; + uint16_t source_id:6; + uint16_t reserved0:2; + uint16_t data_block_size:8; + uint16_t reserved1:2; + uint16_t source_packet_header:1; + uint16_t quadlet_padding_count:3; + uint16_t fraction_number:2; + uint16_t data_block_continuity:8; + uint16_t format_id:6; + uint16_t eoh:2; + uint16_t format_dependent_field:8; + uint16_t syt; +} six1883_header; + +typedef struct { + uint8_t label; + uint8_t value[3]; +} six1883_sample; + +#define ETH_ALEN 6 /* Size of Ethernet address */ + +typedef struct __attribute__ ((packed)) { + /* Destination MAC address. */ + uint8_t h_dest [ETH_ALEN]; + /* Destination MAC address. */ + uint8_t h_source [ETH_ALEN]; + /* Protocol ID. */ + uint8_t h_protocol[2]; +} eth_header; + +typedef struct { + int64_t ml_phoffset; + int64_t ls_phoffset; + long double ml_freqoffset; + long double ls_freqoffset; + uint64_t local_time; +} gPtpTimeData; + +#ifndef false +typedef enum { false = 0, true = 1 } bool; +#endif + +int pci_connect(device_t * igb_dev); + +int gptpscaling(gPtpTimeData * td, char *memory_offset_buffer); + +void gptpdeinit(int shm_fd, char *memory_offset_buffer); + +int gptpinit(int *shm_fd, char **memory_offset_buffer); + +void avb_set_1722_cd_indicator(seventeen22_header *h1722, uint64_t cd_indicator); +uint64_t avb_get_1722_cd_indicator(seventeen22_header *h1722); +void avb_set_1722_subtype(seventeen22_header *h1722, uint64_t subtype); +uint64_t avb_get_1722_subtype(seventeen22_header *h1722); +void avb_set_1722_sid_valid(seventeen22_header *h1722, uint64_t sid_valid); +uint64_t avb_get_1722_sid_valid(seventeen22_header *h1722); +void avb_set_1722_version(seventeen22_header *h1722, uint64_t version); +uint64_t avb_get_1722_version(seventeen22_header *h1722); +void avb_set_1722_reset(seventeen22_header *h1722, uint64_t reset); +uint64_t avb_get_1722_reset(seventeen22_header *h1722); +void avb_set_1722_reserved0(seventeen22_header *h1722, uint64_t reserved0); +uint64_t avb_get_1722_reserved0(seventeen22_header *h1722); +void avb_set_1722_reserved1(seventeen22_header *h1722, uint64_t reserved1); +uint64_t avb_get_1722_reserved1(seventeen22_header *h1722); +void avb_set_1722_timestamp_uncertain(seventeen22_header *h1722, uint64_t timestamp_uncertain); +uint64_t avb_get_1722_timestamp_uncertain(seventeen22_header *h1722); +void avb_set_1722_timestamp(seventeen22_header *h1722, uint64_t timestamp); +uint64_t avb_get_1722_reset(seventeen22_header *h1722); +void avb_set_1722_reserved0(seventeen22_header *h1722, uint64_t reserved0); +uint64_t avb_get_1722_reserved0(seventeen22_header *h1722); +void avb_set_1722_gateway_valid(seventeen22_header *h1722, uint64_t gateway_valid); +uint64_t avb_get_1722_gateway_valid(seventeen22_header *h1722); +void avb_set_1722_timestamp_valid(seventeen22_header *h1722, uint64_t timestamp_valid); +uint64_t avb_get_1722_timestamp_valid(seventeen22_header *h1722); +void avb_set_1722_reserved1(seventeen22_header *h1722, uint64_t reserved1); +uint64_t avb_get_1722_reserved1(seventeen22_header *h1722); +void avb_set_1722_timestamp_uncertain(seventeen22_header *h1722, uint64_t timestamp_uncertain); +uint64_t avb_get_1722_timestamp_uncertain(seventeen22_header *h1722); +void avb_set_1722_timestamp(seventeen22_header *h1722, uint64_t timestamp); +uint64_t avb_get_1722_timestamp(seventeen22_header *h1722); +void avb_set_1722_gateway_info(seventeen22_header *h1722, uint64_t gateway_info); +uint64_t avb_get_1722_gateway_info(seventeen22_header *h1722); +void avb_set_1722_length(seventeen22_header *h1722, uint64_t length); +uint64_t avb_get_1722_length(seventeen22_header *h1722); +void avb_set_1722_stream_id(seventeen22_header *h1722, uint64_t stream_id); +uint64_t avb_get_1722_stream_id(seventeen22_header *h1722); +void avb_set_1722_seq_number(seventeen22_header *h1722, uint64_t seq_number); +uint64_t avb_get_1722_seq_number(seventeen22_header *h1722); + +void avb_set_61883_packet_channel(six1883_header *h61883, uint16_t packet_channel); +uint16_t avb_get_61883_length(six1883_header *h61883); +void avb_set_61883_format_tag(six1883_header *h61883, uint16_t format_tag); +uint16_t avb_get_61883_format_tag(six1883_header *h61883); +void avb_set_61883_app_control(six1883_header *h61883, uint16_t app_control); +uint16_t avb_get_61883_app_control(six1883_header *h61883); +void avb_set_61883_packet_tcode(six1883_header *h61883, uint16_t packet_tcode); +uint16_t avb_get_61883_packet_tcode(six1883_header *h61883); +void avb_set_61883_source_id(six1883_header *h61883, uint16_t source_id); +uint16_t avb_get_61883_source_id(six1883_header *h61883); +void avb_set_61883_reserved0(six1883_header *h61883, uint16_t reserved0); +uint16_t avb_get_61883_reserved0(six1883_header *h61883); +void avb_set_61883_data_block_size(six1883_header *h61883, uint16_t data_block_size); +uint16_t avb_get_61883_data_block_size(six1883_header *h61883); +void avb_set_61883_reserved1(six1883_header *h61883, uint16_t reserved1); +uint16_t avb_get_61883_reserved1(six1883_header *h61883); +void avb_set_61883_source_packet_header(six1883_header *h61883, uint16_t source_packet_header); +uint16_t avb_get_61883_source_packet_header(six1883_header *h61883); +void avb_set_61883_quadlet_padding_count(six1883_header *h61883, uint16_t quadlet_padding_count); +uint16_t avb_get_61883_quadlet_padding_count(six1883_header *h61883); +void avb_set_61883_fraction_number(six1883_header *h61883, uint16_t fraction_number); +uint16_t avb_get_61883_fraction_number(six1883_header *h61883); +void avb_set_61883_data_block_continuity(six1883_header *h61883, uint16_t data_block_continuity); +uint16_t avb_get_61883_data_block_continuity(six1883_header *h61883); +void avb_set_61883_format_id(six1883_header *h61883, uint16_t format_id); +uint16_t avb_get_61883_format_id(six1883_header *h61883); +void avb_set_61883_eoh(six1883_header *h61883, uint16_t eoh); +uint16_t avb_get_61883_eoh(six1883_header *h61883); +void avb_set_61883_format_dependent_field(six1883_header *h61883, uint16_t format_dependent_field); +uint16_t avb_get_61883_format_dependent_field(six1883_header *h61883); +void avb_set_61883_syt(six1883_header *h61883, uint16_t syt); +uint16_t avb_get_61883_syt(six1883_header *h61883); + +void * avb_create_packet(uint32_t payload_len); + +void avb_initialize_h1722_to_defaults(seventeen22_header *h1722); + +void avb_initialize_61883_to_defaults(six1883_header *h61883); + +int32_t avb_get_iface_mac_address(int8_t *iface, uint8_t *addr); + +int32_t +avb_eth_header_set_mac(eth_header *ethernet_header, uint8_t *addr, int8_t *iface); + +void avb_1722_set_eth_type(eth_header *eth_header); + +#endif /* __AVBTP_H__ */ diff --git a/lib/avtp_pipeline/openavb_common/listener_mrp_client.c b/lib/avtp_pipeline/openavb_common/listener_mrp_client.c new file mode 100644 index 00000000..1e5652c1 --- /dev/null +++ b/lib/avtp_pipeline/openavb_common/listener_mrp_client.c @@ -0,0 +1,239 @@ +/* + Copyright (c) 2013 Katja Rohloff <Katja.Rohloff@uni-jena.de> + Copyright (c) 2014, Parrot SA + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "listener_mrp_client.h" + +/* global variables */ + +int control_socket; +volatile int talker = 0; +unsigned char stream_id[8]; + +/* + * private + */ + +int send_msg(char *data, int data_len) +{ + struct sockaddr_in addr; + + if (control_socket == -1) + return -1; + if (data == NULL) + return -1; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(MRPD_PORT_DEFAULT); + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + inet_aton("127.0.0.1", &addr.sin_addr); + return sendto(control_socket, data, data_len, 0, + (struct sockaddr*)&addr, (socklen_t)sizeof(addr)); +} + +int msg_process(char *buf, int buflen) +{ + uint32_t id; + int j, l; + + fprintf(stderr, "Msg: %s\n", buf); + if (strncmp(buf, "SNE T:", 6) == 0 || strncmp(buf, "SJO T:", 6) == 0) + { + l = 6; /* skip "Sxx T:" */ + while ((l < buflen) && ('S' != buf[l++])); + if (l == buflen) + return -1; + l++; + for(j = 0; j < 8 ; l+=2, j++) + { + sscanf(&buf[l],"%02x",&id); + stream_id[j] = (unsigned char)id; + } + talker = 1; + } + return 0; +} + +int recv_msg() +{ + char *databuf; + int bytes = 0; + int ret; + + databuf = (char *)malloc(1500); + if (NULL == databuf) + return -1; + + memset(databuf, 0, 1500); + bytes = recv(control_socket, databuf, 1500, 0); + if (bytes <= -1) + { + free(databuf); + return -1; + } + ret = msg_process(databuf, bytes); + free(databuf); + + return ret; +} + +/* + * public + */ + +int create_socket() // TODO FIX! =:-| +{ + struct sockaddr_in addr; + control_socket = socket(AF_INET, SOCK_DGRAM, 0); + + /** in POSIX fd 0,1,2 are reserved */ + if (2 > control_socket) + { + if (-1 > control_socket) + close(control_socket); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(0); + + if(0 > (bind(control_socket, (struct sockaddr*)&addr, sizeof(addr)))) + { + fprintf(stderr, "Could not bind socket.\n"); + close(control_socket); + return -1; + } + return 0; +} + +int report_domain_status() +{ + char* msgbuf; + int rc; + + msgbuf = malloc(1500); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 1500); + sprintf(msgbuf, "S+D:C=6,P=3,V=0002"); + rc = send_msg(msgbuf, 1500); + free(msgbuf); + + if (rc != 1500) + return -1; + else + return 0; +} + +int join_vlan() +{ + char *msgbuf; + int rc; + + msgbuf = malloc(1500); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 1500); + sprintf(msgbuf, "V++:I=0002"); + rc = send_msg(msgbuf, 1500); + free(msgbuf); + + if (rc != 1500) + return -1; + else + return 0; +} + +int await_talker() +{ + while (0 == talker) + recv_msg(); + return 0; +} + +int send_ready() +{ + char *databuf; + int rc; + + databuf = malloc(1500); + if (NULL == databuf) + return -1; + memset(databuf, 0, 1500); + sprintf(databuf, "S+L:L=%02x%02x%02x%02x%02x%02x%02x%02x, D=2", + stream_id[0], stream_id[1], + stream_id[2], stream_id[3], + stream_id[4], stream_id[5], + stream_id[6], stream_id[7]); + rc = send_msg(databuf, 1500); + free(databuf); + + if (rc != 1500) + return -1; + else + return 0; +} + +int send_leave() +{ + char *databuf; + int rc; + + databuf = malloc(1500); + if (NULL == databuf) + return -1; + memset(databuf, 0, 1500); + sprintf(databuf, "S-L:L=%02x%02x%02x%02x%02x%02x%02x%02x, D=3", + stream_id[0], stream_id[1], + stream_id[2], stream_id[3], + stream_id[4], stream_id[5], + stream_id[6], stream_id[7]); + rc = send_msg(databuf, 1500); + free(databuf); + + if (rc != 1500) + return -1; + else + return 0; +} + +int mrp_disconnect() +{ + int rc; + char *msgbuf = malloc(1500); + + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 1500); + + sprintf(msgbuf, "BYE"); + rc = send_msg(msgbuf, 1500); + free(msgbuf); + + if (rc != 1500) + return -1; + else + return 0; +} diff --git a/lib/avtp_pipeline/openavb_common/listener_mrp_client.h b/lib/avtp_pipeline/openavb_common/listener_mrp_client.h new file mode 100644 index 00000000..142d8f39 --- /dev/null +++ b/lib/avtp_pipeline/openavb_common/listener_mrp_client.h @@ -0,0 +1,58 @@ +/* + Copyright (c) 2013 Katja Rohloff <Katja.Rohloff@uni-jena.de> + Copyright (c) 2014, Parrot SA + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef _LISTENER_MRP_CLIENT_H_ +#define _LISTENER_MRP_CLIENT_H_ + +/* + * simple_listener MRP client part + */ + +#include <stdlib.h> +#include <netinet/in.h> +#include <string.h> +#include <stdio.h> // TODO fprintf, to be removed +#include <unistd.h> + +#include "mrpd.h" + +/* global variables */ + +// TODO move these in a talker_context struct + init func + +extern int control_socket; +extern volatile int talker; +extern unsigned char stream_id[8]; + +/* functions */ + +int create_socket(); +int report_domain_status(); +int join_vlan(); +int await_talker(); +int send_ready(); +int send_leave(); +int mrp_disconnect(); + +#endif /* _LISTENER_MRP_CLIENT_H_ */ diff --git a/lib/avtp_pipeline/openavb_common/talker_mrp_client.c b/lib/avtp_pipeline/openavb_common/talker_mrp_client.c new file mode 100644 index 00000000..1b42fabb --- /dev/null +++ b/lib/avtp_pipeline/openavb_common/talker_mrp_client.c @@ -0,0 +1,597 @@ +/****************************************************************************** + + Copyright (c) 2012, Intel Corporation + Copyright (c) 2014, Parrot SA + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#include "talker_mrp_client.h" + +/* global variables */ + +int control_socket = -1; + +volatile int halt_tx = 0; +volatile int listeners = 0; +volatile int mrp_okay; +volatile int mrp_error = 0;; + +volatile int domain_a_valid = 0; +int domain_class_a_id = 0; +int domain_class_a_priority = 0; +u_int16_t domain_class_a_vid = 0; + +volatile int domain_b_valid = 0; +int domain_class_b_id = 0; +int domain_class_b_priority = 0; +u_int16_t domain_class_b_vid = 0; + +pthread_t monitor_thread; +pthread_attr_t monitor_attr; +unsigned char monitor_stream_id[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* + * private + */ + +int send_mrp_msg(char *notify_data, int notify_len) +{ + struct sockaddr_in addr; + socklen_t addr_len; + + if (control_socket == -1) + return -1; + if (notify_data == NULL) + return -1; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(MRPD_PORT_DEFAULT); + inet_aton("127.0.0.1", &addr.sin_addr); + addr_len = sizeof(addr); + return sendto(control_socket, notify_data, notify_len, 0, + (struct sockaddr *)&addr, addr_len); +} + +int process_mrp_msg(char *buf, int buflen) +{ + + /* + * 1st character indicates application + * [MVS] - MAC, VLAN or STREAM + */ + unsigned int id; + unsigned int priority; + unsigned int vid; + int i, j, k; + unsigned int substate; + unsigned char recovered_streamid[8]; + k = 0; + next_line:if (k >= buflen) + return 0; + switch (buf[k]) { + case 'E': + printf("%s from mrpd\n", buf); + fflush(stdout); + mrp_error = 1; + break; + case 'O': + mrp_okay = 1; + break; + case 'M': + case 'V': + printf("%s unhandled from mrpd\n", buf); + fflush(stdout); + + /* unhandled for now */ + break; + case 'L': + + /* parse a listener attribute - see if it matches our monitor_stream_id */ + i = k; + while (buf[i] != 'D') + i++; + i += 2; /* skip the ':' */ + sscanf(&(buf[i]), "%d", &substate); + while (buf[i] != 'S') + i++; + i += 2; /* skip the ':' */ + for (j = 0; j < 8; j++) { + sscanf(&(buf[i + 2 * j]), "%02x", &id); + recovered_streamid[j] = (unsigned char)id; + } printf + ("FOUND STREAM ID=%02x%02x%02x%02x%02x%02x%02x%02x ", + recovered_streamid[0], recovered_streamid[1], + recovered_streamid[2], recovered_streamid[3], + recovered_streamid[4], recovered_streamid[5], + recovered_streamid[6], recovered_streamid[7]); + switch (substate) { + case 0: + printf("with state ignore\n"); + break; + case 1: + printf("with state askfailed\n"); + break; + case 2: + printf("with state ready\n"); + break; + case 3: + printf("with state readyfail\n"); + break; + default: + printf("with state UNKNOWN (%d)\n", substate); + break; + } + if (substate > MSRP_LISTENER_ASKFAILED) { + if (memcmp + (recovered_streamid, monitor_stream_id, + sizeof(recovered_streamid)) == 0) { + listeners = 1; + printf("added listener\n"); + } + } + fflush(stdout); + + /* try to find a newline ... */ + while ((i < buflen) && (buf[i] != '\n') && (buf[i] != '\0')) + i++; + if (i == buflen) + return 0; + if (buf[i] == '\0') + return 0; + i++; + k = i; + goto next_line; + break; + case 'D': + i = k + 4; + + /* save the domain attribute */ + sscanf(&(buf[i]), "%d", &id); + while (buf[i] != 'P') + i++; + i += 2; /* skip the ':' */ + sscanf(&(buf[i]), "%d", &priority); + while (buf[i] != 'V') + i++; + i += 2; /* skip the ':' */ + sscanf(&(buf[i]), "%x", &vid); + if (id == 6) { + domain_class_a_id = id; + domain_class_a_priority = priority; + domain_class_a_vid = vid; + domain_a_valid = 1; + } else { + domain_class_b_id = id; + domain_class_b_priority = priority; + domain_class_b_vid = vid; + domain_b_valid = 1; + } + while ((i < buflen) && (buf[i] != '\n') && (buf[i] != '\0')) + i++; + if ((i == buflen) || (buf[i] == '\0')) + return 0; + i++; + k = i; + goto next_line; + break; + case 'T': + + /* as simple_talker we don't care about other talkers */ + i = k; + while ((i < buflen) && (buf[i] != '\n') && (buf[i] != '\0')) + i++; + if (i == buflen) + return 0; + if (buf[i] == '\0') + return 0; + i++; + k = i; + goto next_line; + break; + case 'S': + + /* handle the leave/join events */ + switch (buf[k + 4]) { + case 'L': + i = k + 5; + while (buf[i] != 'D') + i++; + i += 2; /* skip the ':' */ + sscanf(&(buf[i]), "%d", &substate); + while (buf[i] != 'S') + i++; + i += 2; /* skip the ':' */ + for (j = 0; j < 8; j++) { + sscanf(&(buf[i + 2 * j]), "%02x", &id); + recovered_streamid[j] = (unsigned char)id; + } printf + ("EVENT on STREAM ID=%02x%02x%02x%02x%02x%02x%02x%02x ", + recovered_streamid[0], recovered_streamid[1], + recovered_streamid[2], recovered_streamid[3], + recovered_streamid[4], recovered_streamid[5], + recovered_streamid[6], recovered_streamid[7]); + switch (substate) { + case 0: + printf("with state ignore\n"); + break; + case 1: + printf("with state askfailed\n"); + break; + case 2: + printf("with state ready\n"); + break; + case 3: + printf("with state readyfail\n"); + break; + default: + printf("with state UNKNOWN (%d)\n", substate); + break; + } + switch (buf[k + 1]) { + case 'L': + printf("got a leave indication\n"); + if (memcmp + (recovered_streamid, monitor_stream_id, + sizeof(recovered_streamid)) == 0) { + listeners = 0; + printf("listener left\n"); + } + break; + case 'J': + case 'N': + printf("got a new/join indication\n"); + if (substate > MSRP_LISTENER_ASKFAILED) { + if (memcmp + (recovered_streamid, + monitor_stream_id, + sizeof(recovered_streamid)) == 0) + listeners = 1; + } + break; + } + + /* only care about listeners ... */ + default: + return 0; + break; + } + break; + case '\0': + break; + } + return 0; +} + +void *mrp_monitor_thread(void *arg) +{ + char *msgbuf; + struct sockaddr_in client_addr; + struct msghdr msg; + struct iovec iov; + int bytes = 0; + struct pollfd fds; + int rc; + (void) arg; /* unused */ + + msgbuf = (char *)malloc(MAX_MRPD_CMDSZ); + if (NULL == msgbuf) + return NULL; + while (!halt_tx) { + fds.fd = control_socket; + fds.events = POLLIN; + fds.revents = 0; + rc = poll(&fds, 1, 100); + if (rc < 0) { + free(msgbuf); + pthread_exit(NULL); + } + if (rc == 0) + continue; + if ((fds.revents & POLLIN) == 0) { + free(msgbuf); + pthread_exit(NULL); + } + memset(&msg, 0, sizeof(msg)); + memset(&client_addr, 0, sizeof(client_addr)); + memset(msgbuf, 0, MAX_MRPD_CMDSZ); + iov.iov_len = MAX_MRPD_CMDSZ; + iov.iov_base = msgbuf; + msg.msg_name = &client_addr; + msg.msg_namelen = sizeof(client_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + bytes = recvmsg(control_socket, &msg, 0); + if (bytes < 0) + continue; + process_mrp_msg(msgbuf, bytes); + } + free(msgbuf); + pthread_exit(NULL); +} + +/* + * public + */ + +int mrp_connect(void) +{ + struct sockaddr_in addr; + int sock_fd = -1; + sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock_fd < 0) + goto out; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(MRPD_PORT_DEFAULT); + inet_aton("127.0.0.1", &addr.sin_addr); + memset(&addr, 0, sizeof(addr)); + control_socket = sock_fd; + return 0; + out: if (sock_fd != -1) + close(sock_fd); + sock_fd = -1; + return -1; +} + +int mrp_disconnect(void) +{ + char *msgbuf; + int rc; + + msgbuf = malloc(1500); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 64); + sprintf(msgbuf, "BYE"); + mrp_okay = 0; + rc = send_mrp_msg(msgbuf, 1500); + free(msgbuf); + + if (rc != 1500) + return -1; + else + return 0; +} + +int mrp_monitor(void) +{ + int rc; + rc = pthread_attr_init(&monitor_attr); + rc |= pthread_create(&monitor_thread, NULL, mrp_monitor_thread, NULL); + return rc; +} + +int mrp_register_domain(int *class_id, int *priority, u_int16_t * vid) +{ + char *msgbuf; + int rc; + + msgbuf = malloc(1500); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 1500); + + sprintf(msgbuf, "S+D:C=%d,P=%d,V=%04x", *class_id, *priority, *vid); + mrp_okay = 0; + rc = send_mrp_msg(msgbuf, 1500); + free(msgbuf); + + if (rc != 1500) + return -1; + else + return 0; +} + + +int +mrp_advertise_stream(uint8_t * streamid, + uint8_t * destaddr, + u_int16_t vlan, + int pktsz, int interval, int priority, int latency) +{ + char *msgbuf; + int rc; + + msgbuf = malloc(1500); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 1500); + + sprintf(msgbuf, "S++:S=%02X%02X%02X%02X%02X%02X%02X%02X" + ",A=%02X%02X%02X%02X%02X%02X" + ",V=%04X" + ",Z=%d" + ",I=%d" + ",P=%d" + ",L=%d", streamid[0], streamid[1], streamid[2], + streamid[3], streamid[4], streamid[5], streamid[6], + streamid[7], destaddr[0], destaddr[1], destaddr[2], + destaddr[3], destaddr[4], destaddr[5], vlan, pktsz, + interval, priority << 5, latency); + mrp_okay = 0; + rc = send_mrp_msg(msgbuf, 1500); + free(msgbuf); + + if (rc != 1500) + return -1; + else + return 0; +} + +int +mrp_unadvertise_stream(uint8_t * streamid, + uint8_t * destaddr, + u_int16_t vlan, + int pktsz, int interval, int priority, int latency) +{ + char *msgbuf; + int rc; + msgbuf = malloc(1500); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 1500); + sprintf(msgbuf, "S--:S=%02X%02X%02X%02X%02X%02X%02X%02X" + ",A=%02X%02X%02X%02X%02X%02X" + ",V=%04X" + ",Z=%d" + ",I=%d" + ",P=%d" + ",L=%d", streamid[0], streamid[1], streamid[2], + streamid[3], streamid[4], streamid[5], streamid[6], + streamid[7], destaddr[0], destaddr[1], destaddr[2], + destaddr[3], destaddr[4], destaddr[5], vlan, pktsz, + interval, priority << 5, latency); + mrp_okay = 0; + rc = send_mrp_msg(msgbuf, 1500); + free(msgbuf); + + if (rc != 1500) + return -1; + else + return 0; +} + + +int mrp_await_listener(unsigned char *streamid) +{ + char *msgbuf; + int rc; + + memcpy(monitor_stream_id, streamid, sizeof(monitor_stream_id)); + msgbuf = malloc(1500); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 1500); + sprintf(msgbuf, "S??"); + rc = send_mrp_msg(msgbuf, 1500); + free(msgbuf); + if (rc != 1500) + return -1; + + /* either already there ... or need to wait ... */ + while (!halt_tx && (listeners == 0)) + usleep(20000); + + return 0; +} + +/* + * actually not used + */ + +int mrp_get_domain(int *class_a_id, int *a_priority, u_int16_t * a_vid, + int *class_b_id, int *b_priority, u_int16_t * b_vid) +{ + char *msgbuf; + int ret; + + /* we may not get a notification if we are joining late, + * so query for what is already there ... + */ + msgbuf = malloc(1500); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 1500); + sprintf(msgbuf, "S??"); + ret = send_mrp_msg(msgbuf, 1500); + free(msgbuf); + if (ret != 1500) + return -1; + while (!halt_tx && (domain_a_valid == 0) && (domain_b_valid == 0)) + usleep(20000); + *class_a_id = 0; + *a_priority = 0; + *a_vid = 0; + *class_b_id = 0; + *b_priority = 0; + *b_vid = 0; + if (domain_a_valid) { + *class_a_id = domain_class_a_id; + *a_priority = domain_class_a_priority; + *a_vid = domain_class_a_vid; + } + if (domain_b_valid) { + *class_b_id = domain_class_b_id; + *b_priority = domain_class_b_priority; + *b_vid = domain_class_b_vid; + } + return 0; +} + +int mrp_join_vlan() +{ + char *msgbuf; + int rc; + + msgbuf = malloc(1500); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 1500); + sprintf(msgbuf, "V++:I=0002"); + rc = send_mrp_msg(msgbuf, 1500); + free(msgbuf); + + if (rc != 1500) + return -1; + else + return 0; +} + +int mrp_join_listener(uint8_t * streamid) +{ + char *msgbuf; + int rc; + + msgbuf = malloc(1500); + if (NULL == msgbuf) + return -1; + memset(msgbuf, 0, 1500); + sprintf(msgbuf, "S+L:S=%02X%02X%02X%02X%02X%02X%02X%02X" + ",D=2", streamid[0], streamid[1], streamid[2], streamid[3], + streamid[4], streamid[5], streamid[6], streamid[7]); + mrp_okay = 0; + rc = send_mrp_msg(msgbuf, 1500); + free(msgbuf); + + if (rc != 1500) + return -1; + else + return 0; +} + +// TODO remove +int recv_mrp_okay() +{ + while ((mrp_okay == 0) && (mrp_error == 0)) + usleep(20000); + return 0; +} + diff --git a/lib/avtp_pipeline/openavb_common/talker_mrp_client.h b/lib/avtp_pipeline/openavb_common/talker_mrp_client.h new file mode 100644 index 00000000..60ced4cd --- /dev/null +++ b/lib/avtp_pipeline/openavb_common/talker_mrp_client.h @@ -0,0 +1,82 @@ +/****************************************************************************** + + Copyright (c) 2012, Intel Corporation + Copyright (c) 2014, Parrot SA + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#ifndef _TALKER_MRP_CLIENT_H_ +#define _TALKER_MRP_CLIENT_H_ + +/* + * simple_talker MRP client part + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <netinet/in.h> +#include <pthread.h> +#include <poll.h> + +#include "mrpd.h" +#include "mrp.h" +#include "msrp.h" // spurious dep daemons/mrpd/msrp.h:50:#define MSRP_LISTENER_ASKFAILED + +/* global variables */ + +// TODO move these in a talker_context struct + init func + +extern volatile int halt_tx; +extern volatile int listeners; +extern volatile int mrp_error; + +extern volatile int domain_a_valid; +extern int domain_class_a_id; +extern int domain_class_a_priority; +extern u_int16_t domain_class_a_vid; + +extern volatile int domain_b_valid; +extern int domain_class_b_id; +extern int domain_class_b_priority; +extern u_int16_t domain_class_b_vid; + +/* functions */ + +int mrp_connect(void); +int mrp_disconnect(void); +int mrp_monitor(void); +int mrp_register_domain(int *class_id, int *priority, u_int16_t *vid); +int mrp_join_vlan(void); +int mrp_advertise_stream(uint8_t * streamid, uint8_t * destaddr, u_int16_t vlan, int pktsz, int interval, int priority, int latency); +int mrp_unadvertise_stream(uint8_t * streamid, uint8_t * destaddr, u_int16_t vlan, int pktsz, int interval, int priority, int latency); +int mrp_await_listener(unsigned char *streamid); + +#endif /* _TALKER_MRP_CLIENT_H_ */ diff --git a/lib/avtp_pipeline/platform/Linux/CMakeLists.txt b/lib/avtp_pipeline/platform/Linux/CMakeLists.txt new file mode 100644 index 00000000..7f04b1d0 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/CMakeLists.txt @@ -0,0 +1,301 @@ +cmake_minimum_required ( VERSION 2.6 ) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") +project ( AVB ) + +# Some CMake voodoo to set the default build type +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) +else () + IF (NOT (("${CMAKE_BUILD_TYPE}" STREQUAL "Release") + OR ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + OR ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo") + OR ("${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel"))) + MESSAGE ( FATAL_ERROR "Unknown CMAKE_BUILD_TYPE; Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel") + ENDIF () +ENDIF() +MESSAGE ("-- Build type is ${CMAKE_BUILD_TYPE}") + +# Set a define to signal build to source files +STRING ( TOUPPER "${CMAKE_BUILD_TYPE}_BUILD" BUILD_TYPE_STRING ) +SET ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D${BUILD_TYPE_STRING}" ) + +# CMake automatically adds some compiler flags based on CMAKE_BUILD_TYPE +# for Debug: "-g" +# for RelWithDebInfo: "-O2 -g" +# for Release: "-03 -DNDEBUG" +# for MinSizeRel: "-0s -DNDEBUG" + +# point to AVB SRC directory +set ( AVB_SRC_DIR ${CMAKE_SOURCE_DIR} ) + +# point to HAL directory +set ( AVB_HAL_DIR ${AVB_SRC_DIR}/platform/${OPENAVB_HAL} ) + +# point to OSAL directory +set ( AVB_OSAL_DIR ${AVB_SRC_DIR}/platform/${OPENAVB_OSAL} ) + +# point to TCAL directory +set ( AVB_TCAL_DIR ${AVB_SRC_DIR}/platform/platTCAL/${OPENAVB_TCAL} ) + +# Directory to install binaries to +set ( AVB_INSTALL_BIN_DIR ${CMAKE_BINARY_DIR}/bin ) + +# Directory to install static libraries to +set ( AVB_INSTALL_LIB_DIR ${CMAKE_BINARY_DIR}/lib ) + +# Directory to install AVTP Interface module SDK to +set ( SDK_INSTALL_SDK_INTF_MOD_DIR ${CMAKE_BINARY_DIR}/sdk_intf_mod ) + +# Directory to install AVTP Interface module SDK to +set ( SDK_INSTALL_SDK_MAP_MOD_DIR ${CMAKE_BINARY_DIR}/sdk_map_mod ) + +# Directory to install EAVB SDK to +set ( SDK_INSTALL_SDK_EAVB_DIR ${CMAKE_BINARY_DIR}/sdk_eavb ) + +MESSAGE ("-- CMAKE_SOURCE_DIR : ${CMAKE_SOURCE_DIR}") +MESSAGE ("-- AVB_SRC_DIR : ${AVB_SRC_DIR}") +MESSAGE ("-- AVB_HAL_DIR : ${AVB_HAL_DIR}") +MESSAGE ("-- AVB_OSAL_DIR : ${AVB_OSAL_DIR}") +MESSAGE ("-- AVB_INSTALL_BIN_DIR : ${AVB_INSTALL_BIN_DIR}") +MESSAGE ("-- AVB_INSTALL_LIB_DIR : ${AVB_INSTALL_LIB_DIR}") +MESSAGE ("-- SDK_INSTALL_SDK_INTF_MOD_DIR : ${SDK_INSTALL_SDK_INTF_MOD_DIR}") +MESSAGE ("-- SDK_INSTALL_SDK_MAP_MOD_DIR : ${SDK_INSTALL_SDK_MAP_MOD_DIR}") +MESSAGE ("-- SDK_INSTALL_SDK_EAVB_DIR : ${SDK_INSTALL_SDK_EAVB_DIR}") + + +# Turn on all build warnings +set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -Wall" ) + +# Set default visibility of symbols (requires GCC version > 4) +set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden" ) + +# Need this to use pthread attributes +set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE" ) + +# Increase ini parser's max line length +set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DINI_MAX_LINE=1024" ) + +# Default feature flags +if (NOT DEFINED AVB_FEATURE_FQTSS) + set ( AVB_FEATURE_FQTSS 1 ) +endif () +# include GStreamer interfaces if not defined +if (NOT DEFINED AVB_FEATURE_GSTREAMER) + set ( AVB_FEATURE_GSTREAMER 1 ) +endif () +# Default Endpoint feature +if (NOT DEFINED AVB_FEATURE_ENDPOINT) + set ( AVB_FEATURE_ENDPOINT 0 ) +endif () + + +# Export feature flags for sub-builds +if (AVB_FEATURE_FQTSS) + set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAVB_FEATURE_FQTSS=1" ) +endif () +if (AVB_FEATURE_GSTREAMER) + set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAVB_FEATURE_GSTREAMER=1" ) +endif () +if (AVB_FEATURE_ENDPOINT) + set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAVB_FEATURE_ENDPOINT=1" ) +endif () + +#Export Platform defines +if ( PLATFORM_DEFINE ) + set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D${PLATFORM_DEFINE}" ) +endif () + +if (NOT DEFINED SDK_DOC_ONLY) + if ( NOT DEFINED CROSS_PREFIX ) + MESSAGE ( "-- Native build for ${CMAKE_SYSTEM} on ${CMAKE_SYSTEM_PROCESSOR}" ) + set ( ARCH x86 ) + set ( LINUX_KERNEL_DIR /usr/src/linux-headers-${CMAKE_SYSTEM_VERSION} ) + find_package(PkgConfig REQUIRED) + pkg_check_modules(GLIB_PKG glib-2.0) + if (AVB_FEATURE_GSTREAMER) + pkg_check_modules(GST_PKG gstreamer-app-0.10) + endif() + find_package(ALSA REQUIRED) + set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUBUNTU=1" ) + else () + message ( "-- Cross-compiling for " ${OPENAVB_PLATFORM} " (" ${CROSS_PREFIX} "gcc)" ) + endif () + + if (NOT DEFINED ARCH) + MESSAGE ( FATAL_ERROR "Aborting: ARCH not set" ) + endif () + if (NOT DEFINED LINUX_KERNEL_DIR) + MESSAGE ( FATAL_ERROR "Aborting: LINUX_KERNEL_DIR not set" ) + endif () + if (NOT DEFINED GLIB_PKG_INCLUDE_DIRS OR NOT DEFINED GLIB_PKG_LIBRARIES) + MESSAGE ( FATAL_ERROR "Aborting: glib-2.0 library not found" ) + endif () + if (AVB_FEATURE_GSTREAMER) + if (NOT DEFINED GST_PKG_INCLUDE_DIRS OR NOT DEFINED GST_PKG_LIBRARIES) + MESSAGE ( FATAL_ERROR "Aborting: gstreamer library not found" ) + endif() + endif () + if (NOT DEFINED ALSA_INCLUDE_DIRS) + MESSAGE ( FATAL_ERROR "Aborting: alsa library not found" ) + endif () +endif() + +# Add /usr/lib to library search path +link_directories( ${SYSROOT}/usr/lib ) +link_directories ( ${PLATFORM_SPECIFIC_DIRECTORIES} ) +set ( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_SPECIFIC_LINKER_FLAGS}" ) + +# Setup platform directories +include_directories ( ${PLATFORM_INCLUDE_DIRECTORIES} ) +link_directories ( ${PLATFORM_LINK_DIRECTORIES} ) + +# Add our include directories +# +# TODO: CMakeLists.txt Cleanup +# Many of the AVB_SRC_DIR directories listed below +# need to be listed due to component A including +# header from componet B which is dependent on +# component C ... +# +# These should be cleaned up to limit the dependencies +# across components. +# +include_directories( + ${AVB_TCAL_DIR} + ${AVB_HAL_DIR} + ${AVB_HAL_DIR}/mcr + ${AVB_OSAL_DIR} + ${AVB_OSAL_DIR}/include + ${AVB_OSAL_DIR}/fqtss/include + ${AVB_SRC_DIR}/include + ${AVB_OSAL_DIR}/avtp + ${AVB_OSAL_DIR}/tl + ${AVB_SRC_DIR}/avtp + ${AVB_SRC_DIR}/endpoint + ${AVB_SRC_DIR}/srp + ${AVB_SRC_DIR}/inih + ${AVB_SRC_DIR}/map_mjpeg + ${AVB_SRC_DIR}/map_mpeg2ts + ${AVB_SRC_DIR}/map_null + ${AVB_SRC_DIR}/map_pipe + ${AVB_SRC_DIR}/map_aaf_audio + ${AVB_SRC_DIR}/map_uncmp_audio + ${AVB_SRC_DIR}/map_ctrl + ${AVB_SRC_DIR}/map_h264 + ${AVB_SRC_DIR}/intf_ctrl + ${AVB_SRC_DIR}/mcr + ${AVB_SRC_DIR}/mediaq + ${AVB_SRC_DIR}/rawsock + ${AVB_SRC_DIR}/qmgr + ${AVB_SRC_DIR}/tl + ${AVB_SRC_DIR}/util + ${AVB_OSAL_DIR}/endpoint + ${AVB_OSAL_DIR}/fqtss/qmgr + ) + +# Need include and link directories for GLIB +include_directories(${GLIB_PKG_INCLUDE_DIRS}) +link_directories(${GLIB_PKG_LIBRARY_DIRS}) + +# AVB Core Library +SET ( SRC_FILES "" ) +add_subdirectory ( avtp ) +add_subdirectory ( tl ) +add_subdirectory ( util ) +add_subdirectory ( mediaq ) +add_subdirectory ( inih ) +add_subdirectory ( rawsock ) +add_subdirectory ( qmgr ) +SET ( SRC_FILES ${SRC_FILES} ${AVB_OSAL_DIR}/openavb_osal.c ) +SET ( SRC_FILES ${SRC_FILES} ${AVB_OSAL_DIR}/rawsock/openavb_rawsock.c ) +SET ( SRC_FILES ${SRC_FILES} ${AVB_OSAL_DIR}/openavb_time_osal.c ) +SET ( SRC_FILES ${SRC_FILES} ${AVB_HAL_DIR}/openavb_ether_hal.c ) +SET ( SRC_FILES ${SRC_FILES} ${AVB_HAL_DIR}/openavb_time_hal.c ) +SET ( SRC_FILES ${SRC_FILES} ${AVB_HAL_DIR}/mcr/openavb_mcr_hal.c ) + +# OpenAVB Common (support) files +SET ( SRC_FILES ${SRC_FILES} ${AVB_SRC_DIR}/openavb_common/avb.c ) +SET ( SRC_FILES ${SRC_FILES} ${AVB_SRC_DIR}/openavb_common/listener_mrp_client.c ) +SET ( SRC_FILES ${SRC_FILES} ${AVB_SRC_DIR}/openavb_common/talker_mrp_client.c ) + +add_library ( avbTl ${SRC_FILES} ) +target_link_libraries ( avbTl dl ) + +install ( TARGETS avbTl ARCHIVE DESTINATION ${AVB_INSTALL_LIB_DIR} ) + +# avb_host (openavb_host and openavb_harness) +add_subdirectory ( ${AVB_OSAL_DIR}/avb_host ) + +# mapping modules +macro (add_map_mod MAP_NAME) + SET ( SRC_FILES "" ) + add_subdirectory ( ${MAP_NAME} ) + add_library ( ${MAP_NAME} ${SRC_FILES} ) + install ( TARGETS ${MAP_NAME} ARCHIVE DESTINATION ${AVB_INSTALL_LIB_DIR} ) +endmacro() +add_map_mod ( "map_ctrl" ) +add_map_mod ( "map_mjpeg" ) +add_map_mod ( "map_mpeg2ts" ) +add_map_mod ( "map_null" ) +add_map_mod ( "map_pipe" ) +add_map_mod ( "map_aaf_audio" ) +add_map_mod ( "map_uncmp_audio" ) +add_map_mod ( "map_h264" ) + +# Interface modules (common) +macro (add_intf_mod INTF_NAME) + SET ( SRC_FILES "" ) + add_subdirectory ( ${INTF_NAME} ) + add_library ( ${INTF_NAME} ${SRC_FILES} ) + install ( TARGETS ${INTF_NAME} ARCHIVE DESTINATION ${AVB_INSTALL_LIB_DIR} ) + install ( DIRECTORY "${AVB_SRC_DIR}/${INTF_NAME}/" DESTINATION ${AVB_INSTALL_BIN_DIR} FILES_MATCHING PATTERN "*.ini" ) +endmacro() +add_intf_mod ( "intf_ctrl" ) +add_intf_mod ( "intf_echo" ) +add_intf_mod ( "intf_logger" ) +add_intf_mod ( "intf_null" ) +add_intf_mod ( "intf_tonegen" ) +add_intf_mod ( "intf_viewer" ) + +# Interface modules (platform) +macro (add_intf_mod_platform INTF_NAME) + SET ( SRC_FILES "" ) + SET ( INTF_INCLUDE_DIR "") + SET ( INTF_LIBRARY_DIR "") + SET ( INTF_LIBRARY "") + add_subdirectory ( ${AVB_OSAL_DIR}/${INTF_NAME} ) + include_directories ( ${INTF_INCLUDE_DIR} ) + add_library ( ${INTF_NAME} ${SRC_FILES} ) + install ( TARGETS ${INTF_NAME} ARCHIVE DESTINATION ${AVB_INSTALL_LIB_DIR} ) + install ( DIRECTORY "${AVB_OSAL_DIR}/${INTF_NAME}/" DESTINATION ${AVB_INSTALL_BIN_DIR} FILES_MATCHING PATTERN "*.ini" ) +endmacro() + +add_intf_mod_platform ( "intf_alsa" ) +if (AVB_FEATURE_GSTREAMER) + add_intf_mod_platform ( "intf_mpeg2ts_gst" ) + add_intf_mod_platform ( "intf_mjpeg_gst" ) +endif () +add_intf_mod_platform ( "intf_mpeg2ts_file" ) +add_intf_mod_platform ( "intf_wav_file" ) + +# API documentation +add_subdirectory ( documents ) + +# SDKS +add_subdirectory ( sdk ) + +# rawsock_rx +add_executable (rawsock_rx ${AVB_OSAL_DIR}/rawsock/rawsock_rx.c) +target_link_libraries (rawsock_rx avbTl ${GLIB_PKG_LIBRARIES} pthread rt pci ${PLATFORM_LINK_LIBRARIES} ) +install ( TARGETS rawsock_rx RUNTIME DESTINATION ${AVB_INSTALL_BIN_DIR} ) + +# rawsock_tx +add_executable (rawsock_tx ${AVB_OSAL_DIR}/rawsock/rawsock_tx.c) +target_link_libraries (rawsock_tx avbTl ${GLIB_PKG_LIBRARIES} pthread rt pci ${PLATFORM_LINK_LIBRARIES} ) +install ( TARGETS rawsock_tx RUNTIME DESTINATION ${AVB_INSTALL_BIN_DIR} ) + +# Copy additional installation files +install ( FILES ${AVB_SRC_DIR}/endpoint/endpoint.ini DESTINATION ${AVB_INSTALL_BIN_DIR} ) +install ( PROGRAMS ${AVB_SRC_DIR}/endpoint/shutdown_openavb_endpoint.sh DESTINATION ${AVB_INSTALL_BIN_DIR} ) + + diff --git a/lib/avtp_pipeline/platform/Linux/avb_host/CMakeLists.txt b/lib/avtp_pipeline/platform/Linux/avb_host/CMakeLists.txt new file mode 100644 index 00000000..2d28ff93 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/avb_host/CMakeLists.txt @@ -0,0 +1,84 @@ +include_directories( + ${AVB_OSAL_DIR}/tl + ${AVB_OSAL_DIR}/endpoint + ${AVB_SRC_DIR}/util + ${AVB_SRC_DIR}/tl + ${AVB_SRC_DIR}/srp + ) + +# Rules to build the AVB host +add_executable ( openavb_host openavb_host.c ) +target_link_libraries( openavb_host + map_ctrl + map_mjpeg + map_mpeg2ts + map_null + map_pipe + map_aaf_audio + map_uncmp_audio + map_h264 + intf_ctrl + intf_echo + intf_logger + intf_null + intf_tonegen + intf_viewer + intf_alsa + intf_mpeg2ts_gst + intf_mjpeg_gst + intf_mpeg2ts_file + intf_wav_file + avbTl + ${PLATFORM_LINK_LIBRARIES} + ${ALSA_LIBRARIES} + ${GSTRTP_PKG_LIBRARIES} + ${GLIB_PKG_LIBRARIES} + ${GST_PKG_LIBRARIES} + pthread + rt + dl + pci ) + + +# Rules to build the AVB harness +add_executable ( openavb_harness openavb_harness.c ) +target_link_libraries( openavb_harness + map_ctrl + map_mjpeg + map_mpeg2ts + map_null + map_pipe + map_aaf_audio + map_uncmp_audio + map_h264 + intf_ctrl + intf_echo + intf_logger + intf_null + intf_tonegen + intf_viewer + intf_alsa + intf_mpeg2ts_gst + intf_mjpeg_gst + intf_mpeg2ts_file + intf_wav_file + avbTl + ${PLATFORM_LINK_LIBRARIES} + ${ALSA_LIBRARIES} + ${GSTRTP_PKG_LIBRARIES} + ${GLIB_PKG_LIBRARIES} + ${GST_PKG_LIBRARIES} + pthread + rt + dl + pci ) + +# Install rules +install ( TARGETS openavb_host RUNTIME DESTINATION ${AVB_INSTALL_BIN_DIR} ) +install ( TARGETS openavb_harness RUNTIME DESTINATION ${AVB_INSTALL_BIN_DIR} ) + +if (AVB_FEATURE_GSTREAMER) +include_directories( ${GLIB_PKG_INCLUDE_DIRS} ${GST_PKG_INCLUDE_DIRS} ) +target_link_libraries( openavb_host ${GLIB_PKG_LIBRARIES} ${GST_PKG_LIBRARIES} ${PLATFORM_LINK_LIBRARIES} ) +target_link_libraries( openavb_harness ${GLIB_PKG_LIBRARIES} ${GST_PKG_LIBRARIES} ${PLATFORM_LINK_LIBRARIES} ) +endif () diff --git a/lib/avtp_pipeline/platform/Linux/avb_host/openavb_harness.c b/lib/avtp_pipeline/platform/Linux/avb_host/openavb_harness.c new file mode 100644 index 00000000..bb2e4353 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/avb_host/openavb_harness.c @@ -0,0 +1,512 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Talker listener test host implementation. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <signal.h> +#include <pthread.h> +#include "openavb_tl_pub.h" +#include "openavb_osal_pub.h" +#include "openavb_plugin.h" +#include "openavb_trace_pub.h" +#ifdef AVB_FEATURE_GSTREAMER +#include <gst/gst.h> +#endif +#include <inttypes.h> + +#define AVB_LOG_COMPONENT "TL Harness" +#include "openavb_log_pub.h" + +bool bRunning = TRUE; + +// Platform indendent mapping modules +extern bool openavbMapPipeInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); +extern bool openavbMapAVTPAudioInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); +extern bool openavbMapCtrlInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); +extern bool openavbMapH264Initialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); +extern bool openavbMapMjpegInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); +extern bool openavbMapMpeg2tsInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); +extern bool openavbMapNullInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); +extern bool openavbMapUncmpAudioInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); + +// Platform indendent interface modules +extern bool openavbIntfEchoInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfCtrlInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfLoggerInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfNullInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfToneGenInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfViewerInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); + +// Linux interface modules +extern bool openavbIntfAlsaInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfMjpegGstInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfMpeg2tsFileInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfMpeg2tsGstInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfWavFileInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); + + +/*********************************************** + * Signal handler - used to respond to signals. + * Allows graceful cleanup. + */ +static void openavbTLSigHandler(int signal) +{ + AVB_TRACE_ENTRY(AVB_TRACE_HOST); + + if (signal == SIGINT) { + AVB_LOG_INFO("Host shutting down"); + bRunning = FALSE; + } + else if (signal == SIGUSR1) { + AVB_LOG_DEBUG("Waking up streaming thread"); + } + else { + AVB_LOG_ERROR("Unexpected signal"); + } + + AVB_TRACE_EXIT(AVB_TRACE_HOST); +} + +void openavbTlHarnessUsage(char *programName) +{ + printf( + "\n" + "Usage: %s [options] file...\n" + " -a val Override stream address in each configuration file.\n" + " -h Prints this message.\n" + " -i Enables interactive mode.\n" + " -s val Stream count. Starts 'val' number of streams for each configuration file. stream_uid will be overriden.\n" + "\n" + "Examples:\n" + " %s talker.ini\n" + " Start 1 stream with data from the ini file.\n\n" + " %s talker1.ini talker2.ini\n" + " Start 2 streams with data from the ini files.\n\n" + " %s listener.ini,stream_addr=84:7E:40:2C:8F:DE\n" + " Start 1 stream and override the sream_addr in the ini file.\n\n" + " %s -i -s 8 -a 84:7E:40:2C:8F:DE listener.ini\n" + " Work interactively with 8 streams overriding the stream_uid and stream_addr of each.\n\n" + , + programName, programName, programName, programName, programName); +} + +void openavbTlHarnessMenu() +{ + printf( + "\n" + " MENU OPTIONS\n" + " s Start all streams\n" + " t Stop all streams\n" + " l List streams\n" + " 0-99 Toggle the state of the numbered stream\n" + " m Display this menu\n" + " z Stats\n" + " x Exit\n" + ); +} + + +/********************************************** + * main + */ +int main(int argc, char *argv[]) +{ + AVB_TRACE_ENTRY(AVB_TRACE_HOST); + + // Command line vars + char *programName; + char *optStreamAddr = NULL; + bool optInteractive = FALSE; + int optStreamCount = 1; + bool optStreamCountSet = FALSE; + + // Talker listener vars + int iniIdx = 0; + int iniCount = 0; + int tlCount = 0; + char **tlIniList = NULL; + tl_handle_t *tlHandleList = NULL; + + // General vars + int i1, i2; + + // Setup signal handler. Catch SIGINT and shutdown cleanly + struct sigaction sa; + sa.sa_handler = openavbTLSigHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; // not SA_RESTART + sigaction(SIGINT, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); + + registerStaticMapModule(openavbMapPipeInitialize); + registerStaticMapModule(openavbMapAVTPAudioInitialize); + registerStaticMapModule(openavbMapCtrlInitialize); + registerStaticMapModule(openavbMapH264Initialize); + registerStaticMapModule(openavbMapMjpegInitialize); + registerStaticMapModule(openavbMapMpeg2tsInitialize); + registerStaticMapModule(openavbMapNullInitialize); + registerStaticMapModule(openavbMapUncmpAudioInitialize); + + registerStaticIntfModule(openavbIntfEchoInitialize); + registerStaticIntfModule(openavbIntfCtrlInitialize); + registerStaticIntfModule(openavbIntfLoggerInitialize); + registerStaticIntfModule(openavbIntfNullInitialize); + //registerStaticIntfModule(openavbIntfToneGenInitialize); + registerStaticIntfModule(openavbIntfViewerInitialize); + registerStaticIntfModule(openavbIntfAlsaInitialize); + //registerStaticIntfModule(openavbIntfMjpegGstInitialize); // TODO_OPENAVB : Not sure why MJPEG is having an issue linking GST RTP + registerStaticIntfModule(openavbIntfMpeg2tsFileInitialize); + registerStaticIntfModule(openavbIntfMpeg2tsGstInitialize); + registerStaticIntfModule(openavbIntfWavFileInitialize); + + osalAVBInitialize(); + + // Process command line + programName = strrchr(argv[0], '/'); + programName = programName ? programName + 1 : argv[0]; + + if (argc < 2) { + osalAVBFinalize(); + openavbTlHarnessUsage(programName); + exit(-1); + } + + bool optDone = FALSE; + while (!optDone) { + int opt = getopt(argc, argv, "a:his:"); + if (opt != EOF) { + switch (opt) { + case 'a': + optStreamAddr = strdup(optarg); + break; + case 'i': + optInteractive = TRUE; + break; + case 's': + optStreamCount = atoi(optarg); + optStreamCountSet = TRUE; + break; + case '?': + default: + openavbTlHarnessUsage(programName); + osalAVBFinalize(); + exit(-1); + } + } + else { + optDone = TRUE; + } + } + + // Setup the talker listener counts and lists + iniIdx = optind; + iniCount = argc - iniIdx; + tlCount = iniCount * optStreamCount; + + tlIniList = calloc(1, sizeof(char *) * tlCount); + if (!tlIniList) { + AVB_LOG_ERROR("Unable to allocate ini list"); + osalAVBFinalize(); + exit(-1); + } + + tlHandleList = calloc(1, sizeof(tl_handle_t) * tlCount); + if (!tlHandleList) { + AVB_LOG_ERROR("Unable to allocate handle list"); + osalAVBFinalize(); + exit(-1); + } + + if (!openavbTLInitialize(tlCount)) { + AVB_LOG_ERROR("Unable to initialize talker listener library"); + osalAVBFinalize(); + exit(-1); + } + + // Populate the ini file list + int tlIndex = 0; + for (i1 = 0; i1 < iniCount; i1++) { + char iniFile1[1024]; + char iniFile2[1024]; + if (optStreamAddr) { + sprintf(iniFile1, "%s,stream_addr=%s", argv[i1 + iniIdx], optStreamAddr); + } + else { + sprintf(iniFile1, "%s", argv[i1 + iniIdx]); + } + + if (!optStreamCountSet) { + tlIniList[tlIndex++] = strdup(iniFile1); + } + else { + for (i2 = 0; i2 < optStreamCount; i2++) { + sprintf(iniFile2, "%s,stream_uid=%d", iniFile1, tlIndex); + tlIniList[tlIndex++] = strdup(iniFile2); + } + } + } + +#ifdef AVB_FEATURE_GSTREAMER + // If we're supporting the interface modules which use GStreamer, + // initialize GStreamer here to avoid errors. + gst_init(0, NULL); +#endif + + // Open all streams + for (i1 = 0; i1 < tlCount; i1++) { + printf("Opening: %s\n", tlIniList[i1]); + tlHandleList[i1] = openavbTLOpen(); + } + + // Parse ini and configure all streams + for (i1 = 0; i1 < tlCount; i1++) { + printf("Configuring: %s\n", tlIniList[i1]); + openavb_tl_cfg_t cfg; + openavb_tl_cfg_name_value_t NVCfg; + + openavbTLInitCfg(&cfg); + memset(&NVCfg, 0, sizeof(NVCfg)); + + if (!openavbTLReadIniFileOsal(tlHandleList[i1], tlIniList[i1], &cfg, &NVCfg)) { + printf("Error reading ini file: %s\n", tlIniList[i1]); + osalAVBFinalize(); + exit(-1); + } + if (!openavbTLConfigure(tlHandleList[i1], &cfg, &NVCfg)) { + printf("Error configuring: %s\n", tlIniList[i1]); + osalAVBFinalize(); + exit(-1); + } + + int i2; + for (i2 = 0; i2 < NVCfg.nLibCfgItems; i2++) { + free(NVCfg.libCfgNames[i2]); + free(NVCfg.libCfgValues[i2]); + } + } + + if (!optInteractive) { + // Non-interactive mode + // Run the streams + for (i1 = 0; i1 < tlCount; i1++) { + printf("Starting: %s\n", tlIniList[i1]); + openavbTLRun(tlHandleList[i1]); + } + + while (bRunning) { + sleep(1); + } + + for (i1 = 0; i1 < tlCount; i1++) { + if (tlHandleList[i1] && openavbTLIsRunning(tlHandleList[i1])) { + printf("Stopping: %s\n", tlIniList[i1]); + openavbTLStop(tlHandleList[i1]); + } + } + } + else { + // Interactive mode + + openavbTlHarnessMenu(); + while (bRunning) { + char buf[16]; + printf("> "); + if (fgets(buf, sizeof(buf), stdin) == NULL) { + openavbTlHarnessMenu(); + continue; + } + + switch (buf[0]) { + case 's': + // Start all streams + { + int i1; + for (i1 = 0; i1 < tlCount; i1++) { + if (tlHandleList[i1] && !openavbTLIsRunning(tlHandleList[i1])) { + printf("Starting: %s\n", tlIniList[i1]); + openavbTLRun(tlHandleList[i1]); + } + } + } + break; + case 't': + // Stop all streams + { + int i1; + for (i1 = 0; i1 < tlCount; i1++) { + if (tlHandleList[i1] && openavbTLIsRunning(tlHandleList[i1])) { + printf("Stopping: %s\n", tlIniList[i1]); + openavbTLStop(tlHandleList[i1]); + } + } + } + break; + case 'l': + // List all streams + { + int i1; + for (i1 = 0; i1 < tlCount; i1++) { + if (tlHandleList[i1] && openavbTLIsRunning(tlHandleList[i1])) { + printf("%02d: [Started] %s\n", i1, tlIniList[i1]); + } + else { + printf("%02d: [Stopped] %s\n", i1, tlIniList[i1]); + } + } + } + break; + case 'm': + // Display menu + openavbTlHarnessMenu(); + break; + case 'z': + // Stats + { + int i1; + for (i1 = 0; i1 < tlCount; i1++) { + if (tlHandleList[i1] && openavbTLIsRunning(tlHandleList[i1])) { + printf("%02d: [Started] %s\n", i1, tlIniList[i1]); + if (openavbTLGetRole(tlHandleList[i1]) == AVB_ROLE_TALKER) { + printf(" Talker totals: calls=%" PRIu64 ", frames=%" PRIu64 ", late=%" PRIu64 ", bytes=%" PRIu64 "\n", + openavbTLStat(tlHandleList[i1], TL_STAT_TX_CALLS), + openavbTLStat(tlHandleList[i1], TL_STAT_TX_FRAMES), + openavbTLStat(tlHandleList[i1], TL_STAT_TX_LATE), + openavbTLStat(tlHandleList[i1], TL_STAT_TX_BYTES)); + } + else if (openavbTLGetRole(tlHandleList[i1]) == AVB_ROLE_LISTENER) { + printf(" Listener totals: calls=%" PRIu64 ", frames=%" PRIu64 ", lost=%" PRIu64 ", bytes=%" PRIu64 "\n", + openavbTLStat(tlHandleList[i1], TL_STAT_RX_CALLS), + openavbTLStat(tlHandleList[i1], TL_STAT_RX_FRAMES), + openavbTLStat(tlHandleList[i1], TL_STAT_RX_LOST), + openavbTLStat(tlHandleList[i1], TL_STAT_RX_BYTES)); + } + } + else { + printf("%02d: [Stopped] %s\n", i1, tlIniList[i1]); + } + } + } + break; + case 'x': + // Exit + { + int i1; + for (i1 = 0; i1 < tlCount; i1++) { + if (tlHandleList[i1] && openavbTLIsRunning(tlHandleList[i1])) { + printf("Stopping: %s\n", tlIniList[i1]); + openavbTLStop(tlHandleList[i1]); + } + } + bRunning = FALSE; + } + break; + default: + // Check for number + { + if (isdigit(buf[0])) { + int idx = atoi(buf); + if (idx < tlCount) { + if (tlHandleList[idx] && openavbTLIsRunning(tlHandleList[idx])) { + // Stop the stream + printf("Stopping: %s\n", tlIniList[idx]); + openavbTLStop(tlHandleList[idx]); + } + else { + // Start the stream + printf("Starting: %s\n", tlIniList[idx]); + openavbTLRun(tlHandleList[idx]); + } + } + else { + openavbTlHarnessMenu(); + } + } + else { + openavbTlHarnessMenu(); + } + } + break; + } + + } + } + + // Close the streams + for (i1 = 0; i1 < tlCount; i1++) { + if (tlHandleList[i1]) { + printf("Stopping: %s\n", tlIniList[i1]); + + openavbTLClose(tlHandleList[i1]); + + tlHandleList[i1] = NULL; + } + } + + openavbTLCleanup(); + + for (i1 = 0; i1 < tlCount; i1++) { + if (tlIniList[i1]) { + free(tlIniList[i1]); + tlIniList[i1] = NULL; + } + } + + if (tlIniList) { + free(tlIniList); + tlIniList = NULL; + } + + if (tlHandleList) { + free(tlHandleList); + tlHandleList = NULL; + } + + if (optStreamAddr) { + free(optStreamAddr); + optStreamAddr = NULL; + } + +#ifdef AVB_FEATURE_GSTREAMER + // If we're supporting the interface modules which use GStreamer, + // De-initialize GStreamer to clean up resources. + gst_deinit(); +#endif + + osalAVBFinalize(); + AVB_TRACE_EXIT(AVB_TRACE_HOST); + exit(0); +} diff --git a/lib/avtp_pipeline/platform/Linux/avb_host/openavb_host.c b/lib/avtp_pipeline/platform/Linux/avb_host/openavb_host.c new file mode 100644 index 00000000..2a3f5cd4 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/avb_host/openavb_host.c @@ -0,0 +1,232 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Talker listener test host implementation. +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include "openavb_tl_pub.h" +#include "openavb_plugin.h" +#include "openavb_trace_pub.h" +#ifdef AVB_FEATURE_GSTREAMER +#include <gst/gst.h> +#endif + +#define AVB_LOG_COMPONENT "TL Host" +#include "openavb_log_pub.h" + +bool bRunning = TRUE; + +// Platform indendent mapping modules +extern bool openavbMapPipeInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); +extern bool openavbMapAVTPAudioInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); +extern bool openavbMapCtrlInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); +extern bool openavbMapH264Initialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); +extern bool openavbMapMjpegInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); +extern bool openavbMapMpeg2tsInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); +extern bool openavbMapNullInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); +extern bool openavbMapUncmpAudioInitialize(media_q_t *pMediaQ, openavb_map_cb_t *pMapCB, U32 inMaxTransitUsec); + +// Platform indendent interface modules +extern bool openavbIntfEchoInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfCtrlInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfLoggerInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfNullInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfToneGenInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfViewerInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); + +// Linux interface modules +extern bool openavbIntfAlsaInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfMjpegGstInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfMpeg2tsFileInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfMpeg2tsGstInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); +extern bool openavbIntfWavFileInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB); + +/*********************************************** + * Signal handler - used to respond to signals. + * Allows graceful cleanup. + */ +static void openavbTLSigHandler(int signal) +{ + AVB_TRACE_ENTRY(AVB_TRACE_HOST); + + if (signal == SIGINT) { + AVB_LOG_INFO("Host shutting down"); + bRunning = FALSE; + } + else if (signal == SIGUSR1) { + AVB_LOG_DEBUG("Waking up streaming thread"); + } + else { + AVB_LOG_ERROR("Unexpected signal"); + } + + AVB_TRACE_EXIT(AVB_TRACE_HOST); +} + + +/********************************************** + * main + */ +int main(int argc, char *argv[]) +{ + AVB_TRACE_ENTRY(AVB_TRACE_HOST); + + if (argc < 2) {fprintf(stderr, "error: usage is:\n\t%s inifile1 [infile2...]\n", argv[0]); + exit(-1); + } + + U32 tlCount = argc - 1; + tl_handle_t *tlHandleList = NULL; + int i1; + + osalAVBInitialize(); + + if (!openavbTLInitialize(tlCount)) { + AVB_LOG_ERROR("Unable to initialize talker listener library"); + osalAVBFinalize(); + exit(-1); + } + + // Setup signal handler + // We catch SIGINT and shutdown cleanly + bool err; + struct sigaction sa; + sa.sa_handler = openavbTLSigHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + err = sigaction(SIGINT, &sa, NULL); + if (err) + { + AVB_LOG_ERROR("Failed to setup SIGINT handler"); + osalAVBFinalize(); + exit(-1); + } + err = sigaction(SIGUSR1, &sa, NULL); + if (err) + { + AVB_LOG_ERROR("Failed to setup SIGUSR1 handler"); + osalAVBFinalize(); + exit(-1); + } + + registerStaticMapModule(openavbMapPipeInitialize); + registerStaticMapModule(openavbMapAVTPAudioInitialize); + registerStaticMapModule(openavbMapCtrlInitialize); + registerStaticMapModule(openavbMapH264Initialize); + registerStaticMapModule(openavbMapMjpegInitialize); + registerStaticMapModule(openavbMapMpeg2tsInitialize); + registerStaticMapModule(openavbMapNullInitialize); + registerStaticMapModule(openavbMapUncmpAudioInitialize); + + registerStaticIntfModule(openavbIntfEchoInitialize); + registerStaticIntfModule(openavbIntfCtrlInitialize); + registerStaticIntfModule(openavbIntfLoggerInitialize); + registerStaticIntfModule(openavbIntfNullInitialize); + //registerStaticIntfModule(openavbIntfToneGenInitialize); + registerStaticIntfModule(openavbIntfViewerInitialize); + registerStaticIntfModule(openavbIntfAlsaInitialize); + //registerStaticIntfModule(openavbIntfMjpegGstInitialize); // TODO_OPENAVB : Not sure why MJPEG is having an issue linking GST RTP + registerStaticIntfModule(openavbIntfMpeg2tsFileInitialize); + registerStaticIntfModule(openavbIntfMpeg2tsGstInitialize); + registerStaticIntfModule(openavbIntfWavFileInitialize); + + tlHandleList = calloc(1, sizeof(tl_handle_t) * tlCount); + + // Open all streams + for (i1 = 0; i1 < tlCount; i1++) { + tlHandleList[i1] = openavbTLOpen(); + } + + // Parse ini and configure all streams + for (i1 = 0; i1 < tlCount; i1++) { + openavb_tl_cfg_t cfg; + openavb_tl_cfg_name_value_t NVCfg; + + openavbTLInitCfg(&cfg); + memset(&NVCfg, 0, sizeof(NVCfg)); + + if (!openavbTLReadIniFileOsal(tlHandleList[i1], argv[i1 + 1], &cfg, &NVCfg)) { + AVB_LOGF_ERROR("Error reading ini file: %s\n", argv[i1 + 1]); + osalAVBFinalize(); + exit(-1); + } + if (!openavbTLConfigure(tlHandleList[i1], &cfg, &NVCfg)) { + AVB_LOGF_ERROR("Error configuring: %s\n", argv[i1 + 1]); + osalAVBFinalize(); + exit(-1); + } + + int i2; + for (i2 = 0; i2 < NVCfg.nLibCfgItems; i2++) { + free(NVCfg.libCfgNames[i2]); + free(NVCfg.libCfgValues[i2]); + } + } + +#ifdef AVB_FEATURE_GSTREAMER + // If we're supporting the interface modules which use GStreamer, + // initialize GStreamer here to avoid errors. + gst_init(0, NULL); +#endif + + for (i1 = 0; i1 < tlCount; i1++) { + openavbTLRun(tlHandleList[i1]); + } + + while (bRunning) { + sleep(1); + } + + for (i1 = 0; i1 < tlCount; i1++) { + openavbTLStop(tlHandleList[i1]); + } + + for (i1 = 0; i1 < tlCount; i1++) { + openavbTLClose(tlHandleList[i1]); + } + + openavbTLCleanup(); + +#ifdef AVB_FEATURE_GSTREAMER + // If we're supporting the interface modules which use GStreamer, + // De-initialize GStreamer to clean up resources. + gst_deinit(); +#endif + + osalAVBFinalize(); + + AVB_TRACE_EXIT(AVB_TRACE_HOST); + exit(0); +} diff --git a/lib/avtp_pipeline/platform/Linux/avtp/openavb_avtp_time_osal.c b/lib/avtp_pipeline/platform/Linux/avtp/openavb_avtp_time_osal.c new file mode 100644 index 00000000..d923a1e8 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/avtp/openavb_avtp_time_osal.c @@ -0,0 +1,34 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Avtp Time implementation +*/ + diff --git a/lib/avtp_pipeline/platform/Linux/endpoint/CMakeLists.txt b/lib/avtp_pipeline/platform/Linux/endpoint/CMakeLists.txt new file mode 100644 index 00000000..c993ded2 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/endpoint/CMakeLists.txt @@ -0,0 +1,38 @@ +# CORE_TODO: This build logic may have been better in the primary Linux CMakeList.txt however some dependany issue occurs when doing so +# related copying the kernel files for FQTSS. Basically the FQTSS custom commands for copy files was not engaged. + +# Places to get include files +include_directories( + ${AVB_OSAL_DIR}/endpoint + ${AVB_OSAL_DIR}/maap + ${AVB_OSAL_DIR}/tl + ${AVB_SRC_DIR}/maap + ${AVB_SRC_DIR}/endpoint + ${AVB_SRC_DIR}/srp + ${AVB_SRC_DIR}/tl + ${AVB_OSAL_DIR}/fqtss/qmgr + ${AVB_OSAL_DIR}/srp + ) + +SET ( SRC_FILES "" ) +add_library (rawsock ${AVB_OSAL_DIR}/rawsock/openavb_rawsock.c) +add_subdirectory ( ${AVB_SRC_DIR}/maap ${CMAKE_BINARY_DIR}/maap ) +add_subdirectory ( ${AVB_SRC_DIR}/srp ${CMAKE_BINARY_DIR}/srp ) +add_subdirectory ( ${AVB_SRC_DIR}/endpoint ${CMAKE_BINARY_DIR}/endpoint ) +add_library ( endpoint ${SRC_FILES} ) +set ( AVB_ENDPOINT_LIBRARIES avbTl pthread rt m ) +if ( AVB_FEATURE_FQTSS ) + # set define for ifdefs + set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAVB_FEATURE_FQTSS=1" ) + # and add library to link list + set ( AVB_ENDPOINT_LIBRARIES "${AVB_ENDPOINT_LIBRARIES}" qmgr ) +endif ( AVB_FEATURE_FQTSS ) +target_link_libraries (endpoint ${AVB_ENDPOINT_LIBRARIES} ) +add_executable ( + openavb_endpoint + ${AVB_OSAL_DIR}/endpoint/openavb_endpoint_osal.c + ) +target_link_libraries (openavb_endpoint endpoint ) +install ( TARGETS openavb_endpoint RUNTIME DESTINATION ${AVB_INSTALL_BIN_DIR} ) + + diff --git a/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_cfg.c b/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_cfg.c new file mode 100644 index 00000000..d9bb80a7 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_cfg.c @@ -0,0 +1,224 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Reads the .ini file for an enpoint and +*/ + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include "openavb_endpoint.h" +#include "openavb_endpoint_cfg.h" +#include "openavb_trace.h" +#include "openavb_rawsock.h" +#include "ini.h" + +#define AVB_LOG_COMPONENT "Endpoint" +#include "openavb_log.h" + +// macro to make matching names easier +#define MATCH(A, B)(strcasecmp((A), (B)) == 0) +#define MATCH_FIRST(A, B)(strncasecmp((A), (B), strlen(B)) == 0) + +static void cfgValErr(const char *section, const char *name, const char *value) +{ + AVB_LOGF_ERROR("Invalid value: section=%s, name=%s, value=%s", + section, name, value); +} + +static int cfgCallback(void *user, const char *section, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + if (!user || !section || !name || !value) { + AVB_LOG_ERROR("Config: invalid arguments passed to callback"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return 0; + } + + openavb_endpoint_cfg_t *pCfg = (openavb_endpoint_cfg_t*)user; + + AVB_LOGF_DEBUG("name=[%s] value=[%s]", name, value); + + bool valOK = FALSE; + char *pEnd; + + if (MATCH(section, "network")) + { + if (MATCH(name, "ifname")) + { + if_info_t ifinfo; + if (openavbCheckInterface(value, &ifinfo)) { + strncpy(pCfg->ifname, value, IFNAMSIZ - 1); + memcpy(pCfg->ifmac, &ifinfo.mac, ETH_ALEN); + pCfg->ifindex = ifinfo.index; + pCfg->mtu = ifinfo.mtu; + valOK = TRUE; + } + } + else if (MATCH(name, "link_kbit")) { + errno = 0; + pCfg->link_kbit = strtoul(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0) + valOK = TRUE; + } + else if (MATCH(name, "nsr_kbit")) { + errno = 0; + pCfg->nsr_kbit = strtoul(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0) + valOK = TRUE; + } + else { + // unmatched item, fail + AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return 0; + } + } + else if (MATCH(section, "ptp")) + { + if (MATCH(name, "start_options")) { + pCfg->ptp_start_opts = strdup(value); + valOK = TRUE; + } + else { + // unmatched item, fail + AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return 0; + } + } + else if (MATCH(section, "fqtss")) + { + if (MATCH(name, "mode")) { + errno = 0; + pCfg->fqtss_mode = strtoul(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0) + valOK = TRUE; + } + else { + // unmatched item, fail + AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return 0; + } + } + else if (MATCH(section, "srp")) + { + if (MATCH(name, "preconfigured")) { + errno = 0; + unsigned temp = strtoul(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0) { + valOK = TRUE; + if (temp == 1) + pCfg->noSrp = TRUE; + else + pCfg->noSrp = FALSE; + } + } + else if (MATCH(name, "gptp_asCapable_not_required")) { + errno = 0; + unsigned temp = strtoul(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0) { + valOK = TRUE; + if (temp == 1) + pCfg->bypassAsCapableCheck = TRUE; + else + pCfg->bypassAsCapableCheck = FALSE; + } + } + else { + // unmatched item, fail + AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return 0; + } + } + else { + // unmatched item, fail + AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return 0; + } + + if (!valOK) { + cfgValErr(section, name, value); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return 1; // OK +} + +// Parse ini file, and create config data +// +int openavbReadConfig(const char *ini_file, openavb_endpoint_cfg_t *pCfg) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + // defaults - most are handled by setting everything to 0 + memset(pCfg, 0, sizeof(openavb_endpoint_cfg_t)); + pCfg->fqtss_mode = -1; + + int result = ini_parse(ini_file, cfgCallback, pCfg); + if (result < 0) { + AVB_LOGF_ERROR("Couldn't parse INI file: %s", ini_file); + return -1; + } + if (result > 0) { + AVB_LOGF_ERROR("Error in INI file: %s, line %d", ini_file, result); + return -1; + } + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + + // Yay, we did it. + return 0; +} + +// Clean up any configuration-related stuff +// +void openavbUnconfigure(openavb_endpoint_cfg_t *pCfg) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + if (pCfg) { + if (pCfg->ptp_start_opts) { + free(pCfg->ptp_start_opts); + pCfg->ptp_start_opts = NULL; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); +} diff --git a/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_cfg.h b/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_cfg.h new file mode 100644 index 00000000..6dcbc8cd --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_cfg.h @@ -0,0 +1,63 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Declarations used by the endpoint configuration +* +* Defines the endpoint configuration data, and the functions +* to read the configuration data. +*/ + +#ifndef AVB_ENDPOINT_CONFIG_H +#define AVB_ENDPOINT_CONFIG_H + +#include "openavb_types.h" +#include "openavb_srp_api.h" +#include "net/if.h" + +#define DEFAULT_INI_FILE "endpoint.ini" + +typedef struct { + char ifname[IFNAMSIZ]; + U8 ifmac[ETH_ALEN]; + char *ptp_start_opts; + int ifindex; + unsigned link_kbit; + unsigned nsr_kbit; + unsigned mtu; + unsigned fqtss_mode; + bool noSrp; + bool bypassAsCapableCheck; +} openavb_endpoint_cfg_t; + +int openavbReadConfig(const char *inifile, openavb_endpoint_cfg_t *pCfg); +void openavbUnconfigure(openavb_endpoint_cfg_t *pCfg); + +#endif // AVB_ENDPOINT_CONFIG_H diff --git a/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_client_osal.c b/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_client_osal.c new file mode 100644 index 00000000..71878f56 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_client_osal.c @@ -0,0 +1,179 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#ifndef OPENAVB_ENDPOINT_CLIENT_OSAL_C +#define OPENAVB_ENDPOINT_CLIENT_OSAL_C + +static void socketClose(int h) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + if (h != AVB_ENDPOINT_HANDLE_INVALID) { + close(h); + } + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); +} + +static bool openavbEptClntSendToServer(int h, openavbEndpointMessage_t *msg) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + if (!msg || h == AVB_ENDPOINT_HANDLE_INVALID) { + AVB_LOG_ERROR("Client send: invalid argument passed"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + + ssize_t nWrite = write(h, msg, OPENAVB_ENDPOINT_MSG_LEN); + AVB_LOGF_VERBOSE("Sent message, len=%d, nWrite=%d", OPENAVB_ENDPOINT_MSG_LEN, nWrite); + + if (nWrite < OPENAVB_ENDPOINT_MSG_LEN) { + if (nWrite < 0) { + AVB_LOGF_ERROR("Client failed to write socket: %s", strerror(errno)); + } + else if (nWrite == 0) { + AVB_LOG_ERROR("Client send: socket closed unexpectedly"); + } + else { + AVB_LOG_ERROR("Client send: short write"); + } + socketClose(h); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return TRUE; +} + +int openavbEptClntOpenSrvrConnection(tl_state_t *pTLState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + struct sockaddr_un server; + server.sun_family = AF_UNIX; + snprintf(server.sun_path, UNIX_PATH_MAX, AVB_ENDPOINT_UNIX_PATH); + + int h = socket(AF_UNIX, SOCK_STREAM, 0); + if (h < 0) { + AVB_LOGF_ERROR("Failed to open socket: %s", strerror(errno)); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return AVB_ENDPOINT_HANDLE_INVALID; + } + + AVB_LOGF_DEBUG("Connecting to %s", server.sun_path); + int rslt = connect(h, (struct sockaddr*)&server, sizeof(struct sockaddr_un)); + if (rslt < 0) { + AVB_LOGF_ERROR("Failed to connect socket: %s", strerror(errno)); + socketClose(h); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return AVB_ENDPOINT_HANDLE_INVALID; + } + + AVB_LOG_DEBUG("connected to endpoint"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return h; +} + +void openavbEptClntCloseSrvrConnection(int h) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + socketClose(h); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); +} + +bool openavbEptClntService(int h, int timeout) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + bool rc = FALSE; + + if (h == AVB_ENDPOINT_HANDLE_INVALID) { + AVB_LOG_ERROR("Client service: invalid socket"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + + struct pollfd fds[1]; + memset(fds, 0, sizeof(struct pollfd)); + fds[0].fd = h; + fds[0].events = POLLIN; + + AVB_LOG_VERBOSE("Waiting for event..."); + int pRet = poll(fds, 1, timeout); + + if (pRet == 0) { + AVB_LOG_VERBOSE("Poll timeout"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return TRUE; + } + else if (pRet < 0) { + if (errno == EINTR) { + AVB_LOG_VERBOSE("Poll interrupted"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return TRUE; + } + else { + AVB_LOGF_ERROR("Poll error: %s", strerror(errno)); + } + } + else { + AVB_LOGF_DEBUG("Poll returned %d events", pRet); + // only one fd, so it's readable. + openavbEndpointMessage_t msgBuf; + memset(&msgBuf, 0, OPENAVB_ENDPOINT_MSG_LEN); + ssize_t nRead = read(h, &msgBuf, OPENAVB_ENDPOINT_MSG_LEN); + + if (nRead < OPENAVB_ENDPOINT_MSG_LEN) { + // sock closed + if (nRead == 0) { + AVB_LOG_ERROR("Socket closed unexpectedly"); + } + else if (nRead < 0) { + AVB_LOGF_ERROR("Socket read error: %s", strerror(errno)); + } + else { + AVB_LOG_ERROR("Socket read to short"); + } + socketClose(h); + } + else { + // got a message + if (openavbEptClntReceiveFromServer(h, &msgBuf)) { + rc = TRUE; + } + else { + AVB_LOG_ERROR("Invalid message received"); + socketClose(h); + } + } + } + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return rc; +} + +#endif // OPENAVB_ENDPOINT_CLIENT_OSAL_C diff --git a/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_osal.c b/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_osal.c new file mode 100644 index 00000000..f10b5aad --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_osal.c @@ -0,0 +1,173 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#include <stdlib.h> +#include <string.h> +#include "openavb_platform.h" +#include "openavb_trace.h" +#include "openavb_endpoint.h" +#include "openavb_endpoint_cfg.h" +#include "openavb_srp.h" + +#define AVB_LOG_COMPONENT "Endpoint" +#include "openavb_pub.h" +#include "openavb_log.h" + +// the following are from openavb_endpoint.c +extern openavb_endpoint_cfg_t x_cfg; +extern bool endpointRunning; + +static void sigHandler(int signal) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + AVB_LOGF_DEBUG("Received signal: %d", signal); + // SIGINT means we should shut ourselves down + if (signal == SIGINT) + endpointRunning = FALSE; + else if (signal == SIGUSR2) + openavbSrpLogAllStreams(); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); +} + +inline int startPTP(void) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + // make sure ptp, a seperate process, starts and is using the same interface as endpoint + int retVal = 0; + char ptpCmd[80]; + memset(ptpCmd, 0, 80); + snprintf(ptpCmd, 80, "./openavb_gptp %s -i %s &", x_cfg.ptp_start_opts, x_cfg.ifname); + AVB_LOGF_INFO("PTP start command: %s", ptpCmd); + if (system(ptpCmd) != 0) { + AVB_LOG_ERROR("PTP failed to start - Exiting"); + retVal = -1; + } + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return retVal; +} + +inline int stopPTP(void) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + int retVal = 0; + if (system("killall -s SIGINT openavb_gptp") != 0) { + retVal = -1; + } + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return retVal; +} + +/************************************************************* + * + * main() - sets up signal handlers, and reads configuration file + * + */ +int main(int argc, char* argv[]) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + LOG_EAVB_CORE_VERSION(); + + char *inifile = NULL; + int exitVal = -1; + + avbLogInit(); + + do { + + switch (argc) { + case 1: + inifile = DEFAULT_INI_FILE; + break; + case 2: + inifile = argv[1]; + break; + default: + fprintf(stderr, "Error: usage is:\n\t%s [endpoint.ini]\n\n", argv[0]); + break;; + } + + // Ensure that we're running as root + // (need to be root to use raw sockets) + uid_t euid = geteuid(); + if (euid != (uid_t)0) { + fprintf(stderr, "Error: %s needs to run as root\n\n", argv[0]); + break; + } + + // Setup signal handlers + // + // We catch SIGINT and shutdown cleanly + endpointRunning = TRUE; + + struct sigaction sa; + sa.sa_handler = sigHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; // not SA_RESTART + + if(sigaction(SIGUSR2, &sa, NULL) == -1) + { + AVB_LOG_ERROR("Failed to setup USR2 signal handler"); + } + + if (sigaction(SIGINT, &sa, NULL) == -1) + { + AVB_LOG_ERROR("Failed to setup signal handler"); + break; + } + + // Parse our INI file + memset(&x_cfg, 0, sizeof(openavb_endpoint_cfg_t)); + AVB_LOGF_INFO("Reading configuration: %s", inifile); + + if (openavbReadConfig(inifile, &x_cfg) == 0) { + if(avbEndpointLoop() < 0) + break; + }else { + AVB_LOG_ERROR("Failed to read configuration"); + } + + AVB_LOG_INFO("Shutting down"); + + openavbUnconfigure(&x_cfg); + + exitVal = 0; + } while (0); + + avbLogExit(); + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + exit(exitVal); +} diff --git a/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_osal.h b/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_osal.h new file mode 100644 index 00000000..8ab9f545 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_osal.h @@ -0,0 +1,58 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#ifndef OSAL_ENDPOINT_H +#define OSAL_ENDPOINT_H + +// should only be included from openavb_endpoint.h + +#include <linux/un.h> +#include <net/if.h> +#include <unistd.h> +#include <signal.h> + +typedef struct { + openavbEndpointMsgType_t type; + AVBStreamID_t streamID; + union { + // Client messages + openavbEndpointParams_TalkerRegister_t talkerRegister; + openavbEndpointParams_ListenerAttach_t listenerAttach; + openavbEndpointParams_ClientStop_t clientStop; + openavbEndpointParams_VersionRequest_t versionRequest; + + // Server messages + openavbEndpointParams_TalkerCallback_t talkerCallback; + openavbEndpointParams_ListenerCallback_t listenerCallback; + openavbEndpointParams_VersionCallback_t versionCallback; + } params; +} openavbEndpointMessage_t; + +#endif // OSAL_ENDPOINT_H diff --git a/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_server_osal.c b/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_server_osal.c new file mode 100644 index 00000000..829f7872 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/endpoint/openavb_endpoint_server_osal.c @@ -0,0 +1,254 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#ifndef OPENAVB_ENDPOINT_SERVER_OSAL_C +#define OPENAVB_ENDPOINT_SERVER_OSAL_C + +#define AVB_ENDPOINT_LISTEN_FDS 0 // first fds, was last MAX_AVB_STREAMS +#define SOCK_INVALID (-1) +#define POLL_FD_COUNT ((MAX_AVB_STREAMS) + 1) + +static int lsock = SOCK_INVALID; +static struct pollfd fds[POLL_FD_COUNT]; +static struct sockaddr_un serverAddr; + +static void socketClose(int h) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + if (h < 0 || h >= POLL_FD_COUNT) { + AVB_LOG_ERROR("Closing socket; invalid handle"); + } + else { + openavbEptSrvrCloseClientConnection(h); + close(fds[h].fd); + fds[h].fd = SOCK_INVALID; + fds[h].events = 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); +} + +static bool openavbEptSrvrSendToClient(int h, openavbEndpointMessage_t *msg) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + + if (h < 0 || h >= POLL_FD_COUNT) { + AVB_LOG_ERROR("Sending message; invalid handle"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + if (!msg) { + AVB_LOG_ERROR("Sending message; invalid argument passed"); + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; + } + + int csock = fds[h].fd; + if (csock == SOCK_INVALID) { + AVB_LOG_ERROR("Socket closed unexpectedly"); + return FALSE; + } + + ssize_t nWrite = write(csock, msg, OPENAVB_ENDPOINT_MSG_LEN); + AVB_LOGF_VERBOSE("Sent message, len=%d, nWrite=%d", OPENAVB_ENDPOINT_MSG_LEN, nWrite); + if (nWrite < OPENAVB_ENDPOINT_MSG_LEN) { + if (nWrite < 0) { + AVB_LOGF_ERROR("Failed to write socket: %s", strerror(errno)); + } + else if (nWrite == 0) { + AVB_LOG_ERROR("Socket closed unexpectedly"); + } + else { + AVB_LOG_ERROR("Socket write too short"); + } + socketClose(h); + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + return FALSE; + } + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return TRUE; +} + +bool openavbEndpointServerOpen(void) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + int i; + + for (i=0; i < POLL_FD_COUNT; i++) { + fds[i].fd = SOCK_INVALID; + fds[i].events = 0; + } + + lsock = socket(AF_UNIX, SOCK_STREAM, 0); + if (lsock < 0) { + AVB_LOGF_ERROR("Failed to open socket: %s", strerror(errno)); + goto error; + } + // serverAddr is file static + serverAddr.sun_family = AF_UNIX; + snprintf(serverAddr.sun_path, UNIX_PATH_MAX, AVB_ENDPOINT_UNIX_PATH); + + int rslt = bind(lsock, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr_un)); + if (rslt != 0) { + AVB_LOGF_ERROR("Failed to create %s: %s", serverAddr.sun_path, strerror(errno)); + AVB_LOG_WARNING("** If endpoint process crashed, run the cleanup script **"); + goto error; + } + + rslt = listen(lsock, 5); + if (rslt != 0) { + AVB_LOGF_ERROR("Failed to listen on socket: %s", strerror(errno)); + goto error; + } + AVB_LOGF_DEBUG("Listening on socket: %s", serverAddr.sun_path); + + fds[AVB_ENDPOINT_LISTEN_FDS].fd = lsock; + fds[AVB_ENDPOINT_LISTEN_FDS].events = POLLIN; + + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return TRUE; + + error: + if (lsock >= 0) { + close(lsock); + lsock = -1; + } + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); + return FALSE; +} + +void openavbEptSrvrService(void) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + struct sockaddr_un addrClient; + socklen_t lenAddr; + int i, j; + int csock; + + int nfds = POLL_FD_COUNT; + int pRet; + + AVB_LOG_VERBOSE("Waiting for event..."); + pRet = poll(fds, nfds, -1); + + if (pRet == 0) { + AVB_LOG_VERBOSE("poll timeout"); + } + else if (pRet < 0) { + if (errno == EINTR) { + AVB_LOG_VERBOSE("Poll interrupted"); + } + else { + AVB_LOGF_ERROR("Poll error: %s", strerror(errno)); + } + } + else { + AVB_LOGF_VERBOSE("Poll returned %d events", pRet); + for (i=0; i<nfds; i++) { + if (fds[i].revents != 0) { + AVB_LOGF_VERBOSE("%d sock=%d, event=0x%x, revent=0x%x", i, fds[i].fd, fds[i].events, fds[i].revents); + + if (i == AVB_ENDPOINT_LISTEN_FDS) { + // listen sock - indicates new connection from client + lenAddr = sizeof(addrClient); + csock = accept(lsock, (struct sockaddr*)&addrClient, &lenAddr); + if (csock < 0) { + AVB_LOGF_ERROR("Failed to accept connection: %s", strerror(errno)); + } + else { + for (j = 0; j < POLL_FD_COUNT; j++) { + if (fds[j].fd == SOCK_INVALID) { + fds[j].fd = csock; + fds[j].events = POLLIN; + break; + } + } + if (j >= POLL_FD_COUNT) { + AVB_LOG_ERROR("Too many client connections"); + close(csock); + } + } + } + else { + csock = fds[i].fd; + openavbEndpointMessage_t msgBuf; + memset(&msgBuf, 0, OPENAVB_ENDPOINT_MSG_LEN); + ssize_t nRead = read(csock, &msgBuf, OPENAVB_ENDPOINT_MSG_LEN); + AVB_LOGF_VERBOSE("Socket read h=%d,fd=%d: read=%d, expect=%d", i, csock, nRead, OPENAVB_ENDPOINT_MSG_LEN); + + if (nRead < OPENAVB_ENDPOINT_MSG_LEN) { + // sock closed + if (nRead == 0) { + AVB_LOGF_DEBUG("Socket closed, h=%d", i); + } + else if (nRead < 0) { + AVB_LOGF_ERROR("Socket read, h=%d: %s", i, strerror(errno)); + } + else { + AVB_LOGF_ERROR("Short read, h=%d", i); + } + socketClose(i); + } + else { + // got a message + if (!openavbEptSrvrReceiveFromClient(i, &msgBuf)) { + AVB_LOG_ERROR("Failed to handle message"); + socketClose(i); + } + } + } + } + } + } + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); +} + +void openavbEndpointServerClose(void) +{ + AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); + int i; + for (i = 0; i < POLL_FD_COUNT; i++) { + if (fds[i].fd != SOCK_INVALID) { + close(fds[i].fd); + } + } + if (lsock != SOCK_INVALID) { + close(lsock); + } + + if (unlink(serverAddr.sun_path) != 0) { + AVB_LOGF_ERROR("Failed to unlink %s: %s", serverAddr.sun_path, strerror(errno)); + } + AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); +} + +#endif // OPENAVB_ENDPOINT_SERVER_OSAL_C diff --git a/lib/avtp_pipeline/platform/Linux/generic.cmake b/lib/avtp_pipeline/platform/Linux/generic.cmake new file mode 100644 index 00000000..588b68d0 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/generic.cmake @@ -0,0 +1,15 @@ +# generic build settings +# just builds linux version of stack on host machine + +# Label for messages / build configuration +set ( OPENAVB_HAL "generic" ) +set ( OPENAVB_OSAL "Linux" ) +set ( OPENAVB_TCAL "GNU" ) +set ( OPENAVB_PLATFORM "${OPENAVB_HAL}-${OPENAVB_OSAL}" ) + +# point to our "proxy" linux/ptp_clock.h include file +# which includes /usr/include/linux/ptp_clock.h +# and adding missing defines just to make everything compile +include_directories ( platform/generic/include ) + +set ( PLATFORM_DEFINE "AVB_DELAY_TWEAK_USEC=45" ) diff --git a/lib/avtp_pipeline/platform/Linux/intf_alsa/CMakeLists.txt b/lib/avtp_pipeline/platform/Linux/intf_alsa/CMakeLists.txt new file mode 100644 index 00000000..4d4747f3 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_alsa/CMakeLists.txt @@ -0,0 +1,10 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_OSAL_DIR}/intf_alsa/openavb_intf_alsa.c + PARENT_SCOPE +) + +# Need include and link directories for ALSA +SET (INTF_INCLUDE_DIR ${INTF_INCLUDE_DIR} ${ALSA_INCLUDE_DIRS} PARENT_SCOPE) +SET (INTF_LIBRARY_DIR ${INTF_LIBRARY_DIR} ${ALSA_LIBRARY_DIRS} PARENT_SCOPE) +SET (INTF_LIBRARY ${ALSA_LIBRARIES} pthread rt PARENT_SCOPE) + diff --git a/lib/avtp_pipeline/platform/Linux/intf_alsa/aaf_file_talker.ini b/lib/avtp_pipeline/platform/Linux/intf_alsa/aaf_file_talker.ini new file mode 100644 index 00000000..ffa339a5 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_alsa/aaf_file_talker.ini @@ -0,0 +1,120 @@ +##################################################################### +# General Talker configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 00:25:64:48:ca:a8 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 2 + +# dest_addr: destination multicast address for the stream. +# +# If using SRP and MAAP, dynamic destination addresses are generated +# automatically by the talker and passed to the listner, and don't +# need to be configured. +# +# Without MAAP, locally administered (static) addresses must be +# configured. Thouse addresses are in the range of: +# 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically use :00 for the first stream, :01 for the second, etc. +# +# When SRP is being used the static destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set (to the same value) in both the talker and listener. +# +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1000 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +#report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_aaf_audio.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapAVTPAudioInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +# map_nv_tx_rate: Transmit rate. +# This must be set for the AAF audio mapping module. +map_nv_tx_rate = 4000 + +# map_nv_packing_factor: Multiple of how many packets of audio frames to place in a media queue item. +map_nv_packing_factor = 32 + +# map_nv_sparse_mode: if set to 0 put presentation time in each packet. +# Set to 1 to use sparse mode - valid timestamp in every 8th packet. +# Default value used (0) when commented. +map_nv_sparse_mode = 0 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +# intf_fn: The name of the initialize function in the interface. +intf_lib = ./libopenavb_intf_wav_file.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfWavFileInitialize + +# intf_nv_file_name: The fully qualified file name. +intf_nv_file_name = test.wav diff --git a/lib/avtp_pipeline/platform/Linux/intf_alsa/aaf_listener.ini b/lib/avtp_pipeline/platform/Linux/intf_alsa/aaf_listener.ini new file mode 100644 index 00000000..4c4fd179 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_alsa/aaf_listener.ini @@ -0,0 +1,129 @@ +##################################################################### +# Configuration for ALSA and the uncompressed audio mapping module +##################################################################### + +##################################################################### +# General Listener configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = listener + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +stream_addr = 84:7e:40:2b:63:f4 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 2 + +# dest_addr: When SRP is being used the destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set in both side the talker and listener. +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# At this time they need to be locally administered and must be in the range +# of 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically :00 for the first stream, :01 for the second, etc. +#dest_addr = 91:e0:f0:00:fe:00 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 50000 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +#report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_aaf_audio.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapAVTPAudioInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 32 + +# map_nv_tx_rate: Transmit rate. +# This must be set for the AAF audio mapping module. +map_nv_tx_rate = 4000 + +# map_nv_packing_factor: Multiple of how many packets of audio frames to place in a media queue item. +# If sparse timestamping mode is enabled the listener should set here one of the possible +# packing factors values to be sure that proper presentation time is put into media queue item. +# Possible values are: 1, 2, 4, 8, 16, 24, 32, 40, 48, (+ 8)... +map_nv_packing_factor = 32 + +# map_nv_sparse_mode: if set to 0 presentation time should be +# valid in each packet. Set to 1 to use sparse mode - presentation +# time should be valid in every 8th packet. +map_nv_sparse_mode = 0 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +# intf_fn: The name of the initialize function in the interface. +intf_lib = ./libopenavb_intf_alsa.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfAlsaInitialize + +# intf_nv_ignore_timestamp: If set the listener will ignore the timestamp on media queue items. +intf_nv_ignore_timestamp = 1 + +# intf_nv_device_name: ALSA device name. Commonly "default" or "plug:dmix" +intf_nv_device_name = default + +# intf_nv_audio_rate: Valid values that are supported by AAF are: +# 8000, 16000, 32000, 44100, 48000, 88200, 96000, 176400 and 192000 +intf_nv_audio_rate = 48000 + +# intf_nv_audio_bit_depth: Valid values that are supported by AAF are: +# 8, 16, 32 +intf_nv_audio_bit_depth = 32 + +# intf_nv_audio_channels: Valid values that are supported by AAF are: +# 1 - 8 +intf_nv_audio_channels = 2 + +# intf_nv_allow_resampling: 0 = disable software resampling. 1 = allow software resampling. Default is disable. +intf_nv_allow_resampling = 1 + +# intf_nv_start_threshold_periods: The number of period to wait before starting playback. The larger the value to great +# the latency. The small the number the great chance for a buffer underrun. A good range is 1 - 5. +intf_nv_start_threshold_periods = 3 + +# intf_nv_period_time: the number of microseconds which should be set to unify latency between different platforms. +# This influence ALSA's period_time and period_size parameters and the result value should be the nearest possible. +# Initial playback latency is equal intf_nv_start_threshold_periods * intf_nv_period_time. If not set internal defaults are used. +# intf_nv_period_time = 31250 diff --git a/lib/avtp_pipeline/platform/Linux/intf_alsa/aaf_talker.ini b/lib/avtp_pipeline/platform/Linux/intf_alsa/aaf_talker.ini new file mode 100644 index 00000000..40bb3e38 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_alsa/aaf_talker.ini @@ -0,0 +1,155 @@ +##################################################################### +# General Talker configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 00:25:64:48:ca:a8 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 2 + +# dest_addr: destination multicast address for the stream. +# +# If using SRP and MAAP, dynamic destination addresses are generated +# automatically by the talker and passed to the listner, and don't +# need to be configured. +# +# Without MAAP, locally administered (static) addresses must be +# configured. Thouse addresses are in the range of: +# 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically use :00 for the first stream, :01 for the second, etc. +# +# When SRP is being used the static destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set (to the same value) in both the talker and listener. +# +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 50000 + +# max_transmit_deficit_usec: Allows setting the maximum packet transmit rate deficit that will +# be recovered when a talker falls behind. This is only used on a talker side. When a talker +# can not keep up with the specified transmit rate it builds up a deficit and will attempt to +# make up for this deficit by sending more packets. There is normally some variability in the +# transmit rate because of other demands on the system so this is expected. However, without this +# bounding value the deficit could grew too large in cases such where more streams are started +# than the system can support and when the number of streams is reduced the remaining streams +# will attempt to recover this deficit by sending packets at a higher rate. This can cause a problem +# at the listener side and significantly delay the recovery time before media playback will return +# to normal. Typically this value can be set to the expected buffer size (in usec) that listeners are +# expected to be buffering. For low latency solutions this is normally a small value. For non-live +# media playback such as video playback the listener side buffers can often be large enough to held many +# seconds of data. +max_transmit_deficit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +#report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_aaf_audio.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapAVTPAudioInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +# map_nv_tx_rate: Transmit rate. +# This must be set for the simple audio mapping module. +# The recommended values are: +# For audio sample rates which are a multiple of 8000hz: 8000 for class A, 4000 for class B +# For audio sample rates which are a multiple of 44100hz: 7350 for class A, 3675 for class B +map_nv_tx_rate = 4000 + +# map_nv_packing_factor: Each media queue item will hold data for this many packets +map_nv_packing_factor = 32 + +# map_nv_sparse_mode: if set to 0 put presentation time in each packet. +# Set to 1 to use sparse mode - valid timestamp in every 8th packet. +# Default value used (0) when commented. +map_nv_sparse_mode = 0 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +# intf_fn: The name of the initialize function in the interface. +intf_lib = ./libopenavb_intf_alsa.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfAlsaInitialize + +# intf_nv_device_name: ALSA device name. Commonly "default" or "plug:dmix" +# hw:CARD=Loopback,DEV=1 +intf_nv_device_name = default + +# intf_nv_audio_rate: Valid values that are supported by AAF are: +# 8000, 16000, 32000, 44100, 48000, 88200, 96000, 176400 and 192000 +intf_nv_audio_rate = 48000 + +# intf_nv_audio_bit_depth: Valid values that are supported by AAF are: +# 8, 16, 32 +intf_nv_audio_bit_depth = 32 + +# intf_nv_audio_channels: Valid values that are supported by AAF are: +# 1 - 8 +intf_nv_audio_channels = 2 + +# intf_nv_allow_resampling: 0 = disable software resampling. 1 = allow software resampling. Default is disable. +intf_nv_allow_resampling = 1 + diff --git a/lib/avtp_pipeline/platform/Linux/intf_alsa/alsa_intf.md b/lib/avtp_pipeline/platform/Linux/intf_alsa/alsa_intf.md new file mode 100644 index 00000000..cd97cf1a --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_alsa/alsa_intf.md @@ -0,0 +1,50 @@ +ALSA interface {#alsa_intf} +============== + +# Description + +ALSA interface module. An interface to connect AVTP streams to ALSA either as an audio source or sink. + +<br> +# Interface module configuration parameters + +Name | Description +--------------------------|--------------------------- +intf_nv_ignore_timestamp | If set to 1 timestamps will be ignored during \ + processing of frames. This also means stale (old) \ + Media Queue items will not be purged. +intf_nv_device_name |ALSA device name. Commonly "default" or "plug:dmix" +intf_nv_audio_rate |Audio rate, numberic values defined by \ + @ref avb_audio_rate_t +intf_nv_audio_bit_depth |Bit depth of audio, numeric values defined by \ + @ref avb_audio_bit_depth_t +intf_nv_audio_type |Type of data samples, possible values <ul><li>float \ + </li><li>sign</li><li>unsign</li><li>int</li><li> \ + uint</li></ul> +intf_nv_audio_endian |Data endianess possible values <ul><li>big</li><li> \ + little</li></ul> +intf_nv_audio_channels |Number of audio channels, numeric values should be \ + within range of values in @ref avb_audio_channels_t +intf_nv_allow_resampling |If 1 software resampling allowed, disallowed \ + otherwise (by default allowed) +intf_nv_start_threshold_periods|Playback start threshold measured in ALSA \ + periods (2 by default) +intf_nv_period_time |Approximate ALSA period duration in microseconds + +<br> +# Notes + +There are some parameters that have to be set during configuration of this +interface module and before configuring mapping: +* [AAF audio mapping](@ref aaf_audio_map) +* [Uncompressed audio mapping](@ref uncmp_audio_map) + +These parameters can be set in either: +* in the ini file (or available configuration system), so values will be parsed +in the intf_cfg_cb function, +* in the interface module initialization function where valid values can be +assigned directly + +Values assigned in the intf_cfg_cb function will override any values set in the +initialization function. + diff --git a/lib/avtp_pipeline/platform/Linux/intf_alsa/alsa_listener.ini b/lib/avtp_pipeline/platform/Linux/intf_alsa/alsa_listener.ini new file mode 100644 index 00000000..2b9eb256 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_alsa/alsa_listener.ini @@ -0,0 +1,145 @@ +##################################################################### +# Configuration for ALSA and the uncompressed audio mapping module +##################################################################### + +##################################################################### +# General Listener configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = listener + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 08:00:28:31:E6:6E +stream_addr = 84:7E:40:2C:8F:DE + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: When SRP is being used the destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set in both side the talker and listener. +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# At this time they need to be locally administered and must be in the range +# of 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically :00 for the first stream, :01 for the second, etc. +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +#max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +sr_class = A + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +raw_rx_buffers = 200 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_uncmp_audio.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapUncmpAudioInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 32 + +# map_nv_tx_rate: Transmit rate. +# This must be set for the uncompressed audio mapping module. +map_nv_tx_rate = 8000 + +# map_nv_packing_factor: Multiple of how many packets of audio frames to place in a media queue item. +# Note: Typically when decreasing the map_nv_tx_rate the packing factor will also be decreased since +# the number of frames per packet will be increasing. +# The ALSA interface module writes entire media queue items to the ALSA APIs. ALSA is much happier with +# larger blocks of writes otherwise buffer underruns can occur. Typically at least the number of frames greater +# than a period size. +map_nv_packing_factor = 256 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +# intf_fn: The name of the initialize function in the interface. +intf_lib = ./libopenavb_intf_alsa.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfAlsaInitialize + +# intf_nv_ignore_timestamp: If set the listener will ignore the timestamp on media queue items. +intf_nv_ignore_timestamp = 1 + +# intf_nv_device_name: ALSA device name. Commonly "default" or "plug:dmix" +intf_nv_device_name = default + +# intf_nv_audio_rate: Valid values that are supported by the Alsa, TI J5 and 61883-6 are: 32000, 44100, 48000, 88200 and 96000 +intf_nv_audio_rate = 48000 + +# intf_nv_audio_bit_depth: Valid values that are supported by the Alsa, TI J5 and 61883-6 are: 16 +# Note: Typically 20 and 24 bit is not supported in ALSA +intf_nv_audio_bit_depth = 16 + +# intf_nv_audio_channels: Valid values that are supported by the Alsa, TI J5 and 61883-6 are: 1 - 8 +intf_nv_audio_channels = 2 + +# intf_nv_allow_resampling: 0 = disable software resampling. 1 = allow software resampling. Default is disable. +intf_nv_allow_resampling = 1 + +# intf_nv_start_threshold_periods: The number of period to wait before starting playback. The larger the value to great +# the latency. The small the number the great chance for a buffer underrun. A good range is 1 - 5. +intf_nv_start_threshold_periods = 2 + +# intf_nv_period_time: the number of microseconds which should be set to unify latency between different platforms. +# This influence ALSA's period_time and period_size parameters and the result value should be the nearest possible. +# Initial playback latency is equal intf_nv_start_threshold_periods * intf_nv_period_time. If not set internal defaults are used. +# intf_nv_period_time = 31250 + diff --git a/lib/avtp_pipeline/platform/Linux/intf_alsa/alsa_talker.ini b/lib/avtp_pipeline/platform/Linux/intf_alsa/alsa_talker.ini new file mode 100644 index 00000000..9c8a3b82 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_alsa/alsa_talker.ini @@ -0,0 +1,148 @@ +##################################################################### +# Configuration for ALSA and the uncompressed audio mapping module +##################################################################### + +##################################################################### +# General Talker configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 00:25:64:48:ca:a8 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: When SRP is being used the destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set in both side the talker and listener. +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# At this time they need to be locally administered and must be in the range +# of 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically :00 for the first stream, :01 for the second, etc. +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +sr_class = A + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 2000 + +# max_transmit_deficit_usec: Allows setting the maximum packet transmit rate deficit that will +# be recovered when a talker falls behind. This is only used on a talker side. When a talker +# can not keep up with the specified transmit rate it builds up a deficit and will attempt to +# make up for this deficit by sending more packets. There is normally some variability in the +# transmit rate because of other demands on the system so this is expected. However, without this +# bounding value the deficit could grew too large in cases such where more streams are started +# than the system can support and when the number of streams is reduced the remaining streams +# will attempt to recover this deficit by sending packets at a higher rate. This can cause a problem +# at the listener side and significantly delay the recovery time before media playback will return +# to normal. Typically this value can be set to the expected buffer size (in usec) that listeners are +# expected to be buffering. For low latency solutions this is normally a small value. For non-live +# media playback such as video playback the listener side buffers can often be large enough to held many +# seconds of data. +max_transmit_deficit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 4 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_uncmp_audio.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapUncmpAudioInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +# map_nv_tx_rate: Transmit rate. +# This must be set for the uncompressed audio mapping module. +map_nv_tx_rate = 8000 + +# map_nv_packing_factor: Multiple of how many packets of audio frames to place in a media queue item. +# Note: Typically when decreasing the map_nv_tx_rate the packing factor will also be decreased since +# the number of frames per packet will be increasing. +map_nv_packing_factor = 32 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +# intf_fn: The name of the initialize function in the interface. +intf_lib = ./libopenavb_intf_alsa.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfAlsaInitialize + +# intf_nv_ignore_timestamp: If set the listener will ignore the timestamp on media queue items. +intf_nv_ignore_timestamp = 1 + +# intf_nv_device_name: ALSA device name. Commonly "default" or "plug:dmix" +intf_nv_device_name = default + +# intf_nv_audio_rate: Valid values that are supported by the Alsa, TI J5 and 61883-6 are: 32000, 44100, 48000, 88200 and 96000 +intf_nv_audio_rate = 48000 + +# intf_nv_audio_bit_depth: Valid values that are supported by the Alsa, TI J5 and 61883-6 are: 16 +# Note: Typically 20 and 24 bit is not supported in ALSA +intf_nv_audio_bit_depth = 16 + +# intf_nv_audio_channels: Valid values that are supported by the Alsa, TI J5 and 61883-6 are: 1 - 8 +intf_nv_audio_channels = 2 + +# intf_nv_allow_resampling: 0 = disable software resampling. 1 = allow software resampling. Default is disable. +intf_nv_allow_resampling = 1 + + diff --git a/lib/avtp_pipeline/platform/Linux/intf_alsa/openavb_intf_alsa.c b/lib/avtp_pipeline/platform/Linux/intf_alsa/openavb_intf_alsa.c new file mode 100644 index 00000000..36bdf6de --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_alsa/openavb_intf_alsa.c @@ -0,0 +1,1040 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : ALSA interface module. +*/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "openavb_types_pub.h" +#include "openavb_audio_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_map_uncmp_audio_pub.h" +#include "openavb_map_aaf_audio_pub.h" +#include "openavb_intf_pub.h" + +#define AVB_LOG_COMPONENT "ALSA Interface" +#include "openavb_log_pub.h" + +// The asoundlib.h header needs to appear after openavb_trace_pub.h otherwise an incompatibtily version of time.h gets pulled in. +#include <alsa/asoundlib.h> + +#define PCM_DEVICE_NAME_DEFAULT "default" +#define PCM_ACCESS_TYPE SND_PCM_ACCESS_RW_INTERLEAVED + +typedef struct { + ///////////// + // Config data + ///////////// + // Ignore timestamp at listener. + bool ignoreTimestamp; + + // ALSA Device name + char *pDeviceName; + + // map_nv_audio_rate + avb_audio_rate_t audioRate; + + // map_nv_audio_type + avb_audio_type_t audioType; + + // map_nv_audio_bit_depth + avb_audio_bit_depth_t audioBitDepth; + + // map_nv_audio_endian + avb_audio_endian_t audioEndian; + + // map_nv_channels + avb_audio_channels_t audioChannels; + + // map_nv_allow_resampling + bool allowResampling; + + U32 startThresholdPeriods; + + U32 periodTimeUsec; + + ///////////// + // Variable data + ///////////// + // Handle for the PCM device + snd_pcm_t *pcmHandle; + + // ALSA stream + snd_pcm_stream_t pcmStream; + + // ALSA read/write interval + U32 intervalCounter; +} pvt_data_t; + + +static snd_pcm_format_t x_AVBAudioFormatToAlsaFormat(avb_audio_type_t type, + avb_audio_bit_depth_t bitDepth, + avb_audio_endian_t endian, + char const* pMediaQDataFormat) +{ + bool tight = FALSE; + if (bitDepth == AVB_AUDIO_BIT_DEPTH_24BIT) { + if (pMediaQDataFormat != NULL + && strcmp(pMediaQDataFormat, MapAVTPAudioMediaQDataFormat) == 0) { + tight = TRUE; + } + } + + if (type == AVB_AUDIO_TYPE_FLOAT) { + switch (bitDepth) { + case AVB_AUDIO_BIT_DEPTH_32BIT: + if (endian == AVB_AUDIO_ENDIAN_BIG) + return SND_PCM_FORMAT_FLOAT_BE; + else + return SND_PCM_FORMAT_FLOAT_LE; + default: + AVB_LOGF_ERROR("Unsupported audio bit depth for float: %d", bitDepth); + break; + } + } + else if (type == AVB_AUDIO_TYPE_UINT) { + switch (bitDepth) { + case AVB_AUDIO_BIT_DEPTH_8BIT: + return SND_PCM_FORMAT_U8; + case AVB_AUDIO_BIT_DEPTH_16BIT: + if (endian == AVB_AUDIO_ENDIAN_BIG) + return SND_PCM_FORMAT_U16_BE; + else + return SND_PCM_FORMAT_U16_LE; + case AVB_AUDIO_BIT_DEPTH_20BIT: + if (endian == AVB_AUDIO_ENDIAN_BIG) + return SND_PCM_FORMAT_U20_3BE; + else + return SND_PCM_FORMAT_U20_3LE; + case AVB_AUDIO_BIT_DEPTH_24BIT: + if (tight) { + if (endian == AVB_AUDIO_ENDIAN_BIG) + return SND_PCM_FORMAT_U24_3BE; + else + return SND_PCM_FORMAT_U24_3LE; + } + else { + if (endian == AVB_AUDIO_ENDIAN_BIG) + return SND_PCM_FORMAT_U24_BE; + else + return SND_PCM_FORMAT_U24_LE; + } + case AVB_AUDIO_BIT_DEPTH_32BIT: + if (endian == AVB_AUDIO_ENDIAN_BIG) + return SND_PCM_FORMAT_U32_BE; + else + return SND_PCM_FORMAT_U32_LE; + case AVB_AUDIO_BIT_DEPTH_1BIT: + case AVB_AUDIO_BIT_DEPTH_48BIT: + case AVB_AUDIO_BIT_DEPTH_64BIT: + default: + AVB_LOGF_ERROR("Unsupported integer audio bit depth: %d", bitDepth); + break; + } + } + else { + // AVB_AUDIO_TYPE_INT + // or unspecified (defaults to signed int) + switch (bitDepth) { + case AVB_AUDIO_BIT_DEPTH_8BIT: + // 8bit samples don't worry about endianness, + // but default to unsigned instead of signed. + if (type == AVB_AUDIO_TYPE_INT) + return SND_PCM_FORMAT_S8; + else // default + return SND_PCM_FORMAT_U8; + case AVB_AUDIO_BIT_DEPTH_16BIT: + if (endian == AVB_AUDIO_ENDIAN_BIG) + return SND_PCM_FORMAT_S16_BE; + else + return SND_PCM_FORMAT_S16_LE; + case AVB_AUDIO_BIT_DEPTH_20BIT: + if (endian == AVB_AUDIO_ENDIAN_BIG) + return SND_PCM_FORMAT_S20_3BE; + else + return SND_PCM_FORMAT_S20_3LE; + case AVB_AUDIO_BIT_DEPTH_24BIT: + if (tight) { + if (endian == AVB_AUDIO_ENDIAN_BIG) + return SND_PCM_FORMAT_S24_3BE; + else + return SND_PCM_FORMAT_S24_3LE; + } + else { + if (endian == AVB_AUDIO_ENDIAN_BIG) + return SND_PCM_FORMAT_S24_BE; + else + return SND_PCM_FORMAT_S24_LE; + } + case AVB_AUDIO_BIT_DEPTH_32BIT: + if (endian == AVB_AUDIO_ENDIAN_BIG) + return SND_PCM_FORMAT_S32_BE; + else + return SND_PCM_FORMAT_S32_LE; + case AVB_AUDIO_BIT_DEPTH_1BIT: + case AVB_AUDIO_BIT_DEPTH_48BIT: + case AVB_AUDIO_BIT_DEPTH_64BIT: + default: + AVB_LOGF_ERROR("Unsupported audio bit depth: %d", bitDepth); + break; + } + } + + return SND_PCM_FORMAT_UNKNOWN; +} + + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbIntfAlsaCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + if (pMediaQ) { + char *pEnd; + long tmp; + U32 val; + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + media_q_pub_map_uncmp_audio_info_t *pPubMapUncmpAudioInfo; + pPubMapUncmpAudioInfo = (media_q_pub_map_uncmp_audio_info_t *)pMediaQ->pPubMapInfo; + if (!pPubMapUncmpAudioInfo) { + AVB_LOG_ERROR("Public map data for audio info not allocated."); + return; + } + + + if (strcmp(name, "intf_nv_ignore_timestamp") == 0) { + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->ignoreTimestamp = (tmp == 1); + } + } + + else if (strcmp(name, "intf_nv_device_name") == 0) { + if (pPvtData->pDeviceName) + free(pPvtData->pDeviceName); + pPvtData->pDeviceName = strdup(value); + } + + else if (strcmp(name, "intf_nv_audio_rate") == 0) { + val = strtol(value, &pEnd, 10); + // TODO: Should check for specific values + if (val >= AVB_AUDIO_RATE_8KHZ && val <= AVB_AUDIO_RATE_192KHZ) { + pPvtData->audioRate = val; + } + else { + AVB_LOG_ERROR("Invalid audio rate configured for intf_nv_audio_rate."); + pPvtData->audioRate = AVB_AUDIO_RATE_44_1KHZ; + } + + // Give the audio parameters to the mapping module. + if (pMediaQ->pMediaQDataFormat) { + if (strcmp(pMediaQ->pMediaQDataFormat, MapUncmpAudioMediaQDataFormat) == 0 + || strcmp(pMediaQ->pMediaQDataFormat, MapAVTPAudioMediaQDataFormat) == 0) { + pPubMapUncmpAudioInfo->audioRate = pPvtData->audioRate; + } + //else if (pMediaQ->pMediaQDataFormat == MapSAFMediaQDataFormat) { + //} + } + } + + else if (strcmp(name, "intf_nv_audio_bit_depth") == 0) { + val = strtol(value, &pEnd, 10); + // TODO: Should check for specific values + if (val >= AVB_AUDIO_BIT_DEPTH_1BIT && val <= AVB_AUDIO_BIT_DEPTH_64BIT) { + pPvtData->audioBitDepth = val; + } + else { + AVB_LOG_ERROR("Invalid audio type configured for intf_nv_audio_bits."); + pPvtData->audioBitDepth = AVB_AUDIO_BIT_DEPTH_24BIT; + } + + // Give the audio parameters to the mapping module. + if (pMediaQ->pMediaQDataFormat) { + if (strcmp(pMediaQ->pMediaQDataFormat, MapUncmpAudioMediaQDataFormat) == 0 + || strcmp(pMediaQ->pMediaQDataFormat, MapAVTPAudioMediaQDataFormat) == 0) { + pPubMapUncmpAudioInfo->audioBitDepth = pPvtData->audioBitDepth; + } + //else if (pMediaQ->pMediaQDataFormat == MapSAFMediaQDataFormat) { + //} + } + } + + else if (strcmp(name, "intf_nv_audio_type") == 0) { + if (strncasecmp(value, "float", 5) == 0) + pPvtData->audioType = AVB_AUDIO_TYPE_FLOAT; + else if (strncasecmp(value, "sign", 4) == 0 + || strncasecmp(value, "int", 4) == 0) + pPvtData->audioType = AVB_AUDIO_TYPE_INT; + else if (strncasecmp(value, "unsign", 6) == 0 + || strncasecmp(value, "uint", 4) == 0) + pPvtData->audioType = AVB_AUDIO_TYPE_UINT; + else { + AVB_LOG_ERROR("Invalid audio type configured for intf_nv_audio_type."); + pPvtData->audioType = AVB_AUDIO_TYPE_UNSPEC; + } + + // Give the audio parameters to the mapping module. + if (pMediaQ->pMediaQDataFormat) { + if (strcmp(pMediaQ->pMediaQDataFormat, MapUncmpAudioMediaQDataFormat) == 0 + || strcmp(pMediaQ->pMediaQDataFormat, MapAVTPAudioMediaQDataFormat) == 0) { + pPubMapUncmpAudioInfo->audioType = pPvtData->audioType; + } + //else if (pMediaQ->pMediaQDataFormat == MapSAFMediaQDataFormat) { + //} + } + } + + else if (strcmp(name, "intf_nv_audio_endian") == 0) { + if (strncasecmp(value, "big", 3) == 0) + pPvtData->audioEndian = AVB_AUDIO_ENDIAN_BIG; + else if (strncasecmp(value, "little", 6) == 0) + pPvtData->audioEndian = AVB_AUDIO_ENDIAN_LITTLE; + else { + AVB_LOG_ERROR("Invalid audio type configured for intf_nv_audio_endian."); + pPvtData->audioEndian = AVB_AUDIO_ENDIAN_UNSPEC; + } + + // Give the audio parameters to the mapping module. + if (pMediaQ->pMediaQDataFormat) { + if (strcmp(pMediaQ->pMediaQDataFormat, MapUncmpAudioMediaQDataFormat) == 0 + || strcmp(pMediaQ->pMediaQDataFormat, MapAVTPAudioMediaQDataFormat) == 0) { + pPubMapUncmpAudioInfo->audioEndian = pPvtData->audioEndian; + } + //else if (pMediaQ->pMediaQDataFormat == MapSAFMediaQDataFormat) { + //} + } + } + + else if (strcmp(name, "intf_nv_audio_channels") == 0) { + val = strtol(value, &pEnd, 10); + // TODO: Should check for specific values + if (val >= AVB_AUDIO_CHANNELS_1 && val <= AVB_AUDIO_CHANNELS_8) { + pPvtData->audioChannels = val; + } + else { + AVB_LOG_ERROR("Invalid audio channels configured for intf_nv_audio_channels."); + pPvtData->audioChannels = AVB_AUDIO_CHANNELS_2; + } + + // Give the audio parameters to the mapping module. + if (pMediaQ->pMediaQDataFormat) { + if (strcmp(pMediaQ->pMediaQDataFormat, MapUncmpAudioMediaQDataFormat) == 0 + || strcmp(pMediaQ->pMediaQDataFormat, MapAVTPAudioMediaQDataFormat) == 0) { + pPubMapUncmpAudioInfo->audioChannels = pPvtData->audioChannels; + } + //else if (pMediaQ->pMediaQDataFormat == MapSAFMediaQDataFormat) { + //} + } + + } + + if (strcmp(name, "intf_nv_allow_resampling") == 0) { + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->allowResampling = (tmp == 1); + } + } + + else if (strcmp(name, "intf_nv_start_threshold_periods") == 0) { + pPvtData->startThresholdPeriods = strtol(value, &pEnd, 10); + } + + else if (strcmp(name, "intf_nv_period_time") == 0) { + pPvtData->periodTimeUsec = strtol(value, &pEnd, 10); + } + + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +void openavbIntfAlsaGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// A call to this callback indicates that this interface module will be +// a talker. Any talker initialization can be done in this function. +void openavbIntfAlsaTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + S32 rslt; + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + // Holds the hardware parameters + snd_pcm_hw_params_t *hwParams; + + // Open the pcm device. + rslt = snd_pcm_open(&pPvtData->pcmHandle, pPvtData->pDeviceName, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); + + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_open error(): %s", snd_strerror(rslt)); + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Allocate the parameter structure + rslt = snd_pcm_hw_params_malloc(&hwParams); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_malloc error(): %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Initialize the hardware paramneters + rslt = snd_pcm_hw_params_any(pPvtData->pcmHandle, hwParams); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_any() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Set if resampling allowed. + rslt = snd_pcm_hw_params_set_rate_resample(pPvtData->pcmHandle, hwParams, pPvtData->allowResampling); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_set_rate_resample() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Set the access type + rslt = snd_pcm_hw_params_set_access(pPvtData->pcmHandle, hwParams, PCM_ACCESS_TYPE); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_set_access() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Set the sample format + int fmt = x_AVBAudioFormatToAlsaFormat(pPvtData->audioType, + pPvtData->audioBitDepth, + pPvtData->audioEndian, + pMediaQ->pMediaQDataFormat); + rslt = snd_pcm_hw_params_set_format(pPvtData->pcmHandle, hwParams, fmt); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_set_format() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Set the sample rate + U32 rate = pPvtData->audioRate; + rslt = snd_pcm_hw_params_set_rate_near(pPvtData->pcmHandle, hwParams, &rate, 0); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_set_rate_near() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + if (rate != pPvtData->audioRate) { + AVB_LOGF_ERROR("Could not set the exact rate. Requested: %u Using: %u", pPvtData->audioRate, rate); + } + + // Set the number of channels + rslt = snd_pcm_hw_params_set_channels(pPvtData->pcmHandle, hwParams, pPvtData->audioChannels); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_set_channels() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Give the hardware parameters to ALSA + rslt = snd_pcm_hw_params(pPvtData->pcmHandle, hwParams); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Free the hardware parameters + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + + // Get ready for playback + rslt = snd_pcm_prepare(pPvtData->pcmHandle); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_prepare() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Dump settings + snd_output_t* out; + snd_output_stdio_attach(&out, stderr, 0); + snd_pcm_dump(pPvtData->pcmHandle, out); + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// This callback will be called for each AVB transmit interval. +bool openavbIntfAlsaTxCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + + S32 rslt; + + if (pMediaQ) { + media_q_pub_map_uncmp_audio_info_t *pPubMapUncmpAudioInfo = pMediaQ->pPubMapInfo; + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + media_q_item_t *pMediaQItem = NULL; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + //put current wall time into tail item used by AAF mapping module + if ((pPubMapUncmpAudioInfo->sparseMode != TS_SPARSE_MODE_UNSPEC)) { + pMediaQItem = openavbMediaQTailLock(pMediaQ, TRUE); + if ((pMediaQItem) && (pPvtData->intervalCounter % pPubMapUncmpAudioInfo->sparseMode == 0)) { + openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime); + } + openavbMediaQTailUnlock(pMediaQ); + pMediaQItem = NULL; + } + + if (pPvtData->intervalCounter++ % pPubMapUncmpAudioInfo->packingFactor != 0) + return TRUE; + + pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (pMediaQItem) { + if (pMediaQItem->itemSize < pPubMapUncmpAudioInfo->itemSize) { + AVB_LOG_ERROR("Media queue item not large enough for samples"); + } + + rslt = snd_pcm_readi(pPvtData->pcmHandle, pMediaQItem->pPubData + pMediaQItem->dataLen, pPubMapUncmpAudioInfo->framesPerItem - (pMediaQItem->dataLen / pPubMapUncmpAudioInfo->itemFrameSizeBytes)); + + if (rslt == -EPIPE) { + AVB_LOGF_ERROR("snd_pcm_readi() error: %s", snd_strerror(rslt)); + rslt = snd_pcm_recover(pPvtData->pcmHandle, rslt, 0); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_recover: %s", snd_strerror(rslt)); + } + openavbMediaQHeadUnlock(pMediaQ); + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return FALSE; + } + if (rslt < 0) { + openavbMediaQHeadUnlock(pMediaQ); + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return FALSE; + } + + pMediaQItem->dataLen += rslt * pPubMapUncmpAudioInfo->itemFrameSizeBytes; + if (pMediaQItem->dataLen != pPubMapUncmpAudioInfo->itemSize) { + openavbMediaQHeadUnlock(pMediaQ); + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return TRUE; + } + else { + openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime); + openavbMediaQHeadPush(pMediaQ); + + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return TRUE; + } + } + else { + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; // Media queue full + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; +} + +// A call to this callback indicates that this interface module will be +// a listener. Any listener initialization can be done in this function. +void openavbIntfAlsaRxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + S32 rslt; + + // Holds the hardware parameters + snd_pcm_hw_params_t *hwParams; + snd_pcm_sw_params_t *swParams; + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + // Open the pcm device. + rslt = snd_pcm_open(&pPvtData->pcmHandle, pPvtData->pDeviceName, SND_PCM_STREAM_PLAYBACK, 0); + + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_open error(): %s", snd_strerror(rslt)); + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Allocate the parameter structure + rslt = snd_pcm_hw_params_malloc(&hwParams); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_malloc error(): %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Initialize the hardware paramneters + rslt = snd_pcm_hw_params_any(pPvtData->pcmHandle, hwParams); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_any() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Set if resampling allowed. + rslt = snd_pcm_hw_params_set_rate_resample(pPvtData->pcmHandle, hwParams, pPvtData->allowResampling); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_set_rate_resample() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Set the access type + rslt = snd_pcm_hw_params_set_access(pPvtData->pcmHandle, hwParams, PCM_ACCESS_TYPE); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_set_access() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Set the sample format + int fmt = x_AVBAudioFormatToAlsaFormat(pPvtData->audioType, + pPvtData->audioBitDepth, + pPvtData->audioEndian, + pMediaQ->pMediaQDataFormat); + rslt = snd_pcm_hw_params_set_format(pPvtData->pcmHandle, hwParams, fmt); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_set_format() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Set the sample rate + U32 rate = pPvtData->audioRate; + rslt = snd_pcm_hw_params_set_rate_near(pPvtData->pcmHandle, hwParams, &rate, 0); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_set_rate_near() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + if (rate != pPvtData->audioRate) { + AVB_LOGF_ERROR("Could not set the exact rate. Requested: %u Using: %u", pPvtData->audioRate, rate); + } + + // Set the number of channels + rslt = snd_pcm_hw_params_set_channels(pPvtData->pcmHandle, hwParams, pPvtData->audioChannels); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_set_channels() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + + // Time based buffer and period setup + int dir; + + unsigned int buffer_time = 500000; // ring buffer length in us + unsigned int period_time = pPvtData->periodTimeUsec; // period time in us + int period_event = 1; // produce poll event after each period + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t period_size; + const unsigned int usec_round = 10000; + unsigned int max; + + // Hard-coded buffer and period times were failing for 192KHz. + // Check for maximum buffer time and adjust ours down if necessary + rslt = snd_pcm_hw_params_get_buffer_time_max(hwParams, &max, &dir); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_get_buffer_time_max() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + else if (max < buffer_time) { + buffer_time = (max / usec_round) * usec_round; + } + + // Check for maximum perioid time and adjust ours down if necessary + rslt = snd_pcm_hw_params_get_period_time_max(hwParams, &max, &dir); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_get_period_time_max() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + else if (max < period_time) { + period_time = (max / usec_round) * usec_round; + } + + rslt = snd_pcm_hw_params_set_buffer_time_near(pPvtData->pcmHandle, hwParams, &buffer_time, &dir); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_set_buffer_time_near() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + rslt = snd_pcm_hw_params_get_buffer_size(hwParams, &buffer_size); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_get_buffer_size() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + rslt = snd_pcm_hw_params_set_period_time_near(pPvtData->pcmHandle, hwParams, &period_time, &dir); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_set_period_time_near() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + pPvtData->periodTimeUsec = period_time; + + rslt = snd_pcm_hw_params_get_period_size(hwParams, &period_size, &dir); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params_get_period_size() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Give the hardware parameters to ALSA + rslt = snd_pcm_hw_params(pPvtData->pcmHandle, hwParams); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_hw_params() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Free the hardware parameters + snd_pcm_hw_params_free(hwParams); + hwParams = NULL; + + + // Set software parameters + + // Allocate the parameter structure + rslt = snd_pcm_sw_params_malloc(&swParams); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_sw_params_malloc error(): %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + rslt = snd_pcm_sw_params_current(pPvtData->pcmHandle, swParams); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_sw_params_current error(): %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + rslt = snd_pcm_sw_params_set_start_threshold(pPvtData->pcmHandle, swParams, period_size * pPvtData->startThresholdPeriods); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_sw_params_set_start_threshold error(): %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + rslt = snd_pcm_sw_params_set_avail_min(pPvtData->pcmHandle, swParams, period_event ? buffer_size : period_size); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_sw_params_set_avail_min error(): %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + rslt = snd_pcm_sw_params_set_period_event(pPvtData->pcmHandle, swParams, 1); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_sw_params_set_period_event error(): %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + rslt = snd_pcm_sw_params(pPvtData->pcmHandle, swParams); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_sw_params error(): %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Free the hardware parameters + snd_pcm_sw_params_free(swParams); + swParams = NULL; + + + // Get ready for playback + rslt = snd_pcm_prepare(pPvtData->pcmHandle); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_prepare() error: %s", snd_strerror(rslt)); + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + // Dump settings + snd_output_t* out; + snd_output_stdio_attach(&out, stderr, 0); + snd_pcm_dump(pPvtData->pcmHandle, out); + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// This callback is called when acting as a listener. +bool openavbIntfAlsaRxCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + + if (pMediaQ) { + media_q_pub_map_uncmp_audio_info_t *pPubMapUncmpAudioInfo = pMediaQ->pPubMapInfo; + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + bool moreItems = TRUE; + + while (moreItems) { + media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, pPvtData->ignoreTimestamp); + if (pMediaQItem) { + if (pMediaQItem->dataLen) { + S32 rslt; + + rslt = snd_pcm_writei(pPvtData->pcmHandle, pMediaQItem->pPubData, pPubMapUncmpAudioInfo->framesPerItem); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_writei: %s", snd_strerror(rslt)); + rslt = snd_pcm_recover(pPvtData->pcmHandle, rslt, 0); + if (rslt < 0) { + AVB_LOGF_ERROR("snd_pcm_recover: %s", snd_strerror(rslt)); + } + rslt = snd_pcm_writei(pPvtData->pcmHandle, pMediaQItem->pPubData, pPubMapUncmpAudioInfo->framesPerItem); + } + if (rslt != pPubMapUncmpAudioInfo->framesPerItem) { + AVB_LOGF_WARNING("Not all pcm data consumed written:%u consumed:%u", pMediaQItem->dataLen, rslt * pPubMapUncmpAudioInfo->audioChannels); + } + + // DEBUG + // rslt = snd_pcm_avail(pPvtData->pcmHandle); + // AVB_LOGF_INFO("%d\n", rslt); + + } + else { + } + openavbMediaQTailPull(pMediaQ); + } + else { + moreItems = FALSE; + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return TRUE; +} + +// This callback will be called when the interface needs to be closed. All shutdown should +// occur in this function. +void openavbIntfAlsaEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (pPvtData->pcmHandle) { + snd_pcm_close(pPvtData->pcmHandle); + pPvtData->pcmHandle = NULL; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +void openavbIntfAlsaGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Main initialization entry point into the interface module +extern DLL_EXPORT bool openavbIntfAlsaInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pMediaQ->pPvtIntfInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pPvtIntfInfo) { + AVB_LOG_ERROR("Unable to allocate memory for AVTP interface module."); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + + pIntfCB->intf_cfg_cb = openavbIntfAlsaCfgCB; + pIntfCB->intf_gen_init_cb = openavbIntfAlsaGenInitCB; + pIntfCB->intf_tx_init_cb = openavbIntfAlsaTxInitCB; + pIntfCB->intf_tx_cb = openavbIntfAlsaTxCB; + pIntfCB->intf_rx_init_cb = openavbIntfAlsaRxInitCB; + pIntfCB->intf_rx_cb = openavbIntfAlsaRxCB; + pIntfCB->intf_end_cb = openavbIntfAlsaEndCB; + pIntfCB->intf_gen_end_cb = openavbIntfAlsaGenEndCB; + + pPvtData->ignoreTimestamp = FALSE; + pPvtData->pDeviceName = strdup(PCM_DEVICE_NAME_DEFAULT); + pPvtData->allowResampling = TRUE; + pPvtData->intervalCounter = 0; + pPvtData->startThresholdPeriods = 2; // Default to 2 periods of frames as the start threshold + pPvtData->periodTimeUsec = 100000; + + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return TRUE; +} diff --git a/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/CMakeLists.txt b/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/CMakeLists.txt new file mode 100644 index 00000000..4176d2a3 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/CMakeLists.txt @@ -0,0 +1,10 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_OSAL_DIR}/intf_mjpeg_gst/openavb_intf_mjpeg_gst.c + PARENT_SCOPE +) + +# Need include and link directories for GST +SET (INTF_INCLUDE_DIR ${INTF_INCLUDE_DIR} ${GLIB_PKG_INCLUDE_DIRS} ${GST_PKG_INCLUDE_DIRS} PARENT_SCOPE) +SET (INTF_LIBRARY_DIR ${INTF_LIBRARY_DIR} PARENT_SCOPE) +SET (INTF_LIBRARY ${GLIB_PKG_LIBRARIES} ${GST_PKG_LIBRARIES} ${GSTRTP_PKG_LIBRARIES} PARENT_SCOPE) + diff --git a/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/mjpeg_camera_intf.md b/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/mjpeg_camera_intf.md new file mode 100644 index 00000000..b50e8f50 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/mjpeg_camera_intf.md @@ -0,0 +1,18 @@ +MJPEG GStreamer interface {#mjpeg_gst_intf} +========================= + +# Description + +MJPEG gstreamer interface module. + +<br> +# Interface module configuration parameters + +Name | Description +--------------------------|--------------------------- +intf_nv_gst_pipeline |GStreamer pipeline that will be used +intf_nv_async_rx |If set to 1 sets RX in async mode +intf_nv_blocking_rx |If set to 1 switches gstreamer into blocking mode +intf_nv_ignore_timestamp | If set to 1 timestamps will be ignored during \ + processing of frames. This also means stale (old) \ + Media Queue items will not be purged. diff --git a/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/mjpeg_gst_listener.ini b/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/mjpeg_gst_listener.ini new file mode 100644 index 00000000..02a8cf99 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/mjpeg_gst_listener.ini @@ -0,0 +1,105 @@ +##################################################################### +# General Listener configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = listener + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +stream_addr = 00:50:56:c0:00:08 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: see description in talker.ini +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +#max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +#sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +#max_transit_usec = 2000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_mjpeg.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapMjpegInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +intf_lib = ./libopenavb_intf_mjpeg_gst.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfMjpegGstInitialize + +# intf_nv_file_name: The fully qualified file name used both the talker and listener. +intf_nv_file_name = output.mjpeg + +# intf_nv_repeat: Continually repeat the file stream when running as a talker. +intf_nv_repeat = 0 + +# intf_nv_ignore_timestamp: If set the listener will ignore the timestamp on media queue items. +#intf_nv_ignore_timestamp = 1 +intf_nv_gst_pipeline = appsrc name=avbsrc ! application/x-rtp,media=video,clock-rate=90000,encoding-name=JPEG,payload=96,ssrc=5,clock-base=1,seqnum-base=1 ! rtpjpegdepay ! jpegdec ! ffmpegcolorspace ! omx_scaler ! video/x-raw-yuv,width=1920,height=800 ! omx_ctrl display-mode=OMX_DC_MODE_1080P_60 ! omx_videosink sync=false + +intf_nv_blocking_rx = 1 +intf_nv_async_rx = 1 + diff --git a/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/mjpeg_gst_talker.ini b/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/mjpeg_gst_talker.ini new file mode 100644 index 00000000..fba0fece --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/mjpeg_gst_talker.ini @@ -0,0 +1,131 @@ +##################################################################### +# General Talker configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 00:25:64:48:ca:a8 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: destination multicast address for the stream. +# +# If using SRP and MAAP, dynamic destination addresses are generated +# automatically by the talker and passed to the listner, and don't +# need to be configured. +# +# Without MAAP, locally administered (static) addresses must be +# configured. Thouse addresses are in the range of: +# 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically use :00 for the first stream, :01 for the second, etc. +# +# When SRP is being used the static destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set (to the same value) in both the talker and listener. +# +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# +dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +#sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 2000 + +# max_transmit_deficit_usec: Allows setting the maximum packet transmit rate deficit that will +# be recovered when a talker falls behind. This is only used on a talker side. When a talker +# can not keep up with the specified transmit rate it builds up a deficit and will attempt to +# make up for this deficit by sending more packets. There is normally some variability in the +# transmit rate because of other demands on the system so this is expected. However, without this +# bounding value the deficit could grew too large in cases such where more streams are started +# than the system can support and when the number of streams is reduced the remaining streams +# will attempt to recover this deficit by sending packets at a higher rate. This can cause a problem +# at the listener side and significantly delay the recovery time before media playback will return +# to normal. Typically this value can be set to the expected buffer size (in usec) that listeners are +# expected to be buffering. For low latency solutions this is normally a small value. For non-live +# media playback such as video playback the listener side buffers can often be large enough to held many +# seconds of data. +max_transmit_deficit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_mjpeg.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapMjpegInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +# map_nv_tx_rate: Transmit rate +# If not set default of the talker class will be used. +#map_nv_tx_rate = 2000 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +# intf_fn: The name of the initialize function in the interface. +intf_lib = ./libopenavb_intf_mjpeg_gst.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfMjpegGstInitialize + +#intf_nv_gst_pipeline = v4l2src ! "image/jpeg" ! rtpjpegpay ssrc=5 timestamp-offset=1 seqnum-offset=1 ! appsink name=avbsink +intf_nv_gst_pipeline = v4l2src ! video/x-raw-yuv,width=640,height=480 ! jpegenc ! rtpjpegpay ssrc=5 timestamp-offset=1 seqnum-offset=1 ! appsink name=avbsink diff --git a/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/openavb_intf_mjpeg_gst.c b/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/openavb_intf_mjpeg_gst.c new file mode 100644 index 00000000..12478176 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mjpeg_gst/openavb_intf_mjpeg_gst.c @@ -0,0 +1,532 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : MJPEG File interface module. +*/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <gst/gst.h> +#include <gst/app/gstappsink.h> +#include <gst/app/gstappsrc.h> +#include <gst/rtp/gstrtpbuffer.h> +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_intf_pub.h" +#include "openavb_map_mjpeg_pub.h" + +#define AVB_LOG_COMPONENT "MJPEG Interface" +#include "openavb_log_pub.h" + +#define APPSINK_NAME "avbsink" +#define APPSRC_NAME "avbsrc" +#define PACKETS_PER_RX_CALL 20 + +#define NBUFS 256 + +typedef struct { + char *pPipelineStr; + + bool ignoreTimestamp; + + GstElement *pipe; + GstElement *appsink; + GstElement *appsrc; + + U32 bufwr; + U32 bufrd; + U32 seq; + GstBuffer *rxBufs[NBUFS]; + bool asyncRx; + bool blockingRx; + +} pvt_data_t; + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbIntfMjpegGstCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + if (!pMediaQ) + { + AVB_LOG_DEBUG("mjpeg-gst cfgCB: no mediaQ!"); + return; + } + + char *pEnd; + long tmp; + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + pPvtData->asyncRx = FALSE; + + if (strcmp(name, "intf_nv_gst_pipeline") == 0) + { + if (pPvtData->pPipelineStr) + { + free(pPvtData->pPipelineStr); + } + pPvtData->pPipelineStr = strdup(value); + } + else if (strcmp(name, "intf_nv_async_rx") == 0) { + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->asyncRx = (tmp == 1); + } + } + else if (strcmp(name, "intf_nv_blocking_rx") == 0) { + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->blockingRx = (tmp == 1); + } + } + else if (strcmp(name, "intf_nv_ignore_timestamp") == 0) { + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && tmp == 1) { + pPvtData->ignoreTimestamp = (tmp == 1); + } + } +} + +void openavbIntfMjpegGstGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + if (!pMediaQ) + { + AVB_LOG_DEBUG("mjpeg-gst initCB: no mediaQ!"); + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// A call to this callback indicates that this interface module will be +// a talker. Any talker initialization can be done in this function. +void openavbIntfMjpegGstTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (!pMediaQ) + { + AVB_LOG_DEBUG("mjpeg-gst txinit: no mediaQ!"); + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + GError *error = NULL; + pPvtData->pipe = gst_parse_launch(pPvtData->pPipelineStr, &error); + if (error) { + AVB_LOGF_ERROR("Unable to create pipeline: %s", error->message); + } + + AVB_LOGF_INFO("Pipeline: %s", pPvtData->pPipelineStr); + pPvtData->appsink = gst_bin_get_by_name(GST_BIN(pPvtData->pipe), APPSINK_NAME); + if (!pPvtData->appsink) { + AVB_LOG_ERROR("Failed to find appsink element"); + } + GstCaps *sinkCaps = gst_caps_from_string("application/x-rtp"); + //No limits for internal sink buffers. This may cause large memory consumption. + g_object_set(pPvtData->appsink, "max-buffers", 0, "drop", 0, "caps", sinkCaps, NULL); + gst_caps_unref(sinkCaps); + //FIXME: Check if state change was successful + gst_element_set_state(pPvtData->pipe, GST_STATE_PLAYING); + + AVB_TRACE_EXIT(AVB_TRACE_INTF); + + return; +} + +// This callback will be called for each AVB transmit interval. Commonly this will be +// 4000 or 8000 times per second. +bool openavbIntfMjpegGstTxCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + + if (!pMediaQ) + { + AVB_LOG_DEBUG("No MediaQ in MjpegGstTxCB"); + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + U32 paySize = 0; + GstBuffer *txBuf = gst_app_sink_pull_buffer(GST_APP_SINK(pPvtData->appsink)); + + if (!txBuf) { + AVB_LOG_ERROR("Gstreamer buffer pull problem"); + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return FALSE; + } + paySize = gst_rtp_buffer_get_payload_len(txBuf); + + //Transmit data --BEGIN-- + media_q_item_t *pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (pMediaQItem) { + pMediaQItem->dataLen = paySize; + memcpy(pMediaQItem->pPubData, gst_rtp_buffer_get_payload(txBuf), paySize); + if (gst_rtp_buffer_get_marker(txBuf)) + { + ((media_q_item_map_mjpeg_pub_data_t *)pMediaQItem->pPubMapData)->lastFragment = TRUE; + } + else + { + ((media_q_item_map_mjpeg_pub_data_t *)pMediaQItem->pPubMapData)->lastFragment = FALSE; + } + openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime); + openavbMediaQHeadPush(pMediaQ); + gst_buffer_unref(txBuf); + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return TRUE; + } + else { + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + AVB_LOG_INFO("GStreamer returned NULL buffer, pipeline stopped"); + return FALSE; // Media queue full + } + // never here.... + gst_buffer_unref(txBuf); + openavbMediaQHeadUnlock(pMediaQ); + pMediaQItem->dataLen = 0; + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return TRUE; +} + +// Async stuff... +static pthread_t asyncRxThread; +static pthread_mutex_t asyncRxMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t asyncReadMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t asyncReadCond = PTHREAD_COND_INITIALIZER; +#define LOCK() pthread_mutex_lock(&asyncRxMutex) +#define UNLOCK() pthread_mutex_unlock(&asyncRxMutex) +static bool bAsyncRXStreaming; +static media_q_t *pAsyncRxMediaQ; +//static media_q_item_t *pAsyncRxMediaQItem; +//static bool bAsyncRXDoneWithItem; + +static void *openavbIntfMjpegGstRxThreadfn(void *pv) +{ + pvt_data_t *pPvtData; + + if (!pAsyncRxMediaQ) + { + AVB_LOG_ERROR("No async mediaQ"); + return 0; + } + pPvtData = pAsyncRxMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("No async RX private data."); + return 0; + } + + bAsyncRXStreaming = TRUE; + while (bAsyncRXStreaming) + { + LOCK(); + if (pPvtData->bufwr <= pPvtData->bufrd) + { + UNLOCK(); + pthread_mutex_lock(&asyncReadMutex); + pthread_cond_wait(&asyncReadCond, &asyncReadMutex); + pthread_mutex_unlock(&asyncReadMutex); + } + else + { + UNLOCK(); + } + LOCK(); + GstBuffer *rxBuf = pPvtData->rxBufs[pPvtData->bufrd%NBUFS]; + pPvtData->bufrd++; + UNLOCK(); + if (rxBuf && (pPvtData->bufwr - pPvtData->bufrd) < NBUFS) + { + GstFlowReturn ret = gst_app_src_push_buffer(GST_APP_SRC(pPvtData->appsrc), rxBuf); + + if (ret != GST_FLOW_OK) + { + AVB_LOGF_ERROR("Pushing buffer to appsrc failed with code %d", ret); + } + } + else + { + AVB_LOGF_INFO("Th Buf %x %d skipped, OO!!", rxBuf, pPvtData->bufrd); +// gst_buffer_unref(rxBuf); + } + } + return 0; +} +// A call to this callback indicates that this interface module will be +// a listener. Any listener initialization can be done in this function. +void openavbIntfMjpegGstRxInitCB(media_q_t *pMediaQ) +{ + AVB_LOG_DEBUG("Rx Init callback."); + if (!pMediaQ) + { + AVB_LOG_DEBUG("No MediaQ in MjpegGstRxInitCB"); + return; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + GError *error = NULL; + pPvtData->pipe = gst_parse_launch(pPvtData->pPipelineStr, &error); +// pPvtData->pipe = gst_parse_launch("appsrc name=avbsrc ! application/x-rtp,media=video,clock-rate=90000,encoding-name=JPEG,payload=96,ssrc=5,clock-base=1,seqnum-base=1 ! rtpjpegdepay ! jpegdec ! ffmpegcolorspace ! omx_scaler ! omx_ctrl display-mode=OMX_DC_MODE_1080P_60 ! omx_videosink sync=false", &error); +// pPvtData->pipe = gst_parse_launch("appsrc name=avbsrc ! application/x-rtp,media=video,clock-rate=90000,encoding-name=JPEG,payload=96,ssrc=5,clock-base=1,seqnum-base=1 ! rtpjpegdepay ! jpegdec ! autovideosink", &error); + if (error) { + AVB_LOGF_ERROR("Unable to create pipeline: %s", error->message); + } + + AVB_LOGF_INFO("Pipeline: %s", pPvtData->pPipelineStr); + pPvtData->appsrc = gst_bin_get_by_name(GST_BIN(pPvtData->pipe), APPSRC_NAME); + if (!pPvtData->appsrc) { + AVB_LOG_ERROR("Failed to find appsrc element"); + } + GstCaps *srcCaps = gst_caps_from_string("application/x-rtp"); + if (!srcCaps) + { + AVB_LOGF_DEBUG("Caps=%x",srcCaps); + } + gst_app_src_set_caps ((GstAppSrc *)pPvtData->appsrc, srcCaps); + gst_caps_unref(srcCaps); + gst_app_src_set_max_bytes((GstAppSrc *)pPvtData->appsrc, 3000); + + if (pPvtData->blockingRx) + { + AVB_LOG_DEBUG("Switching gstreamer into blocking mode"); + g_object_set(pPvtData->appsrc, "block", 1, NULL); // and now we have to do async rx :) + } + + //FIXME: Check if state change was successful + gst_element_set_state(pPvtData->pipe, GST_STATE_PLAYING); + + pPvtData->seq = 1; + + if (pPvtData->asyncRx) + { + pAsyncRxMediaQ = pMediaQ; + int err = pthread_mutex_init(&asyncRxMutex, 0); + if (err) + AVB_LOG_ERROR("Mutex init failed"); + pthread_attr_t attr; + struct sched_param param; + pthread_attr_init(&attr); + pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + param.sched_priority = 0; + pthread_attr_setschedparam(&attr, ¶m); + pthread_create(&asyncRxThread, &attr, openavbIntfMjpegGstRxThreadfn, NULL); + } +} + +// This callback is called when acting as a listener. +bool openavbIntfMjpegGstRxCB(media_q_t *pMediaQ) +{ + if (!pMediaQ) + { + AVB_LOG_DEBUG("RxCB: no mediaQ!"); + return TRUE; + } + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + bool moreSourcePackets = TRUE; + + while (moreSourcePackets) + { + media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, pPvtData->ignoreTimestamp); + if (!pMediaQItem) + { + moreSourcePackets = FALSE; + openavbMediaQTailPull(pMediaQ); + continue; + } + if (!pMediaQItem->dataLen) + { + AVB_LOG_DEBUG("No dataLen"); + openavbMediaQTailPull(pMediaQ); + continue; + } + if (pPvtData->asyncRx) + { + LOCK(); + unsigned long mdif = pPvtData->bufwr - pPvtData->bufrd; + if (pPvtData->bufwr > pPvtData->bufrd && mdif >= NBUFS) + { + openavbMediaQTailPull(pMediaQ); + AVB_LOGF_INFO("Rx async queue full, dropping (%lu - %lu = %lu)",pPvtData->bufwr,pPvtData->bufrd,mdif); + UNLOCK(); + continue; + } + UNLOCK(); + } + GstBuffer *rxBuf = gst_rtp_buffer_new_allocate (pMediaQItem->dataLen, 0,0); + if (!rxBuf) + { + AVB_LOG_ERROR("gst_rtp_buffer_allocate failed!"); + openavbMediaQTailUnlock(pMediaQ); + return FALSE; + } + memcpy(gst_rtp_buffer_get_payload(rxBuf), pMediaQItem->pPubData, pMediaQItem->dataLen); + + GST_BUFFER_TIMESTAMP(rxBuf) = GST_CLOCK_TIME_NONE; + GST_BUFFER_DURATION(rxBuf) = -1; + + if ( ((media_q_item_map_mjpeg_pub_data_t *)pMediaQItem->pPubMapData)->lastFragment ) + { + gst_rtp_buffer_set_marker(rxBuf,TRUE); + } + + gst_rtp_buffer_set_ssrc(rxBuf,5); + gst_rtp_buffer_set_payload_type(rxBuf,96); + gst_rtp_buffer_set_version(rxBuf,2); + gst_rtp_buffer_set_seq(rxBuf,pPvtData->seq++); + + if (pPvtData->asyncRx) + { + LOCK(); + pPvtData->rxBufs[pPvtData->bufwr%NBUFS] = rxBuf; + pPvtData->bufwr++; + UNLOCK(); + if (pPvtData->bufwr > pPvtData->bufrd) + { + pthread_mutex_lock(&asyncReadMutex); + pthread_cond_signal(&asyncReadCond); + pthread_mutex_unlock(&asyncReadMutex); + } + } + else + { + GstFlowReturn ret = gst_app_src_push_buffer(GST_APP_SRC(pPvtData->appsrc), rxBuf); + if (ret != GST_FLOW_OK) + { + AVB_LOGF_ERROR("Pushing buffer to appsrc failed with code %d", ret); + } + } + openavbMediaQTailPull(pMediaQ); + } + + return TRUE; +} + +// This callback will be called when the interface needs to be closed. All shutdown should +// occur in this function. +void openavbIntfMjpegGstEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + bAsyncRXStreaming = FALSE; + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + if (pPvtData->pipe) + { + gst_element_set_state(pPvtData->pipe, GST_STATE_NULL); + if (pPvtData->appsink) + { + gst_object_unref(pPvtData->appsink); + pPvtData->appsink = NULL; + } + if (pPvtData->appsrc) + { + gst_object_unref(pPvtData->appsrc); + pPvtData->appsrc = NULL; + } + gst_object_unref(pPvtData->pipe); + pPvtData->pipe = NULL; + } + if (pPvtData->asyncRx) + { + pthread_mutex_lock(&asyncReadMutex); + pthread_cond_signal(&asyncReadCond); + pthread_mutex_unlock(&asyncReadMutex); + pthread_join(asyncRxThread, NULL); + } + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +void openavbIntfMjpegGstGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Main initialization entry point into the interface module +extern DLL_EXPORT bool openavbIntfMjpegGstInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (!pMediaQ) + { + AVB_LOG_DEBUG("mjpeg-gst GstInitialize: no mediaQ!"); + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return TRUE; + } + + pMediaQ->pPvtIntfInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + + pIntfCB->intf_cfg_cb = openavbIntfMjpegGstCfgCB; + pIntfCB->intf_gen_init_cb = openavbIntfMjpegGstGenInitCB; + pIntfCB->intf_tx_init_cb = openavbIntfMjpegGstTxInitCB; + pIntfCB->intf_tx_cb = openavbIntfMjpegGstTxCB; + pIntfCB->intf_rx_init_cb = openavbIntfMjpegGstRxInitCB; + pIntfCB->intf_rx_cb = openavbIntfMjpegGstRxCB; + pIntfCB->intf_end_cb = openavbIntfMjpegGstEndCB; + pIntfCB->intf_gen_end_cb = openavbIntfMjpegGstGenEndCB; + + pPvtData->ignoreTimestamp = FALSE; + + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return TRUE; +} diff --git a/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/CMakeLists.txt b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/CMakeLists.txt new file mode 100644 index 00000000..35bad67a --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/CMakeLists.txt @@ -0,0 +1,4 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_OSAL_DIR}/intf_mpeg2ts_file/openavb_intf_mpeg2ts_file.c + PARENT_SCOPE +) diff --git a/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/mpeg2ts_file_intf.md b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/mpeg2ts_file_intf.md new file mode 100644 index 00000000..ad17f7bd --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/mpeg2ts_file_intf.md @@ -0,0 +1,50 @@ +MPEG2TS file interface {#mpeg2ts_file_intf} +======================= + +# Description + +Mpeg2 TS File interface module. +Computation of TS packet duration copied from Live555 application +(www.live555.com). + +<br> +# Interface module configuration parameters + +Name | Description +--------------------------|--------------------------- +intf_nv_file_name |The fully qualified file name. Used on **talker** \ + and on **listener** side +intf_nv_repeat |If set to 1 it will continually repeat the file \ + stream when running as a talker +intf_nv_repeat_seconds |Delay in seconds which will be skipped when repeating +intf_nv_enable_proper_bitrate_streaming|Setting to 1 will enable tracking of \ + the bitrate +intf_nv_ignore_timestamp | If set to 1 timestamps will be ignored during \ + processing of frames. This also means stale (old) \ + Media Queue items will not be purged. + +<br> +# Notes + +Additionally the @ref openavb_intf_cb_t::intf_get_src_bitrate_cb callback function +can be used to calculate the maximum bitrate of the source. + +**Note**: To make those calculations +**intf_nv_enable_proper_bitrate_streaming** has to be enabled. + +If this callback is registered (not NULL) it will trigger several actions: +* calculated maximum bitrate will be passed to the maping module via the +function @ref openavb_map_cb_t::map_set_src_bitrate_cb +* callback @ref openavb_map_cb_t::map_get_max_interval_frames_cb will be +called to calculate the **maximum interval frames** +* **maximum frame size** is calculated by calling +@ref openavb_map_cb_t::map_max_data_size_cb. + +If this callback function is not registered (is NULL), values taken from +the configuration file for **maximum frame size** and **maximum interval frames** +will be used for calculations. + +**maximum frame size** and **maximum interval frames** values are used by +* SRP to calculate bandwidth, +* FQTSS to calculate queueing discipline parameters. + diff --git a/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/mpeg2ts_file_listener.ini b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/mpeg2ts_file_listener.ini new file mode 100644 index 00000000..eb76cb7d --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/mpeg2ts_file_listener.ini @@ -0,0 +1,100 @@ +##################################################################### +# General Listener configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = listener + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +stream_addr = 00:0c:29:f8:3e:c6 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: see description in talker.ini +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +#max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +#sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +#max_transit_usec = 2000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_mpeg2ts.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapMpeg2tsInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +intf_lib = ./libopenavb_intf_mpeg2ts_file.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfMpeg2tsFileInitialize + +# intf_nv_file_name: The fully qualified file name used both the talker and listener. +intf_nv_file_name = output.ts + +# intf_nv_repeat: Continually repeat the file stream when running as a talker. +intf_nv_repeat = 0 + +# intf_nv_ignore_timestamp: If set the listener will ignore the timestamp on media queue items. +#intf_nv_ignore_timestamp = 1 diff --git a/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/mpeg2ts_file_talker.ini b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/mpeg2ts_file_talker.ini new file mode 100644 index 00000000..c4f595a0 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/mpeg2ts_file_talker.ini @@ -0,0 +1,142 @@ +##################################################################### +# General Talker configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 00:25:64:48:ca:a8 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: destination multicast address for the stream. +# +# If using SRP and MAAP, dynamic destination addresses are generated +# automatically by the talker and passed to the listner, and don't +# need to be configured. +# +# Without MAAP, locally administered (static) addresses must be +# configured. Thouse addresses are in the range of: +# 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically use :00 for the first stream, :01 for the second, etc. +# +# When SRP is being used the static destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set (to the same value) in both the talker and listener. +# +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +#sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 2000 + +# max_transmit_deficit_usec: Allows setting the maximum packet transmit rate deficit that will +# be recovered when a talker falls behind. This is only used on a talker side. When a talker +# can not keep up with the specified transmit rate it builds up a deficit and will attempt to +# make up for this deficit by sending more packets. There is normally some variability in the +# transmit rate because of other demands on the system so this is expected. However, without this +# bounding value the deficit could grew too large in cases such where more streams are started +# than the system can support and when the number of streams is reduced the remaining streams +# will attempt to recover this deficit by sending packets at a higher rate. This can cause a problem +# at the listener side and significantly delay the recovery time before media playback will return +# to normal. Typically this value can be set to the expected buffer size (in usec) that listeners are +# expected to be buffering. For low latency solutions this is normally a small value. For non-live +# media playback such as video playback the listener side buffers can often be large enough to held many +# seconds of data. +max_transmit_deficit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_mpeg2ts.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapMpeg2tsInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +# map_nv_tx_rate: Transmit rate +# If not set default of the talker class will be used. +#map_nv_tx_rate = 2000 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +# intf_fn: The name of the initialize function in the interface. +intf_lib = ./libopenavb_intf_mpeg2ts_file.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfMpeg2tsFileInitialize + +# intf_nv_file_name: The fully qualified file name used both the talker and listener. +#intf_nv_file_name = input.ts +intf_nv_file_name = hbo_trailer_6ksd_converted.ts + +# intf_nv_repeat: Continually repeat the file stream when running as a talker. +intf_nv_repeat = 0 + + + + + + + diff --git a/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/openavb_intf_mpeg2ts_file.c b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/openavb_intf_mpeg2ts_file.c new file mode 100644 index 00000000..01f448a4 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_file/openavb_intf_mpeg2ts_file.c @@ -0,0 +1,706 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Mpeg2 TS File interface module. +* Computation of TS packet duration copied +* from Live555 application (www.live555.com). +*/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_intf_pub.h" + +#define AVB_LOG_COMPONENT "MPEG2TS Interface" +#include "openavb_log_pub.h" + +#define PCR_PERIOD_VARIATION_RATIO 0.5 +#define TIME_ADJUSTMENT_FACTOR 0.8 +#define MAX_PLAYOUT_BUFFER_DURATION 0.1 // (seconds) +#define NEW_DURATION_WEIGHT 0.5 +#define MAX_TABLE_PIDS 100 +#define F27_MHZ 27000000.0 +#define F90_KHZ 90000.0 + +struct PIDStatus { + double firstClock, lastClock, firstRealTime, lastRealTime; + unsigned long long lastPacketNum; + int used; + int pid; +}; + +typedef struct { + ///////////// + // Config data + ///////////// + // intf_nv_file_name: The fully qualified file name used both the talker and listener. + // NULL means use stdin/stdout + char *pFileName; + + // intf_nv_repeat: Continually repeat the file stream when running + bool repeat; + + // Delay repeating the video until this many seconds have passed + int repeatSeconds; + + // Ignore timestamp at listener. + bool ignoreTimestamp; + + ///////////// + // Variable data + ///////////// + FILE *pFile; + + // Talker variables for tracking rewind + struct timespec startTime; + int nRepeatCount; + int nBuffersSent; + + // Talker variables for tracking bitrate + unsigned int maxBitrate; + int enableBitrateTracking; + double nextTransmitTime; + double fTSPacketCount; + double fTSPCRCount; + double fTSPacketDurationEstimate; + struct PIDStatus *fPIDStatusTable; + +} pvt_data_t; + +double openavbIntfMpeg2tsFileComputeDuration(pvt_data_t* pPvtData, unsigned char* pkts, unsigned int length); + +int pidTableFindOrCreatePid(struct PIDStatus *table, const unsigned int pid) +{ + int idx = -1; + int i = 0; + for (i = 0; i < MAX_TABLE_PIDS; ++i) + { + if (pid == table[i].pid) + { + idx = i; + break; + } + } + if (-1 == idx) /* create */ + { + /* find free slot */ + for (i = 0; i < MAX_TABLE_PIDS; ++i) + { + if (0 == table[i].used) + { + table[i].pid = pid; + idx = i; + break; + } + } + } + + return idx; +} + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbIntfMpeg2tsFileCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + char *pEnd; + unsigned long tmp; + bool nameOK = TRUE, valueOK = FALSE; + + if (strcmp(name, "intf_nv_file_name") == 0) { + if (pPvtData->pFileName) + free(pPvtData->pFileName); + pPvtData->pFileName = strdup(value); + valueOK = TRUE; + } + + else if (strcmp(name, "intf_nv_repeat") == 0) { + tmp = strtoul(value, &pEnd, 10); + if (*pEnd == '\0' && pEnd != value && (tmp == 0 || tmp == 1)) { + pPvtData->repeat = (tmp == 1); + valueOK = TRUE; + } + } + else if (strcmp(name, "intf_nv_repeat_seconds") == 0) { + tmp = strtoul(value, &pEnd, 10); + if (*pEnd == '\0' && pEnd != value) { + pPvtData->repeatSeconds = tmp; + valueOK = TRUE; + } + } + else if (strcmp(name, "intf_nv_ignore_timestamp") == 0) { + tmp = strtoul(value, &pEnd, 10); + if (*pEnd == '\0' && pEnd != value && (tmp == 0 || tmp == 1)) { + pPvtData->ignoreTimestamp = (tmp == 1); + valueOK = TRUE; + } + } + else if (strcmp(name, "intf_nv_enable_proper_bitrate_streaming") == 0) { + tmp = strtoul(value, &pEnd, 10); + if (*pEnd == '\0' && pEnd != value && (tmp == 0 || tmp == 1)) { + pPvtData->enableBitrateTracking = (tmp == 1); + valueOK = TRUE; + } + } + else { + AVB_LOGF_WARNING("Unknown configuration item: %s", name); + nameOK = FALSE; + } + + if (nameOK && !valueOK) { + AVB_LOGF_WARNING("Bad value for configuration item: %s = %s", name, value); + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +#define MPEGTS_SYNC_BYTE (0x47) +static void sync_scan(FILE* input) +{ + unsigned char byte = 0; + while (1 == fread(&byte, 1, 1, input)) { + if (MPEGTS_SYNC_BYTE == byte) { + fseek(input, -1, SEEK_CUR); + break; + } + } +} + +#define TS_PACKETS 1 +static unsigned int openavbComputeFileBitrate(char *fileName, media_q_t *pMediaQ) +{ + double max_bitrate = 0; + FILE *input = fopen(fileName, "rb"); + if (input != NULL) { + unsigned char* packets = (unsigned char *) malloc(188*TS_PACKETS); + double fTSPCRCount = 0; + struct PIDStatus *fPIDStatusTable = (struct PIDStatus*) calloc(MAX_TABLE_PIDS, sizeof(struct PIDStatus)); + double fTSPacketCount = 0; + int i = 0; + for(i = 0; i < MAX_TABLE_PIDS; ++i) + { + fPIDStatusTable[i].pid = -1; + fPIDStatusTable[i].used = 0; + } + sync_scan(input); + while((TS_PACKETS * 188) == fread((void *)packets, 1, 188*TS_PACKETS, input)) + { + unsigned char* pkt; + for (pkt = packets; pkt < &(packets[TS_PACKETS*188]); pkt += 188) + { + fTSPacketCount++; + + unsigned char const adaptation_field_control = (pkt[3]&0x30)>>4; + if (adaptation_field_control != 2 && adaptation_field_control != 3) continue; + // there's no adaptation_field + + unsigned char const adaptation_field_length = pkt[4]; + if (adaptation_field_length == 0) continue; + + unsigned char const pcrFlag = pkt[5]&0x10; + if (pcrFlag == 0) continue; // no PCR + + unsigned char const discontinuity_indicator = pkt[5]&0x80; + // There's a PCR. Get it. + ++fTSPCRCount; + unsigned int pcrBaseHigh = (pkt[6]<<24)|(pkt[7]<<16)|(pkt[8]<<8)|pkt[9]; + double fClock = pcrBaseHigh/(F90_KHZ/2); + if ((pkt[10]&0x80) != 0) fClock += 1/F90_KHZ; // add in low-bit (if set) + unsigned short pcrExt = ((pkt[10]&0x01)<<8) | pkt[11]; + fClock += pcrExt/F27_MHZ; + + unsigned pid = ((pkt[1]&0x1F)<<8) | pkt[2]; + int idx = pidTableFindOrCreatePid(fPIDStatusTable, pid); + if (!fPIDStatusTable[idx].used) { + // We're seeing this PID's PCR for the first time: + fPIDStatusTable[idx].used = 1; + fPIDStatusTable[idx].firstClock = fClock; + fPIDStatusTable[idx].lastClock = fClock; + fPIDStatusTable[idx].lastPacketNum = fTSPacketCount; + } + else { + if (discontinuity_indicator == 0) { + double duration = fClock - fPIDStatusTable[idx].lastClock; + if (duration > 0) { + double data = (fTSPacketCount - fPIDStatusTable[idx].lastPacketNum) * 188 * 8; + double bitrate = data / duration; + if (bitrate > max_bitrate) + max_bitrate = bitrate; + } + fPIDStatusTable[idx].lastClock = fClock; + if (duration > 0) + fPIDStatusTable[idx].lastPacketNum = fTSPacketCount; + } + else { + fPIDStatusTable[idx].firstClock = fClock; + fPIDStatusTable[idx].lastPacketNum = fTSPacketCount; + } + } + } + } + fclose(input); + free(fPIDStatusTable); + free(packets); + } + + return (unsigned int)max_bitrate; +} + + +void openavbIntfMpeg2tsFileGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +unsigned int openavbIntMpeg2tsGetSrcBitrate(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + if (((pvt_data_t *)pMediaQ->pPvtIntfInfo)->enableBitrateTracking) + ((pvt_data_t *)pMediaQ->pPvtIntfInfo)->maxBitrate = openavbComputeFileBitrate(((pvt_data_t*)pMediaQ->pPvtIntfInfo)->pFileName, pMediaQ); + else + ((pvt_data_t *)pMediaQ->pPvtIntfInfo)->maxBitrate = 0; + AVB_TRACE_EXIT(AVB_TRACE_INTF); + + return ((pvt_data_t *)pMediaQ->pPvtIntfInfo)->maxBitrate; +} +// A call to this callback indicates that this interface module will be +// a talker. Any talker initialization can be done in this function. +void openavbIntfMpeg2tsFileTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + pPvtData->nRepeatCount = 0; + pPvtData->nBuffersSent = 0; + + if (!pPvtData->pFileName) { + AVB_LOG_INFO("using stdin"); + pPvtData->pFileName = strdup("stdin"); + pPvtData->pFile = stdin; + } + else { + pPvtData->pFile = fopen(pPvtData->pFileName, "rb"); + if (!pPvtData->pFile) { + AVB_LOGF_ERROR("Unable to open input file: %s", pPvtData->pFileName); + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// This callback will be called for each AVB transmit interval. Commonly this will be +// 4000 or 8000 times per second. +bool openavbIntfMpeg2tsFileTxCB(media_q_t *pMediaQ) +{ + media_q_item_t *pMediaQItem = NULL; + bool retval = FALSE; + + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + if (!pPvtData->pFile) { + // input already closed + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; + } + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + double nowSeconds = 0; + if (pPvtData->enableBitrateTracking) { + nowSeconds = (double)now.tv_sec + (double)now.tv_nsec / NANOSECONDS_PER_SECOND; + + if (nowSeconds < pPvtData->nextTransmitTime) + { + return FALSE; + } + } + + // handle end-of-file + if (feof(pPvtData->pFile)) { + if (pPvtData->pFileName && pPvtData->repeat) { + if (pPvtData->nRepeatCount < 2) + ; // No delay for first few rewinds - want to buffer some data for restarts + else if (pPvtData->repeatSeconds > (now.tv_sec - pPvtData->startTime.tv_sec) + || (pPvtData->repeatSeconds == (now.tv_sec - pPvtData->startTime.tv_sec) + && (now.tv_nsec >= pPvtData->startTime.tv_nsec))) { + // don't rewind, yet + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; + } + + AVB_LOGF_INFO("EOF, rewinding input file: %s", pPvtData->pFileName); + fseek(pPvtData->pFile, 0, 0); + + pPvtData->nRepeatCount++; + pPvtData->nBuffersSent = 0; + + if (pPvtData->enableBitrateTracking) { + // clear PCR infos here, PID hashtable, packet duration estimate + pPvtData->fTSPacketDurationEstimate = 0; + pPvtData->fTSPacketCount = 0; + pPvtData->fTSPCRCount = 0; + { + int i = 0; + for (i = 0; i < MAX_TABLE_PIDS; ++i) + { + pPvtData->fPIDStatusTable[i].used = 0; + pPvtData->fPIDStatusTable[i].pid = -1; + } + } + } + } + else { + AVB_LOGF_INFO("EOF, closing input file: %s", pPvtData->pFileName); + fclose(pPvtData->pFile); + pPvtData->pFile = NULL; + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; + } + } + + pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (!pMediaQItem) { + //AVB_LOG_ERROR("Media queue full"); + AVB_TRACE_EXIT(AVB_TRACE_MAP_DETAIL); + return FALSE; // Media queue full + } + + size_t result = fread(pMediaQItem->pPubData, 1, pMediaQItem->itemSize, pPvtData->pFile); + if (result == 0) { + int e = ferror(pPvtData->pFile); + if (e != 0) { + AVB_LOGF_ERROR("Error reading file: %s, %s", pPvtData->pFileName, strerror(e)); + fclose(pPvtData->pFile); + pPvtData->pFile = NULL; + } + pMediaQItem->dataLen = 0; + openavbMediaQHeadUnlock(pMediaQ); + } + else { + pMediaQItem->dataLen = result; + if (pPvtData->enableBitrateTracking) { + pPvtData->nextTransmitTime = nowSeconds + openavbIntfMpeg2tsFileComputeDuration(pPvtData,(unsigned char*) pMediaQItem->pPubData, pMediaQItem->dataLen); + } + openavbMediaQHeadPush(pMediaQ); + retval = TRUE; + + if (pPvtData->nBuffersSent++ == 0) { + pPvtData->startTime = now; + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return retval; +} + +double openavbIntfMpeg2tsFileComputeDuration(pvt_data_t* pPvtData, unsigned char* pkts, unsigned int length) +{ + int offset = 0; + unsigned char *pkt = NULL; + + while(pkts[offset] != 0x47) + ++offset; + + for (pkt = &pkts[offset]; pkt <= &(pkts[length-188]); pkt += 188) + { + struct timespec tvNow; + clock_gettime(CLOCK_MONOTONIC, &tvNow); + + + double timeNow = tvNow.tv_sec + tvNow.tv_nsec/NANOSECONDS_PER_SECOND; + + pPvtData->fTSPacketCount++; + + unsigned char const adaptation_field_control = (pkt[3]&0x30)>>4; + if (adaptation_field_control != 2 && adaptation_field_control != 3) continue; + // there's no adaptation_field + + unsigned char const adaptation_field_length = pkt[4]; + if (adaptation_field_length == 0) continue; + + unsigned char const discontinuity_indicator = pkt[5]&0x80; + unsigned char const pcrFlag = pkt[5]&0x10; + if (pcrFlag == 0) continue; // no PCR + + // There's a PCR. Get it, and the PID: + ++(pPvtData->fTSPCRCount); + unsigned int pcrBaseHigh = (pkt[6]<<24)|(pkt[7]<<16)|(pkt[8]<<8)|pkt[9]; + double fClock = pcrBaseHigh/(F90_KHZ/2); + if ((pkt[10]&0x80) != 0) fClock += 1/F90_KHZ; // add in low-bit (if set) + unsigned short pcrExt = ((pkt[10]&0x01)<<8) | pkt[11]; + fClock += pcrExt/F27_MHZ; + + unsigned pid = ((pkt[1]&0x1F)<<8) | pkt[2]; + int idx = pidTableFindOrCreatePid(pPvtData->fPIDStatusTable, pid); + if (!pPvtData->fPIDStatusTable[idx].used) { + // We're seeing this PID's PCR for the first time: + pPvtData->fPIDStatusTable[idx].used = 1; + pPvtData->fPIDStatusTable[idx].firstClock = fClock; + pPvtData->fPIDStatusTable[idx].lastClock = fClock; + pPvtData->fPIDStatusTable[idx].firstRealTime = timeNow; + pPvtData->fPIDStatusTable[idx].lastRealTime = timeNow; + pPvtData->fPIDStatusTable[idx].lastPacketNum = 0; + AVB_LOGF_VERBOSE("PID 0x%x, FIRST PCR 0x%08x+%d:%03x == %f @ %f, pkt #%lu\n", pid, pcrBaseHigh, pkt[10]>>7, pcrExt, fClock, timeNow, pPvtData->fTSPacketCount); + } else { + // We've seen this PID's PCR before; update our per-packet duration estimate: + double packetsSinceLast = (pPvtData->fTSPacketCount -pPvtData->fPIDStatusTable[idx].lastPacketNum); + // it's "int64_t" because some compilers can't convert "u_int64_t" -> "double" + double durationPerPacket = (fClock - pPvtData->fPIDStatusTable[idx].lastClock)/packetsSinceLast; + // Hack (suggested by "Romain"): Don't update our estimate if this PCR appeared unusually quickly. + // (This can produce more accurate estimates for wildly VBR streams.) + double meanPCRPeriod = 0.0; + if (pPvtData->fTSPCRCount > 0) { + double tsPacketCount = (double)(long long) pPvtData->fTSPacketCount; + double tsPCRCount = (double)(long long)pPvtData->fTSPCRCount; + meanPCRPeriod = tsPacketCount/tsPCRCount; + if (packetsSinceLast < meanPCRPeriod*PCR_PERIOD_VARIATION_RATIO) continue ; + } + + if (pPvtData->fTSPacketDurationEstimate == 0.0) { // we've just started + pPvtData->fTSPacketDurationEstimate = durationPerPacket; + } else if (discontinuity_indicator == 0 && durationPerPacket >= 0.0) { + pPvtData->fTSPacketDurationEstimate= durationPerPacket*NEW_DURATION_WEIGHT + pPvtData->fTSPacketDurationEstimate*(1-NEW_DURATION_WEIGHT); + + // Also adjust the duration estimate to try to ensure that the transmission + // rate matches the playout rate: + + double transmitDuration = timeNow - pPvtData->fPIDStatusTable[idx].firstRealTime; + double playoutDuration = fClock - pPvtData->fPIDStatusTable[idx].firstClock; + if (transmitDuration > playoutDuration) { + pPvtData->fTSPacketDurationEstimate *= TIME_ADJUSTMENT_FACTOR; // reduce estimate + } else if (transmitDuration + MAX_PLAYOUT_BUFFER_DURATION < playoutDuration) { + pPvtData->fTSPacketDurationEstimate /= TIME_ADJUSTMENT_FACTOR; // increase estimate + } + } else { + // the PCR has a discontinuity from its previous value; don't use it now, + // but reset our PCR and real-time values to compensate: + pPvtData->fPIDStatusTable[idx].firstClock = fClock; + pPvtData->fPIDStatusTable[idx].firstRealTime = timeNow; + } + AVB_LOGF_VERBOSE("PID 0x%x, PCKT_CNT %lu PCR 0x%08x+%d:%03x == %f @ %f (diffs %f @ %f), pkt #%lu, discon %d => this duration %f, new estimate %f, mean PCR period=%f\n", + pid, pPvtData->fTSPacketCount, pcrBaseHigh, pkt[10]>>7, pcrExt, fClock, timeNow, fClock - pPvtData->fPIDStatusTable[idx].firstClock, timeNow - pPvtData->fPIDStatusTable[idx].firstRealTime, pPvtData->fTSPacketCount, discontinuity_indicator != 0, durationPerPacket, pPvtData->fTSPacketDurationEstimate, meanPCRPeriod ); + } + pPvtData->fPIDStatusTable[idx].lastClock = fClock; + pPvtData->fPIDStatusTable[idx].lastRealTime = timeNow; + pPvtData->fPIDStatusTable[idx].lastPacketNum = pPvtData->fTSPacketCount; + } + + return pPvtData->fTSPacketDurationEstimate * ((length - offset) / 188); +} + +// A call to this callback indicates that this interface module will be +// a listener. Any listener initialization can be done in this function. +void openavbIntfMpeg2tsFileRxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (!pPvtData->pFileName) { + AVB_LOG_INFO("Using stdout"); + pPvtData->pFileName = strdup("stdout"); + pPvtData->pFile = stdout; + } + else { + pPvtData->pFile = fopen(pPvtData->pFileName, "wb"); + if (!pPvtData->pFile) { + AVB_LOGF_ERROR("Unable to open output file: %s", pPvtData->pFileName); + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// This callback is called when acting as a listener. +bool openavbIntfMpeg2tsFileRxCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + bool moreData = TRUE; + size_t written; + + while (moreData) { + media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, pPvtData->ignoreTimestamp); + if (pMediaQItem) { + while (pPvtData->pFile && pMediaQItem->dataLen > 0) { + written = fwrite(pMediaQItem->pPubData, 1, pMediaQItem->dataLen, pPvtData->pFile); + if (written == 0) { + int e = ferror(pPvtData->pFile); + AVB_LOGF_ERROR("Error writing file: %s, %s", pPvtData->pFileName, strerror(e)); + fclose(pPvtData->pFile); + pPvtData->pFile = NULL; + } + else { + pMediaQItem->dataLen -= written; + } + } + openavbMediaQTailPull(pMediaQ); + } + else { + moreData = FALSE; + } + } + } + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return TRUE; +} + +// This callback will be called when the interface needs to be closed. All shutdown should +// occur in this function. +void openavbIntfMpeg2tsFileEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (pPvtData->pFile) { + fclose(pPvtData->pFile); + pPvtData->pFile = NULL; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +void openavbIntfMpeg2tsFileGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (pPvtData->pFileName) { + free(pPvtData->pFileName); + pPvtData->pFileName = NULL; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Main initialization entry point into the interface module +extern DLL_EXPORT bool openavbIntfMpeg2tsFileInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + int i = 0; + pMediaQ->pPvtIntfInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pPvtIntfInfo) { + AVB_LOG_ERROR("Unable to allocate memory for AVTP interface module."); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + + pIntfCB->intf_cfg_cb = openavbIntfMpeg2tsFileCfgCB; + pIntfCB->intf_gen_init_cb = openavbIntfMpeg2tsFileGenInitCB; + pIntfCB->intf_tx_init_cb = openavbIntfMpeg2tsFileTxInitCB; + pIntfCB->intf_tx_cb = openavbIntfMpeg2tsFileTxCB; + pIntfCB->intf_rx_init_cb = openavbIntfMpeg2tsFileRxInitCB; + pIntfCB->intf_rx_cb = openavbIntfMpeg2tsFileRxCB; + pIntfCB->intf_end_cb = openavbIntfMpeg2tsFileEndCB; + pIntfCB->intf_gen_end_cb = openavbIntfMpeg2tsFileGenEndCB; + pIntfCB->intf_get_src_bitrate_cb = openavbIntMpeg2tsGetSrcBitrate; + + pPvtData->ignoreTimestamp = FALSE; + + pPvtData->fPIDStatusTable = (struct PIDStatus*) calloc(MAX_TABLE_PIDS, sizeof(struct PIDStatus)); + + if (NULL == pPvtData->fPIDStatusTable) { + AVB_LOG_ERROR("Unable to allocate memory for PID status table."); + return FALSE; + } + + for (i = 0; i < MAX_TABLE_PIDS; ++i) + { + pPvtData->fPIDStatusTable[i].pid = -1; + } + pPvtData->enableBitrateTracking = 1; + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return TRUE; +} diff --git a/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/CMakeLists.txt b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/CMakeLists.txt new file mode 100644 index 00000000..cd37f416 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/CMakeLists.txt @@ -0,0 +1,9 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_OSAL_DIR}/intf_mpeg2ts_gst/openavb_intf_mpeg2ts_gst.c + PARENT_SCOPE +) + +# Need include and link directories for gstreamer +SET (INTF_INCLUDE_DIR ${INTF_INCLUDE_DIR} ${GLIB_PKG_INCLUDE_DIRS} ${GST_PKG_INCLUDE_DIRS} PARENT_SCOPE) +SET (INTF_LIBRARY_DIR ${INTF_LIBRARY_DIR} PARENT_SCOPE) +SET (INTF_LIBRARY ${GLIB_PKG_LIBRARIES} ${GST_PKG_LIBRARIES} PARENT_SCOPE) diff --git a/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/mpeg2ts_gst_intf.md b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/mpeg2ts_gst_intf.md new file mode 100644 index 00000000..61a1a260 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/mpeg2ts_gst_intf.md @@ -0,0 +1,16 @@ +MPEG2TS GStreamer interface {#mpeg2ts_gst_intf} +============================ + +# Description + +Mpeg2 TS GStreamer interface module. + +<br> +# Interface module configuration parameters + +Name | Description +--------------------------|--------------------------- +intf_nv_gst_pipeline |GStreamer pipeline to be used +intf_nv_ignore_timestamp | If set to 1 timestamps will be ignored during \ + processing of frames. This also means stale (old) \ + Media Queue items will not be purged. diff --git a/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/mpeg2ts_gst_listener.ini b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/mpeg2ts_gst_listener.ini new file mode 100644 index 00000000..68ae616a --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/mpeg2ts_gst_listener.ini @@ -0,0 +1,114 @@ +##################################################################### +# General Listener configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = listener + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +stream_addr = 84:7E:40:2B:73:D6 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: When SRP is being used the destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set in both side the talker and listener. +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# At this time they need to be locally administered and must be in the range +# of 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically :00 for the first stream, :01 for the second, etc. +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +#max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +#sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +#max_transit_usec = 2000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_mpeg2ts.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapMpeg2tsInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +intf_lib = ./libopenavb_intf_mpeg2ts_gst.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfMpeg2tsGstInitialize + +# intf_nv_file_name: The fully qualified file name used both the talker and listener. +intf_nv_file_name = output.ts + +# intf_nv_repeat: Continually repeat the file stream when running as a talker. +#intf_nv_repeat = 0 + +# intf_nv_ignore_timestamp: If set the listener will ignore the timestamp on media queue items. +intf_nv_ignore_timestamp = 1 + +#gstreamer pipeline +intf_nv_gst_pipeline = appsrc name=avbsrc ! queue ! mpegtsdemux ! h264parse ! omx_h264dec ! omx_scaler ! omx_ctrl display-mode=OMX_DC_MODE_1080P_60 ! gstperf ! omx_videosink sync=false + +# alternate pipe to test that file is transferred +#intf_nv_gst_pipeline = appsrc name=avbsrc ! filesink location=/dev/shm/listen.ts diff --git a/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/mpeg2ts_gst_talker.ini b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/mpeg2ts_gst_talker.ini new file mode 100644 index 00000000..fa48e7a3 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/mpeg2ts_gst_talker.ini @@ -0,0 +1,129 @@ +##################################################################### +# General Talker configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 00:25:64:48:ca:a8 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: When SRP is being used the destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set in both side the talker and listener. +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# At this time they need to be locally administered and must be in the range +# of 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically :00 for the first stream, :01 for the second, etc. +dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +#sr_class = B + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 2000 + +# max_transmit_deficit_usec: Allows setting the maximum packet transmit rate deficit that will +# be recovered when a talker falls behind. This is only used on a talker side. When a talker +# can not keep up with the specified transmit rate it builds up a deficit and will attempt to +# make up for this deficit by sending more packets. There is normally some variability in the +# transmit rate because of other demands on the system so this is expected. However, without this +# bounding value the deficit could grew too large in cases such where more streams are started +# than the system can support and when the number of streams is reduced the remaining streams +# will attempt to recover this deficit by sending packets at a higher rate. This can cause a problem +# at the listener side and significantly delay the recovery time before media playback will return +# to normal. Typically this value can be set to the expected buffer size (in usec) that listeners are +# expected to be buffering. For low latency solutions this is normally a small value. For non-live +# media playback such as video playback the listener side buffers can often be large enough to held many +# seconds of data. +max_transmit_deficit_usec = 50000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_mpeg2ts.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapMpeg2tsInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +# map_nv_tx_rate: Transmit rate +# If not set default of the talker class will be used. +#map_nv_tx_rate = 2000 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +# intf_fn: The name of the initialize function in the interface. +intf_lib = ./libopenavb_intf_mpeg2ts_gst.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfMpeg2tsGstInitialize + +# intf_nv_file_name: The fully qualified file name used both the talker and listener. +#intf_nv_file_name = input.ts +#intf_nv_file_name = hbo_trailer_6ksd_converted.ts + +# intf_nv_repeat: Continually repeat the file stream when running as a talker. +#intf_nv_repeat = 0 + +#gstreamer pipeline +intf_nv_gst_pipeline = v4l2src ! video/x-raw-yuv,width=320,height=240 ! ffmpegcolorspace ! omx_h264enc ! queue ! h264parse ! mpegtsmux m2ts_mode=true ! appsink name=avbsink + diff --git a/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/openavb_intf_mpeg2ts_gst.c b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/openavb_intf_mpeg2ts_gst.c new file mode 100644 index 00000000..59656771 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_mpeg2ts_gst/openavb_intf_mpeg2ts_gst.c @@ -0,0 +1,552 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* + * MODULE SUMMARY : Mpeg2 TS Gstreamer interface module. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <gst/gst.h> +#include <gst/app/gstappsink.h> +#include <gst/app/gstappsrc.h> +#include "openavb_types_pub.h" +#include "openavb_log_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_intf_pub.h" + +#define APPSINK_NAME "avbsink" +#define APPSRC_NAME "avbsrc" + +typedef struct { + ///////////// + // Config data + ///////////// + char *pPipelineStr; + + bool ignoreTimestamp; + + ///////////// + // Variable data + ///////////// + GstElement *pipe; + GstBus *bus; + GstAppSink *appsink; + GstAppSrc *appsrc; + + // talker: number of gstreamer buffers waiting to be pulled + gint nWaiting; + // listener: whether gstreamer wants more pushed data now + bool srcPaused; + +} pvt_data_t; + + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbIntfMpeg2tsGstCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + char *pEnd; + unsigned long tmp; + bool nameOK = TRUE, valueOK = FALSE; + + if (strcmp(name, "intf_nv_gst_pipeline") == 0) { + if (pPvtData->pPipelineStr) + free(pPvtData->pPipelineStr); + pPvtData->pPipelineStr = strdup(value); + valueOK = (value != NULL && strlen(value) > 0); + } + else if (strcmp(name, "intf_nv_ignore_timestamp") == 0) { + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && pEnd != value && (tmp == 0 || tmp == 1)) { + pPvtData->ignoreTimestamp = (tmp == 1); + valueOK = TRUE; + } + } + else { + AVB_LOGF_WARNING("Unknown configuration item: %s", name); + nameOK = FALSE; + } + + if (nameOK && !valueOK) { + AVB_LOGF_WARNING("Bad value for configuration item: %s = %s", name, value); + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); +} + +void openavbIntfMpeg2tsGstGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); +} + +static gboolean +bus_message(GstBus *bus, GstMessage *message, void *pv) +{ + switch (GST_MESSAGE_TYPE(message)) { + case GST_MESSAGE_ERROR: + { + GError *err = NULL; + gchar *dbg_info = NULL; + + gst_message_parse_error(message, &err, &dbg_info); + AVB_LOGF_ERROR("GStreamer ERROR message from element %s: %s", + GST_OBJECT_NAME(message->src), + err->message); + AVB_LOGF_ERROR("Additional info: %s\n", (dbg_info) ? dbg_info : "none"); + g_error_free(err); + g_free(dbg_info); + break; + } + case GST_MESSAGE_WARNING: + { + GError *err = NULL; + gchar *dbg_info = NULL; + + gst_message_parse_warning(message, &err, &dbg_info); + AVB_LOGF_WARNING("GStreamer WARNING message from element %s: %s", + GST_OBJECT_NAME(message->src), + err->message); + AVB_LOGF_WARNING("Additional info: %s\n", (dbg_info) ? dbg_info : "none"); + g_error_free(err); + g_free(dbg_info); + break; + } + case GST_MESSAGE_INFO: + { + GError *err = NULL; + gchar *dbg_info = NULL; + + gst_message_parse_info(message, &err, &dbg_info); + AVB_LOGF_ERROR("GStreamer INFO message from element %s: %s", + GST_OBJECT_NAME(message->src), + err->message); + AVB_LOGF_ERROR("Additional info: %s\n", (dbg_info) ? dbg_info : "none"); + g_error_free(err); + g_free(dbg_info); + break; + } + case GST_MESSAGE_STATE_CHANGED: + { + GstState old_state, new_state; + gst_message_parse_state_changed(message, &old_state, &new_state, NULL); + AVB_LOGF_DEBUG("Element %s changed state from %s to %s", + GST_OBJECT_NAME(message->src), + gst_element_state_get_name(old_state), + gst_element_state_get_name(new_state)); + break; + } + case GST_MESSAGE_STREAM_STATUS: + { + // not so valuable + break; + } + case GST_MESSAGE_EOS: + AVB_LOG_INFO("EOS received"); + break; + default: + AVB_LOGF_INFO("GStreamer '%s' message from element %s", + gst_message_type_get_name(GST_MESSAGE_TYPE(message)), + GST_OBJECT_NAME(message->src)); + break; + } + + return TRUE; +} + +// This callback triggers when appsrc needs data. +static void srcStartFeed (GstAppSrc *source, guint size, gpointer pv) { + media_q_t *pMediaQ = (media_q_t *)pv; + assert(pMediaQ); + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + assert(pPvtData); + if (pPvtData->srcPaused) { +// AVB_LOG_INFO("Restart"); + pPvtData->srcPaused = FALSE; + } +} + +// This callback triggers when appsrc has enough data and we can stop sending. +static void srcStopFeed (GstAppSrc *source, gpointer pv) { + media_q_t *pMediaQ = (media_q_t *)pv; + assert(pMediaQ); + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + assert(pPvtData); +// AVB_LOG_INFO("Pause"); + pPvtData->srcPaused = TRUE; +} + +static GstFlowReturn sinkNewBuffer(GstAppSink *sink, gpointer pv) +{ + assert(pv); + media_q_t *pMediaQ = (media_q_t *)pv; + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + assert(pPvtData); + + g_atomic_int_add(&pPvtData->nWaiting, 1); + + return GST_FLOW_OK; +} + + +// A call to this callback indicates that this interface module will be +// a talker. Any talker initialization can be done in this function. +void openavbIntfMpeg2tsGstTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + pPvtData->pipe = (GstElement*)NULL; + pPvtData->appsink = (GstAppSink*)NULL; + pPvtData->appsrc = (GstAppSrc*)NULL; + pPvtData->bus = (GstBus*)NULL; + pPvtData->nWaiting = 0; + + GError *error = NULL; + pPvtData->pipe = gst_parse_launch(pPvtData->pPipelineStr, &error); + if (error) { + AVB_LOGF_ERROR("Error creating pipeline: %s", error->message); + return; + } + + AVB_LOGF_INFO("Pipeline: %s", pPvtData->pPipelineStr); + pPvtData->appsink = GST_APP_SINK(gst_bin_get_by_name(GST_BIN(pPvtData->pipe), APPSINK_NAME)); + if (!pPvtData->appsink) { + AVB_LOG_ERROR("Failed to find appsink element"); + return; + } + + // create bus + pPvtData->bus = gst_pipeline_get_bus(GST_PIPELINE(pPvtData->pipe)); + if (!pPvtData->bus) { + AVB_LOG_ERROR("Failed to create bus"); + return; + } + + /* add callback for bus messages */ + gst_bus_add_watch(pPvtData->bus, (GstBusFunc)bus_message, pMediaQ); + + // Setup callback function to handle new buffers delivered to sink + GstAppSinkCallbacks cbfns; + memset(&cbfns, 0, sizeof(GstAppSinkCallbacks)); + cbfns.new_buffer = sinkNewBuffer; + gst_app_sink_set_callbacks(pPvtData->appsink, &cbfns, (gpointer)(pMediaQ), NULL); + + // Set most capabilities in pipeline (config), not code + + // Don't drop buffers + g_object_set(pPvtData->appsink, "drop", 0, NULL); + + // Start playing + gst_element_set_state(pPvtData->pipe, GST_STATE_PLAYING); + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); +} + +bool openavbIntfMpeg2tsGstTxCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + + if (!pMediaQ) { + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + if (!pPvtData->appsink) { + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; + } + + media_q_item_t *pMediaQItem; + GstBuffer *txBuf; + + while (g_atomic_int_get(&pPvtData->nWaiting) > 0) { + + // Get a mediaQItem to hold the buffered data + pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (!pMediaQItem) { + //AVB_LOG_ERROR("Media queue full"); + break; + } + + /* Retrieve the buffer + */ + txBuf = gst_app_sink_pull_buffer(pPvtData->appsink); + if (txBuf) { + g_atomic_int_add(&pPvtData->nWaiting, -1); + + if ( GST_BUFFER_SIZE(txBuf) > pMediaQItem->itemSize ) { + AVB_LOGF_ERROR("GStreamer buffer too large (size=%d) for mediaQ item (dataLen=%d)", + GST_BUFFER_SIZE(txBuf), pMediaQItem->itemSize); + pMediaQItem->dataLen = 0; + openavbMediaQHeadUnlock(pMediaQ); + } + else { + memcpy(pMediaQItem->pPubData, GST_BUFFER_DATA(txBuf), GST_BUFFER_SIZE(txBuf)); + pMediaQItem->dataLen = GST_BUFFER_SIZE(txBuf); + openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime); + openavbMediaQHeadPush(pMediaQ); + } + + gst_buffer_unref(txBuf); + } + else { + AVB_LOG_ERROR("GStreamer buffer pull failed"); + // assume the pipeline is empty + g_atomic_int_set(&pPvtData->nWaiting, 0); + // abandon the mediaq item + pMediaQItem->dataLen = 0; + openavbMediaQHeadUnlock(pMediaQ); + // and get out + break; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return TRUE; +} + + +// A call to this callback indicates that this interface module will be +// a listener. Any listener initialization can be done in this function. +void openavbIntfMpeg2tsGstRxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + pPvtData->pipe = (GstElement*)NULL; + pPvtData->appsink = (GstAppSink*)NULL; + pPvtData->appsrc = (GstAppSrc*)NULL; + pPvtData->bus = (GstBus*)NULL; + pPvtData->srcPaused = FALSE; + + GError *error = NULL; + pPvtData->pipe = gst_parse_launch(pPvtData->pPipelineStr, &error); + if (error) { + AVB_LOGF_ERROR("Error creating pipeline: %s", error->message); + return; + } + + AVB_LOGF_INFO("Pipeline: %s", pPvtData->pPipelineStr); + pPvtData->appsrc = GST_APP_SRC(gst_bin_get_by_name(GST_BIN(pPvtData->pipe), APPSRC_NAME)); + if (!pPvtData->appsrc) { + AVB_LOG_ERROR("Failed to find appsrc element"); + return; + } + + // Make appsrc non-blocking + g_object_set(G_OBJECT(pPvtData->appsrc), "block", FALSE, NULL); + + // create bus + pPvtData->bus = gst_pipeline_get_bus(GST_PIPELINE(pPvtData->pipe)); + if (!pPvtData->bus) { + AVB_LOG_ERROR("Failed to create bus"); + return; + } + + /* add callback for bus messages */ + gst_bus_add_watch(pPvtData->bus, (GstBusFunc)bus_message, pMediaQ); + + // Setup callback function to handle request from src to pause/start data flow + GstAppSrcCallbacks cbfns; + memset(&cbfns, 0, sizeof(GstAppSrcCallbacks)); + cbfns.enough_data = srcStopFeed; + cbfns.need_data = srcStartFeed; + gst_app_src_set_callbacks(pPvtData->appsrc, &cbfns, (gpointer)(pMediaQ), NULL); + + // Set most capabilities in pipeline (config), not code + + // Don't block + g_object_set(pPvtData->appsrc, "block", 0, NULL); + + // PLAY + gst_element_set_state(pPvtData->pipe, GST_STATE_PLAYING); + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); +} + +// This callback is called when acting as a listener. +bool openavbIntfMpeg2tsGstRxCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + + if (!pMediaQ) { + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + if (!pPvtData->appsrc) { + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; + } + + bool moreData = TRUE; + bool retval = TRUE; + + while (moreData) { + media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, pPvtData->ignoreTimestamp); + if (pMediaQItem) { + unsigned long len = pMediaQItem->dataLen; + if (len > 0) { + GstBuffer *rxBuf = gst_buffer_new_and_alloc(len); + if (rxBuf) { + GST_BUFFER_TIMESTAMP(rxBuf) = GST_CLOCK_TIME_NONE; + GST_BUFFER_DURATION(rxBuf) = GST_CLOCK_TIME_NONE; + memcpy(GST_BUFFER_DATA(rxBuf), pMediaQItem->pPubData, GST_BUFFER_SIZE(rxBuf)); + GstFlowReturn gstret = gst_app_src_push_buffer(GST_APP_SRC(pPvtData->appsrc), rxBuf); + if (gstret != GST_FLOW_OK) { + AVB_LOGF_ERROR("Pushing buffer to gstreamer failed: %d", gstret); + gst_buffer_unref(rxBuf); + retval = moreData = FALSE; + } + } + else { + AVB_LOG_ERROR("Failed to get gstreamer buffer"); + retval = moreData = FALSE; + } + } + openavbMediaQTailPull(pMediaQ); + } else { + moreData = FALSE; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return retval; +} + +// This callback will be called when the interface needs to be closed. All shutdown should +// occur in this function. +void openavbIntfMpeg2tsGstEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (pPvtData->pipe) { + gst_element_set_state(GST_ELEMENT(pPvtData->pipe), GST_STATE_NULL); + if (pPvtData->bus) { + gst_object_unref(pPvtData->bus); + pPvtData->bus = NULL; + } + if (pPvtData->appsink) { + gst_object_unref(pPvtData->appsink); + pPvtData->appsink = NULL; + } + if (pPvtData->appsrc) { + gst_object_unref(pPvtData->appsrc); + pPvtData->appsrc = NULL; + } + gst_object_unref(pPvtData->pipe); + pPvtData->pipe = NULL; + } + } + + // Media Queue destroy cleans up all of our allocated data. + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); +} + +void openavbIntfMpeg2tsGstGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Main initialization entry point into the interface module +extern DLL_EXPORT bool openavbIntfMpeg2tsGstInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pMediaQ->pPvtIntfInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pPvtIntfInfo) { + AVB_LOG_ERROR("Unable to allocate memory for AVTP interface module."); + return FALSE; + } + + //pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + + pIntfCB->intf_cfg_cb = openavbIntfMpeg2tsGstCfgCB; + pIntfCB->intf_gen_init_cb = openavbIntfMpeg2tsGstGenInitCB; + pIntfCB->intf_tx_init_cb = openavbIntfMpeg2tsGstTxInitCB; + pIntfCB->intf_tx_cb = openavbIntfMpeg2tsGstTxCB; + pIntfCB->intf_rx_init_cb = openavbIntfMpeg2tsGstRxInitCB; + pIntfCB->intf_rx_cb = openavbIntfMpeg2tsGstRxCB; + pIntfCB->intf_end_cb = openavbIntfMpeg2tsGstEndCB; + pIntfCB->intf_gen_end_cb = openavbIntfMpeg2tsGstGenEndCB; + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return TRUE; +} diff --git a/lib/avtp_pipeline/platform/Linux/intf_wav_file/CMakeLists.txt b/lib/avtp_pipeline/platform/Linux/intf_wav_file/CMakeLists.txt new file mode 100644 index 00000000..8ca81a8a --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_wav_file/CMakeLists.txt @@ -0,0 +1,9 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_OSAL_DIR}/intf_wav_file/openavb_intf_wav_file.c + PARENT_SCOPE +) + +# Need include and link directories +SET (INTF_INCLUDE_DIR ${INTF_INCLUDE_DIR} PARENT_SCOPE) +SET (INTF_LIBRARY_DIR ${INTF_LIBRARY_DIR} PARENT_SCOPE) +SET (INTF_LIBRARY ${INTF_LIBRARY} PARENT_SCOPE) diff --git a/lib/avtp_pipeline/platform/Linux/intf_wav_file/openavb_intf_wav_file.c b/lib/avtp_pipeline/platform/Linux/intf_wav_file/openavb_intf_wav_file.c new file mode 100644 index 00000000..be8474fb --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_wav_file/openavb_intf_wav_file.c @@ -0,0 +1,698 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : wav File interface module. Talker only. +* +* This interface module is narrowly focused to read a common wav file format +* and send the data samples to mapping modules. +* +*/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/stat.h> +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_map_uncmp_audio_pub.h" +#include "openavb_map_aaf_audio_pub.h" +#include "openavb_intf_pub.h" + +#define AVB_LOG_COMPONENT "Wav File Interface" +#include "openavb_log_pub.h" + +typedef struct { + // RIFF Chunk descriptor + U8 chunkID[4]; // "RIFF" + U32 chunkSize; + U8 format[4]; // "WAVE" + + // FMT Sub Chuck + U8 subChunk1ID[4]; // "fmt " + U32 subChunk1Size; + U16 audioFormat; // 1 = PCM + U16 numberChannels; + U32 sampleRate; + U32 byteRate; + U16 blockAlign; + U16 bitsPerSample; + + // Data Sub Chunk + U8 subChunk2ID[4]; // "data" + U32 subChunk2Size; + +} wav_file_header_t; + + +typedef struct { + ///////////// + // Config data + ///////////// + // intf_nv_file_name: The fully qualified file name used both the talker and listener. + char *pFileName; + + ///////////// + // Variable data + ///////////// + FILE *pFile; + + // ALSA read/write interval + U32 intervalCounter; + + // map_nv_audio_rate + avb_audio_rate_t audioRate; + + // map_nv_audio_bit_depth + avb_audio_bit_depth_t audioBitDepth; + + // map_nv_channels + avb_audio_channels_t audioChannels; + + // map_nv_audio_endian + avb_audio_endian_t audioEndian; + + // intf_nv_number_of_data_bytes + U32 numberOfDataBytes; + +} pvt_data_t; + +// fread that (mostly) ignores return value - to silence compiler warnings +static inline void ifread(void *ptr, size_t size, size_t num, FILE *stream) +{ + if (fread(ptr, size, num, stream) != num) { + AVB_LOG_DEBUG("Error reading file"); + } +} + +static inline void ifwrite(void *ptr, size_t size, size_t num, FILE *stream) +{ + if (fwrite(ptr, size, num, stream) != num) { + AVB_LOG_DEBUG("Error writting to file"); + } +} + +static void x_parseWaveFile(media_q_t *pMediaQ) +{ + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (!pPvtData->pFileName) { + AVB_LOG_ERROR("Input file name not found."); + return; + } + + pPvtData->pFile = fopen(pPvtData->pFileName, "rb"); + if (!pPvtData->pFile) { + AVB_LOGF_ERROR("Unable to open input file: %s", pPvtData->pFileName); + return; + } + + // Check if wav file format is valid and of a supported type. + wav_file_header_t wavFileHeader; + + // RIFF Chunk + ifread(wavFileHeader.chunkID, sizeof(wavFileHeader.chunkID), 1, pPvtData->pFile); + ifread(&wavFileHeader.chunkSize, sizeof(wavFileHeader.chunkSize), 1, pPvtData->pFile); + ifread(wavFileHeader.format, sizeof(wavFileHeader.format), 1, pPvtData->pFile); + + // FMT sub Chunk + ifread(wavFileHeader.subChunk1ID, sizeof(wavFileHeader.subChunk1ID), 1, pPvtData->pFile); + ifread(&wavFileHeader.subChunk1Size, sizeof(wavFileHeader.subChunk1Size), 1, pPvtData->pFile); + ifread(&wavFileHeader.audioFormat, sizeof(wavFileHeader.audioFormat), 1, pPvtData->pFile); + ifread(&wavFileHeader.numberChannels, sizeof(wavFileHeader.numberChannels), 1, pPvtData->pFile); + ifread(&wavFileHeader.sampleRate, sizeof(wavFileHeader.sampleRate), 1, pPvtData->pFile); + ifread(&wavFileHeader.byteRate, sizeof(wavFileHeader.byteRate), 1, pPvtData->pFile); + ifread(&wavFileHeader.blockAlign, sizeof(wavFileHeader.blockAlign), 1, pPvtData->pFile); + ifread(&wavFileHeader.bitsPerSample, sizeof(wavFileHeader.bitsPerSample), 1, pPvtData->pFile); + + // Data sub Chunk + ifread(wavFileHeader.subChunk2ID, sizeof(wavFileHeader.subChunk2ID), 1, pPvtData->pFile); + ifread(&wavFileHeader.subChunk2Size, sizeof(wavFileHeader.subChunk2Size), 1, pPvtData->pFile); + + AVB_LOGF_INFO("Number of data bytes:%d", wavFileHeader.subChunk2Size); + + // Make sure wav file format is supported + if (memcmp(wavFileHeader.chunkID, "RIFF", 4) != 0) { + AVB_LOGF_ERROR("%s does not appear to be a supported wav file.", pPvtData->pFileName); + fclose(pPvtData->pFile); + pPvtData->pFile = NULL; + return; + } + + if (memcmp(wavFileHeader.format, "WAVE", 4) != 0) { + AVB_LOGF_ERROR("%s does not appear to be a supported wav file.", pPvtData->pFileName); + fclose(pPvtData->pFile); + pPvtData->pFile = NULL; + return; + } + + if (memcmp(wavFileHeader.subChunk1ID, "fmt ", 4) != 0) { + AVB_LOGF_ERROR("%s does not appear to be a supported wav file.", pPvtData->pFileName); + fclose(pPvtData->pFile); + pPvtData->pFile = NULL; + return; + } + + if (memcmp(wavFileHeader.subChunk2ID, "data", 4) != 0) { + AVB_LOGF_ERROR("%s does not appear to be a supported wav file.", pPvtData->pFileName); + fclose(pPvtData->pFile); + pPvtData->pFile = NULL; + return; + } + + if (wavFileHeader.audioFormat != 1) { + AVB_LOGF_ERROR("%s does not appear to be a supported wav file.", pPvtData->pFileName); + fclose(pPvtData->pFile); + pPvtData->pFile = NULL; + return; + } + + // Give the audio parameters to the mapping module. + if (pMediaQ->pMediaQDataFormat) { + if (strcmp(pMediaQ->pMediaQDataFormat, MapUncmpAudioMediaQDataFormat) == 0 + || strcmp(pMediaQ->pMediaQDataFormat, MapAVTPAudioMediaQDataFormat) == 0) { + media_q_pub_map_uncmp_audio_info_t *pPubMapUncmpAudioInfo; + pPubMapUncmpAudioInfo = (media_q_pub_map_uncmp_audio_info_t *)pMediaQ->pPubMapInfo; + pPubMapUncmpAudioInfo->audioRate = wavFileHeader.sampleRate; + pPubMapUncmpAudioInfo->audioBitDepth = wavFileHeader.bitsPerSample; + pPubMapUncmpAudioInfo->audioChannels = wavFileHeader.numberChannels; + pPubMapUncmpAudioInfo->audioEndian = pPvtData->audioEndian; + + AVB_LOGF_INFO("Wav file - Rate:%d Bits:%d Channels:%d", pPubMapUncmpAudioInfo->audioRate, pPubMapUncmpAudioInfo->audioBitDepth, pPubMapUncmpAudioInfo->audioChannels); + } + else { + AVB_LOG_ERROR("MediaQ mapping format not supported in this interface module."); + } + } + else { + AVB_LOG_ERROR("MediaQ mapping format not defined."); + } + } +} + +static void passParamToMapModule(media_q_t *pMediaQ) +{ + if (pMediaQ) { + media_q_pub_map_uncmp_audio_info_t *pPubMapUncmpAudioInfo = (media_q_pub_map_uncmp_audio_info_t *)pMediaQ->pPubMapInfo; + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + + if (!pPubMapUncmpAudioInfo) { + AVB_LOG_ERROR("Public interface module data not allocated."); + return; + } + + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (pMediaQ->pMediaQDataFormat) { + if (strcmp(pMediaQ->pMediaQDataFormat, MapUncmpAudioMediaQDataFormat) == 0 + || strcmp(pMediaQ->pMediaQDataFormat, MapAVTPAudioMediaQDataFormat) == 0) { + pPubMapUncmpAudioInfo->audioRate = pPvtData->audioRate; + pPubMapUncmpAudioInfo->audioBitDepth = pPvtData->audioBitDepth; + pPubMapUncmpAudioInfo->audioChannels = pPvtData->audioChannels; + pPubMapUncmpAudioInfo->audioEndian = pPvtData->audioEndian; + } + } + } +} + +// CORE_TODO : this version of convertEndianess is in the commit process but didn't appear to work as expected. +// As part of a separate merge the function below this one was pulled in which does work as expected. +#if 0 +// little <-> big endian conversion: copy bytes of each +// sample in reverse order back into the buffer +static void convertEndianness(uint8_t *pData, U32 dataLen, U32 sampleSize) +{ + U32 sampleByteIndex = sampleSize; + U32 itemIndex, readIndex = 0; + U8 itemData[dataLen]; + for (itemIndex = 0; itemIndex < dataLen; itemIndex++) { + sampleByteIndex--; + itemData[itemIndex] = *(pData + readIndex + sampleByteIndex); + if (sampleByteIndex == 0) { + sampleByteIndex = sampleSize; + readIndex = itemIndex + 1; + } + } + memcpy(pData, itemData, dataLen); +} +#endif + + +#define SWAPU16(x) (((x) >> 8) | ((x) << 8)) +#define SWAPU32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24)) +static void convertEndianness(uint8_t *pData, U32 dataLen, U32 sampleSize) +{ + if (sampleSize == 2) { + int i1; + int cnt = dataLen >> 1; // 2 bytes at a time + U16 *pData16 = (U16 *)pData; + for (i1 = 0; i1 < cnt; i1++) { + *pData16 = SWAPU16(*pData16); + pData16++; + } + } + else if (sampleSize == 4) { + int i1; + int cnt = dataLen >> 1; // 2 bytes at a time + U32 *pData32 = (U32 *)pData; + for (i1 = 0; i1 < cnt; i1++) { + *pData32 = SWAPU32(*pData32); + pData32++; + } + } +} + +// Each configuration name value pair for this mapping will result in this callback being called. +void openavbIntfWavFileCfgCB(media_q_t *pMediaQ, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + char *pEnd; + U32 val; + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (strcmp(name, "intf_nv_file_name") == 0) { + if (pPvtData->pFileName) { + free(pPvtData->pFileName); + } + pPvtData->pFileName = strdup(value); + x_parseWaveFile(pMediaQ); + } + else if (strcmp(name, "intf_nv_file_name_rx") == 0) { + if (pPvtData->pFileName) { + free(pPvtData->pFileName); + } + pPvtData->pFileName = strdup(value); + passParamToMapModule(pMediaQ); + } + else if (strcmp(name, "intf_nv_audio_rate") == 0) { + val = strtol(value, &pEnd, 10); + if (val >= AVB_AUDIO_RATE_8KHZ && val <= AVB_AUDIO_RATE_192KHZ) { + pPvtData->audioRate = val; + } + else { + AVB_LOG_ERROR("Invalid audio rate configured for intf_nv_audio_rate."); + pPvtData->audioRate = AVB_AUDIO_RATE_44_1KHZ; + } + } + else if (strcmp(name, "intf_nv_audio_bit_depth") == 0) { + val = strtol(value, &pEnd, 10); + if (val >= AVB_AUDIO_BIT_DEPTH_1BIT && val <= AVB_AUDIO_BIT_DEPTH_64BIT) { + pPvtData->audioBitDepth = val; + } + else { + AVB_LOG_ERROR("Invalid audio type configured for intf_nv_audio_bits."); + pPvtData->audioBitDepth = AVB_AUDIO_BIT_DEPTH_24BIT; + } + } + else if (strcmp(name, "intf_nv_audio_channels") == 0) { + val = strtol(value, &pEnd, 10); + if (val >= AVB_AUDIO_CHANNELS_1 && val <= AVB_AUDIO_CHANNELS_8) { + pPvtData->audioChannels = val; + } + else { + AVB_LOG_ERROR("Invalid audio channels configured for intf_nv_audio_channels."); + pPvtData->audioChannels = AVB_AUDIO_CHANNELS_2; + } + } + else if (strcmp(name, "intf_nv_number_of_data_bytes") == 0) { + val = strtol(value, &pEnd, 10); + if (val > 0) { + pPvtData->numberOfDataBytes = val; + } + else { + AVB_LOG_ERROR("Invalid number of data bytes for intf_nv_number_of_data_bytes."); + } + } + else if (strcmp(name, "intf_nv_audio_endian") == 0) { + if (strncasecmp(value, "big", 3) == 0) { + pPvtData->audioEndian = AVB_AUDIO_ENDIAN_BIG; + AVB_LOG_INFO("Forced audio samples endian conversion: little <-> big"); + } + } + } + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +void openavbIntfWavFileGenInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (strcmp(pMediaQ->pMediaQDataFormat, MapAVTPAudioMediaQDataFormat) == 0) { + // WAV files are little-endian, AAF (SAF) need big-endian + pPvtData->audioEndian = AVB_AUDIO_ENDIAN_BIG; + } + else { + pPvtData->audioEndian = AVB_AUDIO_ENDIAN_LITTLE; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// A call to this callback indicates that this interface module will be +// a talker. Any talker initialization can be done in this function. +void openavbIntfWavFileTxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + // U8 b; + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (!pPvtData->pFile) { + pPvtData->pFile = fopen(pPvtData->pFileName, "rb"); + if (!pPvtData->pFile) { + AVB_LOGF_ERROR("Unable to open input file: %s", pPvtData->pFileName); + return; + } + } + + if (pPvtData->pFile) { + // Seek to start of data for our only supported wav file format. + fseek(pPvtData->pFile, 44, 0); + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// This callback will be called for each AVB transmit interval. Commonly this will be +// 4000 or 8000 times per second. +bool openavbIntfWavFileTxCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + if (pMediaQ) { + media_q_pub_map_uncmp_audio_info_t *pPubMapUncmpAudioInfo = pMediaQ->pPubMapInfo; + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + media_q_item_t *pMediaQItem = NULL; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + //put current wall time into tail item used by AAF maping module + if ((pPubMapUncmpAudioInfo->sparseMode != TS_SPARSE_MODE_UNSPEC)) { + pMediaQItem = openavbMediaQTailLock(pMediaQ, TRUE); + if ((pMediaQItem) && (pPvtData->intervalCounter % pPubMapUncmpAudioInfo->sparseMode == 0)) { + openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime); + } + openavbMediaQTailUnlock(pMediaQ); + pMediaQItem = NULL; + } + + if (pPvtData->intervalCounter++ % pPubMapUncmpAudioInfo->packingFactor != 0) + return TRUE; + + pMediaQItem = openavbMediaQHeadLock(pMediaQ); + if (pMediaQItem) { + if (pMediaQItem->itemSize < pPubMapUncmpAudioInfo->itemSize) { + AVB_LOG_ERROR("Media queue item not large enough for samples"); + } + + if (pPvtData->pFile) { + + U32 bytesRead = fread(pMediaQItem->pPubData, 1, pPubMapUncmpAudioInfo->itemSize, pPvtData->pFile); + + if (bytesRead < pPubMapUncmpAudioInfo->itemSize) { + // Pad reminder of item with anything we didn't read because of end of file. + memset(pMediaQItem->pPubData + bytesRead, 0x00, pPubMapUncmpAudioInfo->itemSize - bytesRead); + + // Repeat wav file. Seek to start of data for our only supported wav file format. + fseek(pPvtData->pFile, 44, 0); + } + pMediaQItem->dataLen = pPubMapUncmpAudioInfo->itemSize; + + if (pPvtData->audioEndian == AVB_AUDIO_ENDIAN_BIG) { + convertEndianness((uint8_t *)pMediaQItem->pPubData, pMediaQItem->dataLen, pPubMapUncmpAudioInfo->itemSampleSizeBytes); + } + + openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime); + openavbMediaQHeadPush(pMediaQ); + + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return TRUE; + } + else { + openavbMediaQHeadUnlock(pMediaQ); + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; // No File handle + } + } + else { + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; // Media queue full + } + } + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return FALSE; +} + +// A call to this callback indicates that this interface module will be +// a listener. Any listener initialization can be done in this function. +void openavbIntfWavFileRxInitCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + struct stat buf; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (!pPvtData->pFileName) { + AVB_LOG_ERROR("Output wav file not provided in ini"); + AVB_TRACE_EXIT(AVB_TRACE_INTF); + } + else if (stat(pPvtData->pFileName, &buf) == 0) { + AVB_LOGF_ERROR("Will not open output file: %s file exists", pPvtData->pFileName); + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + else { + AVB_LOGF_INFO("Creating output wav file: %s", pPvtData->pFileName); + pPvtData->pFile = fopen(pPvtData->pFileName, "wb"); + if (!pPvtData->pFile) { + AVB_LOGF_ERROR("Unable to open output wav file: %s", pPvtData->pFileName); + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return; + } + wav_file_header_t wavFileHeader; + memcpy(wavFileHeader.chunkID, "RIFF", sizeof(wavFileHeader.chunkID)); + memcpy(wavFileHeader.format, "WAVE", sizeof(wavFileHeader.format)); + memcpy(wavFileHeader.subChunk1ID, "fmt ", sizeof(wavFileHeader.subChunk1ID)); + wavFileHeader.subChunk1Size = 0x10; + wavFileHeader.audioFormat = 0x01; + wavFileHeader.numberChannels = pPvtData->audioChannels; + wavFileHeader.sampleRate = pPvtData->audioRate; + wavFileHeader.bitsPerSample = pPvtData->audioBitDepth; + wavFileHeader.byteRate = wavFileHeader.sampleRate * wavFileHeader.numberChannels * wavFileHeader.bitsPerSample/8; + wavFileHeader.blockAlign = wavFileHeader.numberChannels * wavFileHeader.bitsPerSample/8; + memcpy(wavFileHeader.subChunk2ID, "data", sizeof(wavFileHeader.subChunk2ID)); + wavFileHeader.subChunk2Size = pPvtData->numberOfDataBytes; + wavFileHeader.chunkSize = 4 + (8 + wavFileHeader.subChunk1Size) + (8 + wavFileHeader.subChunk2Size); + + ifwrite(&wavFileHeader.chunkID, sizeof(wavFileHeader.chunkID), 1, pPvtData->pFile); + ifwrite(&wavFileHeader.chunkSize, sizeof(wavFileHeader.chunkSize), 1, pPvtData->pFile); + ifwrite(wavFileHeader.format, sizeof(wavFileHeader.format), 1, pPvtData->pFile); + ifwrite(&wavFileHeader.subChunk1ID, sizeof(wavFileHeader.subChunk1ID), 1, pPvtData->pFile); + ifwrite(&wavFileHeader.subChunk1Size, sizeof(wavFileHeader.subChunk1Size), 1, pPvtData->pFile); + ifwrite(&wavFileHeader.audioFormat, sizeof(wavFileHeader.audioFormat), 1, pPvtData->pFile); + ifwrite(&wavFileHeader.numberChannels, sizeof(wavFileHeader.numberChannels), 1, pPvtData->pFile); + ifwrite(&wavFileHeader.sampleRate, sizeof(wavFileHeader.sampleRate), 1, pPvtData->pFile); + ifwrite(&wavFileHeader.byteRate, sizeof(wavFileHeader.byteRate), 1, pPvtData->pFile); + ifwrite(&wavFileHeader.blockAlign, sizeof(wavFileHeader.blockAlign), 1, pPvtData->pFile); + ifwrite(&wavFileHeader.bitsPerSample, sizeof(wavFileHeader.bitsPerSample), 1, pPvtData->pFile); + ifwrite(wavFileHeader.subChunk2ID, sizeof(wavFileHeader.subChunk2ID), 1, pPvtData->pFile); + ifwrite(&wavFileHeader.subChunk2Size, sizeof(wavFileHeader.subChunk2Size), 1, pPvtData->pFile); + } + } + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// This callback is called when acting as a listener. +bool openavbIntfWavFileRxCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + media_q_pub_map_uncmp_audio_info_t *pPubMapUncmpAudioInfo = pMediaQ->pPubMapInfo; + + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return FALSE; + } + + bool moreData = TRUE; + size_t written; + static U32 numOfStoredDataBytes = 0; //total number of data bytes stored in file so far + static bool fileReady = FALSE; //set when writing to file is finished + bool expectedNumberOfDataReceived = FALSE; //set when expected number of data bytes has been received + + while (moreData) { + media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, TRUE); + if ((pMediaQItem) && (fileReady == FALSE)) { + if (pPvtData->pFile && pMediaQItem->dataLen > 0) { + if (expectedNumberOfDataReceived == FALSE) { + if ((numOfStoredDataBytes + pMediaQItem->dataLen ) > pPvtData->numberOfDataBytes) { + pMediaQItem->dataLen = pPvtData->numberOfDataBytes - numOfStoredDataBytes; + expectedNumberOfDataReceived = TRUE; + } + } + if (pPvtData->audioEndian == AVB_AUDIO_ENDIAN_BIG) { + convertEndianness((uint8_t *)pMediaQItem->pPubData, pMediaQItem->dataLen, pPubMapUncmpAudioInfo->itemSampleSizeBytes); + } + written = fwrite(pMediaQItem->pPubData, 1, pMediaQItem->dataLen, pPvtData->pFile); + if (written != pMediaQItem->dataLen) { + int e = ferror(pPvtData->pFile); + AVB_LOGF_ERROR("Error writing file: %s, %s", pPvtData->pFileName, strerror(e)); + fclose(pPvtData->pFile); + pPvtData->pFile = NULL; + } + else { + pMediaQItem->dataLen = 0; + numOfStoredDataBytes += written; + if (expectedNumberOfDataReceived == TRUE) { + fileReady = TRUE; + AVB_LOG_INFO("Wav file ready."); + } + } + } + openavbMediaQTailPull(pMediaQ); + } + else { + moreData = FALSE; + } + } + } + AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); + return TRUE; +} + +// This callback will be called when the interface needs to be closed. All shutdown should +// occur in this function. +void openavbIntfWavFileEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (pPvtData->pFile) { + fclose(pPvtData->pFile); + pPvtData->pFile = NULL; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +void openavbIntfWavFileGenEndCB(media_q_t *pMediaQ) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + if (!pPvtData) { + AVB_LOG_ERROR("Private interface module data not allocated."); + return; + } + + if (pPvtData->pFileName) { + free(pPvtData->pFileName); + pPvtData->pFileName = NULL; + } + } + AVB_TRACE_EXIT(AVB_TRACE_INTF); +} + +// Main initialization entry point into the interface module +extern DLL_EXPORT bool openavbIntfWavFileInitialize(media_q_t *pMediaQ, openavb_intf_cb_t *pIntfCB) +{ + AVB_TRACE_ENTRY(AVB_TRACE_INTF); + + if (pMediaQ) { + pMediaQ->pPvtIntfInfo = calloc(1, sizeof(pvt_data_t)); // Memory freed by the media queue when the media queue is destroyed. + + if (!pMediaQ->pPvtIntfInfo) { + AVB_LOG_ERROR("Unable to allocate memory for AVTP interface module."); + return FALSE; + } + + pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; + + pIntfCB->intf_cfg_cb = openavbIntfWavFileCfgCB; + pIntfCB->intf_gen_init_cb = openavbIntfWavFileGenInitCB; + pIntfCB->intf_tx_init_cb = openavbIntfWavFileTxInitCB; + pIntfCB->intf_tx_cb = openavbIntfWavFileTxCB; + pIntfCB->intf_rx_init_cb = openavbIntfWavFileRxInitCB; + pIntfCB->intf_rx_cb = openavbIntfWavFileRxCB; + pIntfCB->intf_end_cb = openavbIntfWavFileEndCB; + pIntfCB->intf_gen_end_cb = openavbIntfWavFileGenEndCB; + pPvtData->audioEndian = AVB_AUDIO_ENDIAN_LITTLE; //wave file default + + pPvtData->intervalCounter = 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_INTF); + return TRUE; +} diff --git a/lib/avtp_pipeline/platform/Linux/intf_wav_file/wav_file_intf.md b/lib/avtp_pipeline/platform/Linux/intf_wav_file/wav_file_intf.md new file mode 100644 index 00000000..3d409d3d --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_wav_file/wav_file_intf.md @@ -0,0 +1,45 @@ +WAV file interface {#wav_file_intf} +================== + +# Description + +WAV file interface module. + +This interface module is narrowly focused to read a common wav file format +and send the data samples to mapping modules. + +<br> +# Interface module configuration parameters + +Name | Description +--------------------------|--------------------------- +intf_nv_file_name |Name of the file to be read +intf_nv_file_name_rx |Name of wav file where received data will be stored +intf_nv_audio_rate |Audio rate, numberic values defined by \ + @ref avb_audio_rate_t +intf_nv_audio_bit_depth |Bit depth of audio, numeric values defined by \ + @ref avb_audio_bit_depth_t +intf_nv_audio_channels |Number of audio channels, numeric values should be \ + within range of values in @ref avb_audio_channels_t +intf_nv_number_of_data_bytes|Size of sample data counted in bytes. This size \ + should be equal to Subchunk2Size field in wav file\ + to be transferred. The data is printed out by \ + talker when started (INFO: Number of data bytes) + +<br> +# Notes + +There are some parameters that have to be set during configuration of this +interface module and before configuring mapping: +* [AAF audio mapping](@ref aaf_audio_map) +* [Uncompressed audio mapping](@ref uncmp_audio_map) + +These parameters can be set in either: +* in the ini file (or available configuration system), so values will be parsed +in the intf_cfg_cb function, +* in the interface module initialization function where valid values can be +assigned directly + +Values assigned in the intf_cfg_cb function will override any values set in the +initialization function. + diff --git a/lib/avtp_pipeline/platform/Linux/intf_wav_file/wav_file_listener.ini b/lib/avtp_pipeline/platform/Linux/intf_wav_file/wav_file_listener.ini new file mode 100644 index 00000000..565ce467 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_wav_file/wav_file_listener.ini @@ -0,0 +1,112 @@ +##################################################################### +# General wav file listener configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = listener + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +stream_addr = 84:7E:40:2C:1b:3a + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: When SRP is being used the destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set in both side the talker and listener. +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# At this time they need to be locally administered and must be in the range +# of 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically :00 for the first stream, :01 for the second, etc. +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +#max_interval_frames = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 50000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_uncmp_audio.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapUncmpAudioInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 400 + +# map_nv_tx_rate: Transmit rate. +# This must be set for the uncompressed audio mapping module. +map_nv_tx_rate = 8000 + +# map_nv_packing_factor: Multiple of how many packets of audio frames to place in a media queue item. +# Note: Typically when decreasing the map_nv_tx_rate the packing factor will also be decreased since +# the number of frames per packet will be increasing. +map_nv_packing_factor = 256 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +# intf_fn: The name of the initialize function in the interface. +intf_lib = ./libopenavb_intf_wav_file.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfWavFileInitialize + +# intf_nv_ignore_timestamp: If set the listener will ignore the timestamp on media queue items. +intf_nv_ignore_timestamp = 1 + +# intf_nv_audio_rate: Audio sample rate, must be consistenet with wav file sample rate +intf_nv_audio_rate = 48000 + +# intf_nv_audio_bit_depth: must be consistenet with wav file bit depth +intf_nv_audio_bit_depth = 16 + +# intf_nv_audio_channels: must be consistenet with wav file audio channels +intf_nv_audio_channels = 1 + +#intf_nv_number_of_data_bytes: Size of sample data counted in bytes. This field should be equal to Subchunk2Size field +#in wav file to be transferred. This data is printed out by talker when started with wav interface. +# see: INFO: Number of data bytes: +intf_nv_number_of_data_bytes = 9600 + +#intf_nv_file_name_rx: Name of wav file where received data will be stored. Finally it should be the same as +#original wav file transferred by talker. +intf_nv_file_name_rx = wavfileout.wav + diff --git a/lib/avtp_pipeline/platform/Linux/intf_wav_file/wav_file_talker.ini b/lib/avtp_pipeline/platform/Linux/intf_wav_file/wav_file_talker.ini new file mode 100644 index 00000000..ead3bbfc --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/intf_wav_file/wav_file_talker.ini @@ -0,0 +1,128 @@ +##################################################################### +# General Talker configuration +##################################################################### +# role: Sets the process as a talker or listener. Valid values are +# talker or listener +role = talker + +# stream_addr: Used on the listener and should be set to the +# mac address of the talker. +#stream_addr = 00:25:64:48:ca:a8 + +# stream_uid: The unique stream ID. The talker and listener must +# both have this set the same. +stream_uid = 1 + +# dest_addr: destination multicast address for the stream. +# +# If using SRP and MAAP, dynamic destination addresses are generated +# automatically by the talker and passed to the listner, and don't +# need to be configured. +# +# Without MAAP, locally administered (static) addresses must be +# configured. Thouse addresses are in the range of: +# 91:E0:F0:00:FE:00 - 91:E0:F0:00:FE:FF. +# Typically use :00 for the first stream, :01 for the second, etc. +# +# When SRP is being used the static destination address only needs to +# be set in the talker. If SRP is not being used the destination address +# needs to be set (to the same value) in both the talker and listener. +# +# The destination is a multicast address, not a real MAC address, so it +# does not match the talker or listener's interface MAC. There are +# several pools of those addresses for use by AVTP defined in 1722. +# +#dest_addr = 91:e0:f0:00:fe:00 + +# max_interval_frames: The maximum number of packets that will be sent during +# an observation interval. This is only used on the talker. +max_interval_frames = 1 + +# sr_class: A talker only setting. Values are either A or B. If not set an internal +# default is used. +sr_class = A + +# sr_rank: A talker only setting. If not set an internal default is used. +#sr_rank = 1 + +# max_transit_usec: Allows manually specifying a maximum transit time. +# On the talker this value is added to the PTP walltime to create the AVTP Timestamp. +# On the listener this value is used to validate an expected valid timestamp range. +# Note: For the listener the map_nv_item_count value must be set large enough to +# allow buffering at least as many AVTP packets that can be transmitted during this +# max transit time. +max_transit_usec = 2000 + +# internal_latency: Allows mannually specifying an internal latency time. This is used +# only on the talker. +#internal_latency = 0 + +# max_stale: The number of microseconds beyond the presentation time that media queue items will be purged +# because they are too old (past the presentation time). This is only used on listener end stations. +# Note: needing to purge old media queue items is often a sign of some other problem. For example: a delay at +# stream startup before incoming packets are ready to be processed by the media sink. If this deficit +# in processing or purging the old (stale) packets is not handled, syncing multiple listeners will be problematic. +#max_stale = 1000 + +# raw_tx_buffers: The number of raw socket transmit buffers. Typically 4 - 8 are good values. +# This is only used by the talker. If not set internal defaults are used. +#raw_tx_buffers = 1 + +# raw_rx_buffers: The number of raw socket receive buffers. Typically 50 - 100 are good values. +# This is only used by the listener. If not set internal defaults are used. +#raw_rx_buffers = 100 + +# report_seconds: How often to output stats. Defaults to 10 seconds. 0 turns off the stats. +# report_seconds = 0 + +# Ethernet Interface Name. Only needed on some platforms when stack is built with no endpoint functionality +# ifname = eth0 + +##################################################################### +# Mapping module configuration +##################################################################### +# map_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the map_lib name +# and link in the .c file to the openavb_tl executable to embed the mapper +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +map_lib = ./libopenavb_map_uncmp_audio.so + +# map_fn: The name of the initialize function in the mapper. +map_fn = openavbMapUncmpAudioInitialize + +# map_nv_item_count: The number of media queue elements to hold. +map_nv_item_count = 20 + +# map_nv_tx_rate: Transmit rate. +# This must be set for the uncompressed audio mapping module. +map_nv_tx_rate = 8000 + +# map_nv_packing_factor: Multiple of how many packets of audio frames to place in a media queue item. +# Note: Typically when decreasing the map_nv_tx_rate the packing factor will also be decreased since +# the number of frames per packet will be increasing. +map_nv_packing_factor = 32 + +##################################################################### +# Interface module configuration +##################################################################### +# intf_lib: The name of the library file (commonly a .so file) that +# implements the Initialize function. Comment out the intf_lib name +# and link in the .c file to the openavb_tl executable to embed the interface +# directly into the executable unit. There is no need to change anything +# else. The Initialize function will still be dynamically linked in. +# intf_fn: The name of the initialize function in the interface. +intf_lib = ./libopenavb_intf_wav_file.so + +# intf_fn: The name of the initialize function in the interface. +intf_fn = openavbIntfWavFileInitialize + +# intf_nv_file_name: The fully qualified file name. +intf_nv_file_name = song1.wav + + + + + + + diff --git a/lib/avtp_pipeline/platform/Linux/openavb_ether_osal.c b/lib/avtp_pipeline/platform/Linux/openavb_ether_osal.c new file mode 100644 index 00000000..81792e8c --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/openavb_ether_osal.c @@ -0,0 +1,39 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#include "openavb_ether_osal.h"
+
+// MAC address is retrieved differently for Linux
+bool osalGetMacAddr(U8 *macAddr)
+{
+ return TRUE;
+}
+
+
diff --git a/lib/avtp_pipeline/platform/Linux/openavb_ether_osal.h b/lib/avtp_pipeline/platform/Linux/openavb_ether_osal.h new file mode 100644 index 00000000..33965963 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/openavb_ether_osal.h @@ -0,0 +1,40 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+ +#ifndef OPENAVB_ETHER_OSAL_H +#define OPENAVB_ETHER_OSAL_H 1 + +#include "openavb_types_base.h" +#include "openavb_osal.h" + +struct ether_addr* ether_aton_r(const char *asc, struct ether_addr *addr); +bool osalGetMacAddr(U8 *macAddr); + +#endif // OPENAVB_ETHER_OSAL_H diff --git a/lib/avtp_pipeline/platform/Linux/openavb_os_services_osal.h b/lib/avtp_pipeline/platform/Linux/openavb_os_services_osal.h new file mode 100644 index 00000000..e119c84d --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/openavb_os_services_osal.h @@ -0,0 +1,169 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#ifndef _OPENAVB_OS_SERVICES_OSAL_H +#define _OPENAVB_OS_SERVICES_OSAL_H + +#include "openavb_hal.h" + +#include <unistd.h> +#include <pthread.h> +#include <signal.h> +#include <time.h> +#include <semaphore.h> +#include <arpa/inet.h> +#include <errno.h> +#include <sys/mman.h> +#include <poll.h> +#include <fcntl.h> +#include <net/if.h> +#include <dlfcn.h> + +#include "openavb_tasks.h" + +#define EXTERN_DLL_EXPORT extern DLL_EXPORT + +#ifndef PTHREAD_MUTEX_RECURSIVE +#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP +#endif + +#include "openavb_osal_pub.h" + +// Uncomment to use manual data alignment adjustments. Not needed for Linux +//#define DATA_ALIGNMENT_ADJUSTMENT 1 + +// Many socket implementations support a minimum timeout of 1ms (value 1000 here). +#define RAWSOCK_MIN_TIMEOUT_USEC 1 + +#define SLEEP(sec) sleep(sec) +#define SLEEP_MSEC(mSec) usleep(mSec * 1000) +#define SLEEP_NSEC(nSec) usleep(nSec) +#define SLEEP_UNTIL_NSEC(nSec) xSleepUntilNSec(nSec) +inline static void xSleepUntilNSec(U64 nSec) +{ + struct timespec tmpTime; + tmpTime.tv_sec = nSec / NANOSECONDS_PER_SECOND; + tmpTime.tv_nsec = nSec % NANOSECONDS_PER_SECOND; + clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &tmpTime, NULL); +} + + + +#define RAND() random() +#define SRAND(seed) srandom(seed) + +#define PRAGMA_ALIGN_8 + +#define SIGNAL_CALLBACK_SETUP(__NAM, __CB) \ + struct sigaction __NAM; \ + __NAM.sa_handler = __CB + +#define SIGNAL_SIGNAL_SETUP(__SIG, __NAM, __CB, __ERR) \ + sigemptyset(&__NAM.sa_mask); \ + __NAM.sa_flags = 0; \ + __ERR = sigaction(__SIG, &__NAM, NULL) + + +// the following macros define thread related items +#define THREAD_TYPE(thread) \ +typedef struct \ +{ \ + pthread_t pthread; \ + int err; \ +} thread##_type; + +#define THREAD_DEFINITON(thread) \ +thread##_type thread##_ThreadData + +#define THREAD_CREATE(threadName, threadhandle, thread_attr_name, thread_function, thread_function_arg) \ + { \ + pthread_attr_t thread_attr; \ + do { \ + threadhandle##_ThreadData.err = pthread_attr_init(&thread_attr); \ + if (threadhandle##_ThreadData.err) break; \ + threadhandle##_ThreadData.err = pthread_attr_setstacksize(&thread_attr, threadName##_THREAD_STK_SIZE); \ + if (threadhandle##_ThreadData.err) break; \ + threadhandle##_ThreadData.err = pthread_create( \ + (pthread_t*)&threadhandle##_ThreadData.pthread, \ + &thread_attr, \ + thread_function, \ + (void*)thread_function_arg); \ + } while (0); \ + pthread_attr_destroy(&thread_attr); \ + } + +#define THREAD_CHECK_ERROR(threadhandle, message, error) \ + do { \ + error=FALSE; \ + if (threadhandle##_ThreadData.err != 0) \ + { \ + AVB_LOGF_ERROR("Thread error: %s code:", message, threadhandle##_ThreadData.err); \ + error=TRUE; \ + break; \ + } \ + } while (0) + +#define THREAD_STARTTHREAD(err) +#define THREAD_KILL(threadhandle, signal) pthread_kill(threadhandle##_ThreadData.pthread, signal) +#define THREAD_JOINABLE(threadhandle) +#define THREAD_JOIN(threadhandle, signal) pthread_join(threadhandle##_ThreadData.pthread, (void**)signal) +#define THREAD_SLEEP(threadhandle, secs) sleep(secs) + + +#define SEM_T(sem) sem_t sem; +#define SEM_ERR_T(err) int err; +#define SEM_INIT(sem, init, err) err = sem_init(&sem, 0, init); +#define SEM_WAIT(sem, err) err = sem_wait(&sem); +#define SEM_TIMEDWAIT(sem, timeoutMSec, err) \ +{ \ + struct timespec timeout; \ + CLOCK_GETTIME(OPENAVB_CLOCK_REALTIME, &timeout); \ + openavbTimeTimespecAddUsec(&timeout, timeoutMSec * MICROSECONDS_PER_MSEC); \ + err = sem_timedwait(&sem, &timeout); \ +} +#define SEM_POST(sem, err) err = sem_post(&sem); +#define SEM_DESTROY(sem, err) err = sem_destroy(&sem); +#define SEM_IS_ERR_NONE(err) (0 == err) +#define SEM_IS_ERR_TIMEOUT(err) (ETIMEDOUT == err) +#define SEM_LOG_ERR(err) if (0 != err) AVB_LOGF_ERROR("Semaphore error code: %d", err); + + +typedef struct +{ + char *libName; + char *funcName; + void *libHandle; +} link_lib_t; + +#define LINK_LIB(library) \ +link_lib_t library + +#endif // _OPENAVB_OS_SERVICES_OSAL_H + diff --git a/lib/avtp_pipeline/platform/Linux/openavb_os_services_osal_pub.h b/lib/avtp_pipeline/platform/Linux/openavb_os_services_osal_pub.h new file mode 100644 index 00000000..19d47b36 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/openavb_os_services_osal_pub.h @@ -0,0 +1,109 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#ifndef _OPENAVB_OS_SERVICES_OSAL_PUB_H +#define _OPENAVB_OS_SERVICES_OSAL_PUB_H + +#define LINUX 1 // !!! FIX ME !!! THIS IS A HACK TO SUPPORT ANOTHER HACK IN openavb_avtp_time.c. REMOVE THIS WHEN openavb_avtp_time.c GETS FIXED !!! + +#include "openavb_time_osal_pub.h" + +#define INLINE_VARIABLE_NUM_OF_ARGUMENTS inline // must be okay of gcc + +#include <netinet/ether.h> +#include <netinet/in.h> +#include <unistd.h> +#include <pthread.h> +#include <math.h> + + +#define STD_LOG stderr + +#define NEWLINE "\n" + +#define SIN(rad) sin(rad) + +#define THREAD_SELF() pthread_self() +#define GET_PID() getpid() + + +// Funky struct to hold a configurable ethernet address (MAC). +// The "mac" pointer is null if no config value was supplied, +// and points to the configured value if one was +typedef struct { + struct ether_addr buffer; + struct ether_addr *mac; +} cfg_mac_t; + +#define MUTEX_ATTR_TYPE_DEFAULT PTHREAD_MUTEX_DEFAULT +#define MUTEX_ATTR_TYPE_RECURSIVE PTHREAD_MUTEX_RECURSIVE +#define MUTEX_HANDLE(mutex_handle) pthread_mutex_t mutex_handle +#define MUTEX_ATTR_HANDLE(mutex_attr_name) pthread_mutexattr_t mutex_attr_name +#define MUTEX_ATTR_CREATE_ERR() static mutex_err +#define MUTEX_ATTR_INIT(mutex_attr_name) pthread_mutexattr_init(&mutex_attr_name) +#define MUTEX_ATTR_SET_TYPE(mutex_attr_name,type) pthread_mutexattr_settype(&mutex_attr_name, type) +#define MUTEX_ATTR_SET_NAME(mutex_attr_name, name) +#define MUTEX_CREATE_ERR() int mutex_err +#define MUTEX_CREATE(mutex_handle,mutex_attr_name) mutex_err=pthread_mutex_init(&mutex_handle, &mutex_attr_name) +#define MUTEX_LOCK(mutex_handle) mutex_err=pthread_mutex_lock(&mutex_handle) +#define MUTEX_UNLOCK(mutex_handle) mutex_err=pthread_mutex_unlock(&mutex_handle) +#define MUTEX_DESTROY(mutex_handle) mutex_err=pthread_mutex_destroy(&mutex_handle) +#define MUTEX_LOG_ERR(message) if (mutex_err) AVB_LOG_ERROR(message); +#define MUTEX_IS_ERR (mutex_err != 0) + +// Alternate simplified mutex mapping +#define MUTEX_HANDLE_ALT(mutex_handle) pthread_mutex_t mutex_handle +#define MUTEX_CREATE_ALT(mutex_handle) pthread_mutex_init(&mutex_handle, NULL) +#define MUTEX_LOCK_ALT(mutex_handle) pthread_mutex_lock(&mutex_handle) +#define MUTEX_UNLOCK_ALT(mutex_handle) pthread_mutex_unlock(&mutex_handle) +#define MUTEX_DESTROY_ALT(mutex_handle) pthread_mutex_destroy(&mutex_handle) + + +// pthread_mutexattr_t mta; +// pthread_mutexattr_init(&mta); +// pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE); +// pthread_mutex_init(&maapMutex, &mta); + +#if defined __BYTE_ORDER && defined __BIG_ENDIAN && defined __LITTLE_ENDIAN +#if __BYTE_ORDER == __BIG_ENDIAN +# define ntohll(x) (x) +# define htonll(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +# define ntohll(x) __bswap_64 (x) +# define htonll(x) __bswap_64 (x) +#else +# error +#endif +#else +# error +#endif + +#endif // _OPENAVB_OS_SERVICES_OSAL_PUB_H + diff --git a/lib/avtp_pipeline/platform/Linux/openavb_osal.c b/lib/avtp_pipeline/platform/Linux/openavb_osal.c new file mode 100644 index 00000000..4ebfe1ba --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/openavb_osal.c @@ -0,0 +1,57 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#include "openavb_platform.h"
+#include "openavb_ether_hal.h"
+#include "openavb_osal.h"
+#include "openavb_qmgr.h"
+
+#define AVB_LOG_COMPONENT "osal"
+#include "openavb_pub.h"
+#include "openavb_log.h"
+
+extern DLL_EXPORT bool osalAVBInitialize(void)
+{
+ avbLogInit();
+ halEthernetInitialize(NULL, TRUE);
+ osalAVBTimeInit();
+ openavbQmgrInitialize(FQTSS_MODE_HW_CLASS, 0, NULL, 0, 0, 0);
+ return TRUE;
+}
+
+extern DLL_EXPORT bool osalAVBFinalize(void)
+{
+ openavbQmgrFinalize();
+ osalAVBTimeClose();
+ halEthernetFinalize();
+ avbLogExit();
+ return TRUE;
+}
+
diff --git a/lib/avtp_pipeline/platform/Linux/openavb_osal.h b/lib/avtp_pipeline/platform/Linux/openavb_osal.h new file mode 100644 index 00000000..79dc8c1a --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/openavb_osal.h @@ -0,0 +1,41 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#ifndef _OPENAVB_OSAL_H +#define _OPENAVB_OSAL_H + +#include "openavb_hal.h" +#include "openavb_os_services_osal.h" +#include "openavb_tasks.h" +#include "openavb_osal_pub.h" + + +#endif // _OPENAVB_OSAL_H + diff --git a/lib/avtp_pipeline/platform/Linux/openavb_osal_pub.h b/lib/avtp_pipeline/platform/Linux/openavb_osal_pub.h new file mode 100644 index 00000000..4ce91c81 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/openavb_osal_pub.h @@ -0,0 +1,44 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#ifndef _OPENAVB_OSAL_PUB_H +#define _OPENAVB_OSAL_PUB_H + +// TODO_OPENAVB - Is this needed still? +#define LINUX 1 // !!! FIX ME !!! THIS IS A HACK TO SUPPORT ANOTHER HACK IN openavb_avtp_time.c. REMOVE THIS WHEN openavb_avtp_time.c GETS FIXED !!! + +#include "openavb_os_services_osal_pub.h" + +bool osalAVBInitialize(void); + +bool osalAVBFinalize(void); + +#endif // _OPENAVB_OSAL_PUB_H + diff --git a/lib/avtp_pipeline/platform/Linux/openavb_poll_osal.h b/lib/avtp_pipeline/platform/Linux/openavb_poll_osal.h new file mode 100644 index 00000000..4b78730e --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/openavb_poll_osal.h @@ -0,0 +1,77 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#ifndef __OPENAVB_POLL_OSAL_H___ +#define __OPENAVB_POLL_OSAL_H___ + + +#include "openavb_platform.h" + +#define OPENAVB_POLLFD_TYPE struct pollfd + +#define OPENAVB_DECLARE_POLL_RET int ret + +#define OPENAVB_POLL(CNT) \ + { \ + int i; \ + for (i = 0; i < CNT; i++) { \ + pfd[i].revents = 0; \ + } \ + } \ + ret = poll(pfd, CNT, 500); \ + if (ret > 0) + +#define OPENAVB_POLLIN(X) (pfd[X].revents & POLLIN) +#define OPENAVB_POLLERR(X) (pfd[X].revents & POLLERR) +#define OPENAVB_POLLIN_OR_ERR(X) (pfd[X].revents & (POLLIN | POLLERR)) + +#define OPENAVB_READPFD(X) \ + { \ + uint64_t nExpire; \ + int ret = read(pfd[X].fd, &nExpire, sizeof(uint64_t)); \ + if (ret != sizeof(uint64_t)) { \ + AVB_LOGF_WARNING("timerfd read failed (%s)", strerror(errno)); \ + } \ + } \ + +#define OPENAVB_CHECK_LOG_POLL_RESULT \ + if (ret < 0) { \ + if (errno == EAGAIN) { \ + AVB_LOG_STATUS("EAGAIN polling receieve socket"); \ + continue; \ + } \ + if (errno != EINTR) { \ + AVB_LOGF_ERROR("Error polling receive socket: %s", strerror(errno)); \ + } \ + } + + +#endif // __OPENAVB_POLL_OSAL_H___ + diff --git a/lib/avtp_pipeline/platform/Linux/openavb_tasks.h b/lib/avtp_pipeline/platform/Linux/openavb_tasks.h new file mode 100644 index 00000000..1e222ed9 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/openavb_tasks.h @@ -0,0 +1,106 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#ifndef _EAVBTASKS_H +#define _EAVBTASKS_H + +#define THREAD_STACK_SIZE 65536 + +/////////////////////////// +// Platform code Tasks values +/////////////////////////// + +/////////////////////////// +// Common code Tasks values +/////////////////////////// + +//task rcvThread. SRP +#define rcvThread_THREAD_STK_SIZE 32768 + +//task txThread. SRP +#define txThread_THREAD_STK_SIZE 32768 + +//task maapThread +#define maapThread_THREAD_STK_SIZE 16384 + +//task loggingThread +#define loggingThread_THREAD_STK_SIZE THREAD_STACK_SIZE + +//task TLThread Used for both Talker and Listener threads +#define TLThread_THREAD_STK_SIZE THREAD_STACK_SIZE + +//task TalkerThread +#define talkerThread_THREAD_STK_SIZE THREAD_STACK_SIZE + +//task ListenerThread +#define listenerThread_THREAD_STK_SIZE THREAD_STACK_SIZE + +//task openavbAecpSMEntityModelEntityThread +#define openavbAecpSMEntityModelEntityThread_THREAD_STK_SIZE THREAD_STACK_SIZE + +//task openavbAdpSmAdvertiseEntityThread +#define openavbAdpSmAdvertiseEntityThread_THREAD_STK_SIZE THREAD_STACK_SIZE + +//task openavbAcmpSmListenerThread +#define openavbAcmpSmListenerThread_THREAD_STK_SIZE THREAD_STACK_SIZE + +//task openavbAecpMessageRxThread +#define openavbAecpMessageRxThread_THREAD_STK_SIZE THREAD_STACK_SIZE + +//task openavbAdpMessageRxThread +#define openavbAdpMessageRxThread_THREAD_STK_SIZE THREAD_STACK_SIZE + +//task openavbAdpSmAdvertiseInterfaceThread +#define openavbAdpSmAdvertiseInterfaceThread_THREAD_STK_SIZE THREAD_STACK_SIZE + +//task openavbAcmpMessageRxThread +#define openavbAcmpMessageRxThread_THREAD_STK_SIZE THREAD_STACK_SIZE + +//task openavbAcmpSmTalkerThread +#define openavbAcmpSmTalkerThread_THREAD_STK_SIZE THREAD_STACK_SIZE + +//task openavbAcmpSmTalkerThread +#define openavbAcmpSmTalkerThread_THREAD_STK_SIZE THREAD_STACK_SIZE + + + + + + + + + + + + + + + +#endif // _EAVBTASKS_H diff --git a/lib/avtp_pipeline/platform/Linux/openavb_time_osal.c b/lib/avtp_pipeline/platform/Linux/openavb_time_osal.c new file mode 100644 index 00000000..0e99778f --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/openavb_time_osal.c @@ -0,0 +1,206 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#include <inttypes.h>
+#include <linux/ptp_clock.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+
+#include "igb.h"
+#include "avb.h"
+
+#include "openavb_platform.h"
+#include "openavb_time_osal.h"
+#include "openavb_time_hal.h"
+#include "openavb_trace.h"
+
+#define AVB_LOG_COMPONENT "osalTime"
+#include "openavb_pub.h"
+#include "openavb_log.h"
+
+//#include "openavb_time_util_osal.h"
+
+static pthread_mutex_t gOSALTimeInitMutex = PTHREAD_MUTEX_INITIALIZER;
+#define LOCK() pthread_mutex_lock(&gOSALTimeInitMutex)
+#define UNLOCK() pthread_mutex_unlock(&gOSALTimeInitMutex)
+
+static bool bInitialized = FALSE;
+static int gIgbShmFd = -1;
+static char *gIgbMmap = NULL;
+static gPtpTimeData gPtpTD;
+
+static bool x_timeInit(void) {
+ AVB_TRACE_ENTRY(AVB_TRACE_TIME);
+
+ if (!halTimeInitialize()) {
+ AVB_LOG_ERROR("HAL Time Init failed");
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return FALSE;
+ }
+
+ if (!gptpinit(&gIgbShmFd, &gIgbMmap)) {
+ AVB_LOG_ERROR("GPTP init failed");
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return FALSE;
+ }
+
+ if (!gptpscaling(&gPtpTD, gIgbMmap)) {
+ AVB_LOG_ERROR("GPTP scaling failed");
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return FALSE;
+ }
+
+ AVB_LOGF_INFO("local_time = %llu", gPtpTD.local_time);
+ AVB_LOGF_INFO("ml_phoffset = %lld, ls_phoffset = %lld", gPtpTD.ml_phoffset, gPtpTD.ls_phoffset);
+ AVB_LOGF_INFO("ml_freqffset = %Lf, ls_freqoffset = %Lf", gPtpTD.ml_freqoffset, gPtpTD.ls_freqoffset);
+
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return TRUE;
+}
+
+static bool x_getPTPTime(U64 *timeNsec) {
+ AVB_TRACE_ENTRY(AVB_TRACE_TIME);
+
+ // TODO_OPENAVB : There may be a reasonable way to avoid calling this each time. Really only need to update it after gPTP SYNC.
+ if (!gptpscaling(&gPtpTD, gIgbMmap)) {
+ AVB_LOG_ERROR("GPTP scaling failed");
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return FALSE;
+ }
+
+ uint64_t now_local;
+ uint64_t update_8021as;
+ unsigned delta_8021as;
+ unsigned delta_local;
+
+ if (halTimeGetLocaltime(&now_local)) {
+ update_8021as = gPtpTD.local_time - gPtpTD.ml_phoffset;
+ delta_local = (unsigned)(now_local - gPtpTD.local_time);
+ delta_8021as = (unsigned)(gPtpTD.ml_freqoffset * delta_local);
+ *timeNsec = update_8021as + delta_8021as;
+
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return TRUE;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return FALSE;
+}
+
+bool osalAVBTimeInit(void) {
+ AVB_TRACE_ENTRY(AVB_TRACE_TIME);
+
+ LOCK();
+ if (!bInitialized) {
+ if (x_timeInit())
+ bInitialized = TRUE;
+ }
+ UNLOCK();
+
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return TRUE;
+}
+
+bool osalAVBTimeClose(void) {
+ AVB_TRACE_ENTRY(AVB_TRACE_TIME);
+
+ gptpdeinit(gIgbShmFd, gIgbMmap);
+
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return TRUE;
+}
+
+bool osalClockGettime(openavb_clockId_t openavbClockId, struct timespec *getTime) {
+ AVB_TRACE_ENTRY(AVB_TRACE_TIME);
+
+ if (openavbClockId < OPENAVB_CLOCK_WALLTIME) {
+ clockid_t clockId = CLOCK_MONOTONIC;
+ switch (openavbClockId) {
+ case OPENAVB_CLOCK_REALTIME:
+ clockId = CLOCK_REALTIME;
+ break;
+ case OPENAVB_CLOCK_MONOTONIC:
+ clockId = CLOCK_MONOTONIC;
+ break;
+ case OPENAVB_TIMER_CLOCK:
+ clockId = CLOCK_MONOTONIC;
+ break;
+ case OPENAVB_CLOCK_WALLTIME:
+ break;
+ }
+ if (!clock_gettime(clockId, getTime)) return TRUE;
+ }
+ else if (openavbClockId == OPENAVB_CLOCK_WALLTIME) {
+ U64 timeNsec;
+ if (!x_getPTPTime(&timeNsec)) {
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return FALSE;
+ }
+ getTime->tv_sec = timeNsec / NANOSECONDS_PER_SECOND;
+ getTime->tv_nsec = timeNsec % NANOSECONDS_PER_SECOND;
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return TRUE;
+ }
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return FALSE;
+}
+
+bool osalClockGettime64(openavb_clockId_t openavbClockId, U64 *timeNsec) {
+ if (openavbClockId < OPENAVB_CLOCK_WALLTIME) {
+ clockid_t clockId = CLOCK_MONOTONIC;
+ switch (openavbClockId) {
+ case OPENAVB_CLOCK_REALTIME:
+ clockId = CLOCK_REALTIME;
+ break;
+ case OPENAVB_CLOCK_MONOTONIC:
+ clockId = CLOCK_MONOTONIC;
+ break;
+ case OPENAVB_TIMER_CLOCK:
+ clockId = CLOCK_MONOTONIC;
+ break;
+ case OPENAVB_CLOCK_WALLTIME:
+ break;
+ }
+ struct timespec getTime;
+ if (!clock_gettime(clockId, &getTime)) {
+ *timeNsec = ((U64)getTime.tv_sec * (U64)NANOSECONDS_PER_SECOND) + (U64)getTime.tv_nsec;
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return TRUE;
+ }
+ }
+ else if (openavbClockId == OPENAVB_CLOCK_WALLTIME) {
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return x_getPTPTime(timeNsec);
+ }
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return FALSE;
+}
+
+
diff --git a/lib/avtp_pipeline/platform/Linux/openavb_time_osal.h b/lib/avtp_pipeline/platform/Linux/openavb_time_osal.h new file mode 100644 index 00000000..9db4cb6b --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/openavb_time_osal.h @@ -0,0 +1,36 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#ifndef _OPENAVB_TIME_OSAL_H +#define _OPENAVB_TIME_OSAL_H + +#include "openavb_time_osal_pub.h" + +#endif // _OPENAVB_TIME_OSAL_H diff --git a/lib/avtp_pipeline/platform/Linux/openavb_time_osal_pub.h b/lib/avtp_pipeline/platform/Linux/openavb_time_osal_pub.h new file mode 100644 index 00000000..a1029a76 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/openavb_time_osal_pub.h @@ -0,0 +1,69 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#ifndef _OPENAVB_TIME_OSAL_PUB_H +#define _OPENAVB_TIME_OSAL_PUB_H + +// timer Macros +#include <sys/timerfd.h> +#include <time.h> + +#define AVB_TIME_INIT() osalAVBTimeInit() +#define AVB_TIME_CLOSE() osalAVBTimeClose() + +#define TIMERFD_CREATE(arg1, arg2) timerfd_create(arg1, arg2) +#define TIMERFD_SETTIME(arg1, arg2, arg3, arg4) timerfd_settime(arg1, arg2, arg3, arg4) +#define TIMER_CLOSE(arg1) close(arg1) + +// In this Linux port all clock IDs preceeding OPENAVB_CLOCK_WALLTIME will be set to clock_gettime() +typedef enum { + OPENAVB_CLOCK_REALTIME, + OPENAVB_CLOCK_MONOTONIC, + OPENAVB_TIMER_CLOCK, + OPENAVB_CLOCK_WALLTIME +} openavb_clockId_t; + +#define CLOCK_GETTIME(arg1, arg2) osalClockGettime(arg1, arg2) +#define CLOCK_GETTIME64(arg1, arg2) osalClockGettime64(arg1, arg2) + +// Initialize the AVB Time system for client usage +bool osalAVBTimeInit(void); + +// Close the AVB Time system for client usage +bool osalAVBTimeClose(void); + +// Gets current time. Returns 0 on success otherwise -1 +bool osalClockGettime(openavb_clockId_t openavbClockId, struct timespec *getTime); + +// Gets current time as U64 nSec. Returns 0 on success otherwise -1 +bool osalClockGettime64(openavb_clockId_t openavbClockId, U64 *timeNsec); + + +#endif // _OPENAVB_TIME_OSAL_PUB_H diff --git a/lib/avtp_pipeline/platform/Linux/rawsock/avtp_rx.c b/lib/avtp_pipeline/platform/Linux/rawsock/avtp_rx.c new file mode 100644 index 00000000..27bdb437 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/rawsock/avtp_rx.c @@ -0,0 +1,227 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <glib.h> +#include "openavb_rawsock.h" +#include "openavb_avtp.h" +#ifdef UBUNTU +static int ethertype = ETHERTYPE_AVTP; +#else +static int ethertype = ETHERTYPE_8021Q; +#endif + +#define MAX_NUM_FRAMES 10 + +static bool bRunning = TRUE; + +static char* interface = NULL; + +#define NUM_STREAMS 256 +#define REPORT_SECONDS 10 + +static GOptionEntry entries[] = +{ + { "interface", 'i', 0, G_OPTION_ARG_STRING, &interface, "network interface", "NAME" }, + { NULL } +}; + +int timespec_cmp(struct timespec *a, struct timespec *b) +{ + if (a->tv_sec > b->tv_sec) + return 1; + else if (a->tv_sec < b->tv_sec) + return -1; + else { + if (a->tv_nsec > b->tv_nsec) + return 1; + else if (a->tv_nsec < b->tv_nsec) + return -1; + } + return 0; +} + +void dumpAscii(U8 *pFrame, int i, int *j) +{ + char c; + + printf(" "); + + while (*j <= i) { + c = pFrame[*j]; + *j += 1; + if (!isprint(c) || isspace(c)) + c = '.'; + printf("%c", c); + } +} + +void dumpFrameContent(U8 *pFrame, U32 len) +{ + int i = 0, j = 0; + while (TRUE) { + if (i % 16 == 0) { + if (i != 0 ) { + // end of line stuff + dumpAscii(pFrame, (i < len ? i : len), &j); + printf("\n"); + + if (i >= len) + break; + } + if (i+1 < len) { + // start of line stuff + printf("0x%4.4d: ", i); + } + } + else if (i % 2 == 0) { + printf(" "); + } + + if (i >= len) + printf(" "); + else + printf("%2.2x", pFrame[i]); + + i += 1; + } +} + +void dumpFrame(U8 *pFrame, U32 len, hdr_info_t *hdr) +{ + printf("Frame received, ethertype=0x%x len=%u\n", hdr->ethertype, len); + printf("src: %s\n", ether_ntoa((const struct ether_addr*)hdr->shost)); + printf("dst: %s\n", ether_ntoa((const struct ether_addr*)hdr->dhost)); + if (hdr->vlan) { + printf("VLAN pcp=%u, vid=%u\n", (unsigned)hdr->vlan_pcp, hdr->vlan_vid); + } + dumpFrameContent(pFrame, len); + printf("\n"); +} + +int main(int argc, char* argv[]) +{ + GError *error = NULL; + GOptionContext *context; + + context = g_option_context_new("- rawsock listenr"); + g_option_context_add_main_entries(context, entries, NULL); + if (!g_option_context_parse(context, &argc, &argv, &error)) + { + printf("error: %s\n", error->message); + exit(1); + } + + if (interface == NULL) { + printf("error: must specify network interface\n"); + exit(2); + } + + void* rs = openavbRawsockOpen(interface, TRUE, FALSE, ethertype, 0, MAX_NUM_FRAMES); + if (!rs) { + printf("error: failed to open raw socket (are you root?)\n"); + exit(3); + } + + hdr_info_t hdr; + U8 *pBuf, *pFrame, tmp8; + U32 offset, len; + U16 uid, i; + + long nTotal = 0, nRecv[NUM_STREAMS]; + for (i = 0; i < NUM_STREAMS; i++) + nRecv[i] = 0; + + struct timespec now, report; + clock_gettime(CLOCK_MONOTONIC, &report); + report.tv_sec += REPORT_SECONDS; + + while (bRunning) { + pBuf = openavbRawsockGetRxFrame(rs, 1000, &offset, &len); + if (pBuf) { + pFrame = pBuf + offset; + + offset = openavbRawsockRxParseHdr(rs, pBuf, &hdr); + if ((int)offset < 0) { + printf("error parsing frame header"); + } + else { +#ifndef UBUNTU + if (hdr.ethertype == ETHERTYPE_8021Q) { + // Oh! Need to look past the VLAN tag + U16 vlan_bits = ntohs(*(U16 *)(pFrame + offset)); + hdr.vlan = TRUE; + hdr.vlan_vid = vlan_bits & 0x0FFF; + hdr.vlan_pcp = (vlan_bits >> 13) & 0x0007; + offset += 2; + hdr.ethertype = ntohs(*(U16 *)(pFrame + offset)); + offset += 2; + } +#endif + + if (hdr.ethertype == ETHERTYPE_AVTP) { + //dumpFrame(pFrame + offset, len - offset, &hdr); + + // Look for stream data frames + // (ignore control frames, including MAAP) + tmp8 = *(pFrame + offset); + if ((tmp8 & 0x80) == 0) { + // Find the unique ID in the streamID + uid = htons(*(U16*)(pFrame + offset + 10)); + if (uid < NUM_STREAMS) + nRecv[uid]++; + nTotal++; + } + } + } + + openavbRawsockRelRxFrame(rs, pBuf); + } + + clock_gettime(CLOCK_MONOTONIC, &now); + if (timespec_cmp(&now, &report) >= 0) { + printf("total=%ld\t", nTotal); + nTotal = 0; + for (i = 0; i < NUM_STREAMS-1; i++) { + if (nRecv[i] > 0) { + printf("%d=%ld\t", i, nRecv[i]); + nRecv[i] = 0; + } + } + printf("\n"); + report.tv_sec += REPORT_SECONDS; + } + } + + openavbRawsockClose(rs); + return 0; +} diff --git a/lib/avtp_pipeline/platform/Linux/rawsock/openavb_rawsock.c b/lib/avtp_pipeline/platform/Linux/rawsock/openavb_rawsock.c new file mode 100644 index 00000000..080509da --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/rawsock/openavb_rawsock.c @@ -0,0 +1,1172 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* + * MODULE SUMMARY : Implements raw socket library. + * + * The raw socket library allows its user to send manually-created + * Etherent frames, and to receive full frames directly off the network + * interface. + * + * AVB needs to do this because Linux doesn't have an in-kernel + * implementation of the SRP or AVTP protocols. We handle those + * protocols in our userspace processes. + * + * We use Linux's MMAP method of sending/receiving raw socket frames + * because it's more efficient than the normal socket read/write method. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <poll.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <linux/if_packet.h> +#include <assert.h> +#include "openavb_rawsock.h" +#include "openavb_trace.h" +#include <linux/filter.h> + +#define AVB_LOG_COMPONENT "Raw Socket" +#include "openavb_log.h" + +// CORE TODO: This needs to be centralized; we have multiple defines for 0x8100 and some others like ETHERTYPE_AVTP +#define ETHERTYPE_8021Q 0x8100 + +// Ethernet header +typedef struct { + U8 dhost[ETH_ALEN]; + U8 shost[ETH_ALEN]; + u_int16_t ethertype; +} __attribute__ ((__packed__)) eth_hdr_t; + +// VLAN tag +typedef struct { + u_int16_t tpip; + u_int16_t bits; +} __attribute__ ((__packed__)) vlan_tag_t; + +// Ethernet header w/VLAN tag +typedef struct { + U8 dhost[ETH_ALEN]; + U8 shost[ETH_ALEN]; + vlan_tag_t vlan; + u_int16_t ethertype; +} __attribute__ ((__packed__)) eth_vlan_hdr_t; + +// State information for raw socket +// +typedef struct { + + // interface info + if_info_t ifInfo; + + // saved Ethernet header for TX frames + union { + eth_hdr_t notag; + eth_vlan_hdr_t tagged; + } ethHdr; + unsigned ethHdrLen; + + // Ethertype for TX/RX frames + unsigned ethertype; + + // the underlying socket + int sock; + + // number and size of the frames that the ring can hold + int frameCount; + int frameSize; + + // size of the header prepended to each frame buffer + int bufHdrSize; + // size of the frame buffer (frameSize + bufHdrSize) + int bufferSize; + + // memory for ring is allocated in blocks by kernel + int blockSize; + int blockCount; + + // the memory for the ring + size_t memSize; + U8 *pMem; + + // Active slot in the ring - tracked by + // block and buffer within that block. + int blockIndex, bufferIndex; + + // Number of buffers held by client + int buffersOut; + // Buffers marked ready, but not yet sent + int buffersReady; + + + // TX usage of the socket + bool txMode; + + // RX usage of the socket + bool rxMode; + + // Are we losing RX packets? + bool bLosing; + + // Number of TX buffers we experienced problems with + unsigned long txOutOfBuffer; + // Number of TX buffers we experienced problems with from the time when last stats being displayed + unsigned long txOutOfBufferCyclic; +} raw_sock_t; + +// Kernel block size +#define BLOCK_SIZE 4096 + +// Argument validation +#define VALID_RAWSOCK(s) ((s) != NULL && (s)->pMem != (void*)(-1) && (s)->sock != -1) +#define VALID_TX_RAWSOCK(s) (VALID_RAWSOCK(s) && (s)->txMode) +#define VALID_RX_RAWSOCK(s) (VALID_RAWSOCK(s) && (s)->rxMode) + +// Get information about an interface +bool openavbCheckInterface(const char *ifname, if_info_t *info) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK); + + if (!ifname || !info) { + AVB_LOG_ERROR("Checking interface; invalid arguments"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return FALSE; + } + + // zap the result struct + memset(info, 0, sizeof(if_info_t)); + + AVB_LOGF_DEBUG("ifname=%s", ifname); + strncpy(info->name, ifname, IFNAMSIZ - 1); + + // open a throw-away socket - used for our ioctls + int sk = socket(AF_INET, SOCK_STREAM, 0); + if (sk == -1) { + AVB_LOGF_ERROR("Checking interface; socket open failed: %s", strerror(errno)); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return FALSE; + } + + // set the name of the interface in the ioctl request struct + struct ifreq ifr; + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); + + // First check if the interface is up + // (also proves that the interface exists!) + int r = ioctl(sk, SIOCGIFFLAGS, &ifr); + if (r != 0) { + AVB_LOGF_ERROR("Checking interface; ioctl(SIOCGIFFLAGS) failed: %s", strerror(errno)); + close(sk); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return FALSE; + } + + if (!(ifr.ifr_flags&IFF_UP)) { + AVB_LOGF_ERROR("Checking interface; interface is not up: %s", ifname); + close(sk); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return FALSE; + } + + // get index for interface + r = ioctl(sk, SIOCGIFINDEX, &ifr); + if (r != 0) { + AVB_LOGF_ERROR("Checking interface; ioctl(SIOCGIFINDEX) failed: %s", strerror(errno)); + close(sk); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return FALSE; + } + info->index = ifr.ifr_ifindex; + + // get the MAC address for the interface + r = ioctl(sk, SIOCGIFHWADDR, &ifr); + if (r != 0) { + AVB_LOGF_ERROR("Checking interface; ioctl(SIOCGIFHWADDR) failed: %s", strerror(errno)); + close(sk); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return FALSE; + } + memcpy(&info->mac.ether_addr_octet, &ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + // get the MTU for the interface + r = ioctl(sk, SIOCGIFMTU, &ifr); + if (r != 0) { + AVB_LOGF_ERROR("Checking interface; ioctl(SIOCGIFMTU) failed: %s", strerror(errno)); + close(sk); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return FALSE; + } + info->mtu = ifr.ifr_mtu; + + // close the temporary socket + close(sk); + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return TRUE; +} + +// Open a rawsock for TX or RX +void *openavbRawsockOpen(const char *ifname, bool rx_mode, bool tx_mode, U16 ethertype, U32 frame_size, U32 num_frames) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK); + + AVB_LOGF_DEBUG("Open, rx=%d, tx=%d, ethertype=%x size=%d, num=%d", rx_mode, tx_mode, ethertype, frame_size, num_frames); + + /* Allocate struct to hold info about the raw socket + * that we're going to create. + */ + raw_sock_t *rawsock = malloc(sizeof(raw_sock_t)); + if (!rawsock) { + AVB_LOG_ERROR("Creating rawsock; malloc failed"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return NULL; + } + memset(rawsock, 0, sizeof(raw_sock_t)); + rawsock->sock = -1; + rawsock->pMem = (void*)(-1); + rawsock->rxMode = rx_mode; + rawsock->txMode = tx_mode; + rawsock->frameSize = frame_size; + rawsock->ethertype = ethertype; + + // Get info about the network device + if (!openavbCheckInterface(ifname, &(rawsock->ifInfo))) { + AVB_LOGF_ERROR("Creating rawsock; bad interface name: %s", ifname); + free(rawsock); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return NULL; + } + + // Deal with frame size. + if (rawsock->frameSize == 0) { + // use interface MTU as max frames size, if none specified + rawsock->frameSize = rawsock->ifInfo.mtu; + } + else if (rawsock->frameSize > rawsock->ifInfo.mtu) { + AVB_LOG_ERROR("Creating raswsock; requested frame size exceeds MTU"); + free(rawsock); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return NULL; + } + rawsock->frameSize = TPACKET_ALIGN(rawsock->frameSize); + + // Prepare default Ethernet header. + rawsock->ethHdrLen = sizeof(eth_hdr_t); + memset(&(rawsock->ethHdr.notag.dhost), 0xFF, ETH_ALEN); + memcpy(&(rawsock->ethHdr.notag.shost), &(rawsock->ifInfo.mac), ETH_ALEN); + rawsock->ethHdr.notag.ethertype = htons(rawsock->ethertype); + + // Create socket + rawsock->sock = socket(PF_PACKET, SOCK_RAW, htons(rawsock->ethertype)); + if (rawsock->sock == -1) { + AVB_LOGF_ERROR("Creating rawsock; opening socket: %s", strerror(errno)); + openavbRawsockClose(rawsock); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return NULL; + } + + // Allow address reuse + int temp = 1; + if(setsockopt(rawsock->sock, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(int)) < 0) { + AVB_LOG_ERROR("Creating rawsock; failed to set reuseaddr"); + openavbRawsockClose(rawsock); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return NULL; + } + + // Bind to interface + struct sockaddr_ll my_addr; + memset(&my_addr, 0, sizeof(my_addr)); + my_addr.sll_family = PF_PACKET; + my_addr.sll_protocol = htons(rawsock->ethertype); + my_addr.sll_ifindex = rawsock->ifInfo.index; + + if (bind(rawsock->sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) == -1) { + AVB_LOGF_ERROR("Creating rawsock; bind socket: %s", strerror(errno)); + openavbRawsockClose(rawsock); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return NULL; + } + + // Use version 2 headers for the MMAP packet stuff - avoids 32/64 + // bit problems, gives nanosecond timestamps, and allows rx of vlan id + int val = TPACKET_V2; + if (setsockopt(rawsock->sock, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)) < 0) { + AVB_LOGF_ERROR("Creating rawsock; get PACKET_VERSION: %s", strerror(errno)); + openavbRawsockClose(rawsock); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return NULL; + } + + // Get the size of the headers in the ring + unsigned len = sizeof(val); + if (getsockopt(rawsock->sock, SOL_PACKET, PACKET_HDRLEN, &val, &len) < 0) { + AVB_LOGF_ERROR("Creating rawsock; get PACKET_HDRLEN: %s", strerror(errno)); + openavbRawsockClose(rawsock); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return NULL; + } + rawsock->bufHdrSize = TPACKET_ALIGN(val); + + if (rawsock->rxMode) { + rawsock->bufHdrSize = rawsock->bufHdrSize + TPACKET_ALIGN(sizeof(struct sockaddr_ll)); + } + rawsock->bufferSize = rawsock->frameSize + rawsock->bufHdrSize; + rawsock->frameCount = num_frames; + AVB_LOGF_DEBUG("frameSize=%d, bufHdrSize=%d(%d+%d) bufferSize=%d, frameCount=%d", + rawsock->frameSize, rawsock->bufHdrSize, val, sizeof(struct sockaddr_ll), + rawsock->bufferSize, rawsock->frameCount); + + // Get number of bytes in a memory page. The blocks we ask for + // must be a multiple of pagesize. (Actually, it should be + // (pagesize * 2^N) to avoid wasting memory.) + int pagesize = getpagesize(); + rawsock->blockSize = pagesize * 4; + AVB_LOGF_DEBUG("pagesize=%d blockSize=%d", pagesize, rawsock->blockSize); + + // Calculate number of buffers and frames based on blocks + int buffersPerBlock = rawsock->blockSize / rawsock->bufferSize; + rawsock->blockCount = rawsock->frameCount / buffersPerBlock + 1; + rawsock->frameCount = buffersPerBlock * rawsock->blockCount; + + AVB_LOGF_DEBUG("frameCount=%d, buffersPerBlock=%d, blockCount=%d", + rawsock->frameCount, buffersPerBlock, rawsock->blockCount); + + // Fill in the kernel structure with our calculated values + struct tpacket_req s_packet_req; + memset(&s_packet_req, 0, sizeof(s_packet_req)); + s_packet_req.tp_block_size = rawsock->blockSize; + s_packet_req.tp_frame_size = rawsock->bufferSize; + s_packet_req.tp_block_nr = rawsock->blockCount; + s_packet_req.tp_frame_nr = rawsock->frameCount; + + // Ask the kernel to create the TX_RING or RX_RING + if (rawsock->txMode) { + if (setsockopt(rawsock->sock, SOL_PACKET, PACKET_TX_RING, + (char*)&s_packet_req, sizeof(s_packet_req)) < 0) { + AVB_LOGF_ERROR("Creating rawsock; TX_RING: %s", strerror(errno)); + openavbRawsockClose(rawsock); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return NULL; + } + AVB_LOGF_DEBUG("PACKET_%s_RING OK", "TX"); + } + else { + if (setsockopt(rawsock->sock, SOL_PACKET, PACKET_RX_RING, + (char*)&s_packet_req, sizeof(s_packet_req)) < 0) { + AVB_LOGF_ERROR("Creating rawsock, RX_RING: %s", strerror(errno)); + openavbRawsockClose(rawsock); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return NULL; + } + AVB_LOGF_DEBUG("PACKET_%s_RING OK", "TX"); + } + + // Call MMAP to get access to the memory used for the ring + rawsock->memSize = rawsock->blockCount * rawsock->blockSize; + AVB_LOGF_DEBUG("memSize=%d (%d, %d), sock=%d", + rawsock->memSize, + rawsock->blockCount, + rawsock->blockSize, + rawsock->sock); + rawsock->pMem = mmap((void*)0, rawsock->memSize, PROT_READ|PROT_WRITE, MAP_SHARED, rawsock->sock, (off_t)0); + if (rawsock->pMem == (void*)(-1)) { + AVB_LOGF_ERROR("Creating rawsock; MMAP: %s", strerror(errno)); + openavbRawsockClose(rawsock); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return NULL; + } + AVB_LOGF_DEBUG("mmap: %p", rawsock->pMem); + + // Initialize the memory + memset(rawsock->pMem, 0, rawsock->memSize); + + // Initialize the state of the ring + rawsock->blockIndex = 0; + rawsock->bufferIndex = 0; + rawsock->buffersOut = 0; + rawsock->buffersReady = 0; + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return rawsock; +} + +// Set signal on RX mode +void openavbSetRxSignalMode(void *pvRawsock, bool rxSignalMode) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK); + + // CORE_TODO : Nothing to do on Linux + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); +} + +// Close the rawsock +void openavbRawsockClose(void *pvRawsock) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + + if (rawsock) { + // free ring memory + if (rawsock->pMem != (void*)(-1)) { + munmap(rawsock->pMem, rawsock->memSize); + rawsock->pMem = (void*)(-1); + } + // close the socket + if (rawsock->sock != -1) { + close(rawsock->sock); + rawsock->sock = -1; + } + // free the state struct + free(rawsock); + } + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); +} + +// Get a buffer from the ring to use for TX +U8 *openavbRawsockGetTxFrame(void *pvRawsock, bool blocking, unsigned int *len) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + + // Displays only warning when buffer busy after second try + int bBufferBusyReported = 0; + + + if (!VALID_TX_RAWSOCK(rawsock) || len == NULL) { + AVB_LOG_ERROR("Getting TX frame; bad arguments"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return NULL; + } + if (rawsock->buffersOut >= rawsock->frameCount) { + AVB_LOG_ERROR("Getting TX frame; too many TX buffers in use"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return NULL; + } + + // Get pointer to next framebuf. + volatile struct tpacket2_hdr *pHdr = + (struct tpacket2_hdr*)(rawsock->pMem + + (rawsock->blockIndex * rawsock->blockSize) + + (rawsock->bufferIndex * rawsock->bufferSize)); + // And pointer to portion of buffer to be filled with frame + volatile U8 *pBuffer = (U8*)pHdr + rawsock->bufHdrSize; + + AVB_LOGF_VERBOSE("block=%d, buffer=%d, out=%d, pHdr=%p, pBuffer=%p", + rawsock->blockIndex, rawsock->bufferIndex, rawsock->buffersOut, + pHdr, pBuffer); + + // Check if buffer ready for user + // In send mode, we want to see TP_STATUS_AVAILABLE + while (pHdr->tp_status != TP_STATUS_AVAILABLE) + { + switch (pHdr->tp_status) { + case TP_STATUS_SEND_REQUEST: + case TP_STATUS_SENDING: + if (blocking) { +#if 0 +// We should be able to poll on the socket to wait for the buffer to +// be ready, but it doesn't work (at least on 2.6.37). +// Keep this code, because it may work on newer kernels + // poll until tx buffer is ready + struct pollfd pfd; + pfd.fd = rawsock->sock; + pfd.events = POLLWRNORM; + pfd.revents = 0; + int ret = poll(&pfd, 1, -1); + if (ret < 0 && errno != EINTR) { + AVB_LOGF_DEBUG("getting TX frame; poll failed: %s", strerror(errno)); + } +#else + // Can't poll, so sleep instead to avoid tight loop + if(0 == bBufferBusyReported) { + if(!rawsock->txOutOfBuffer) { + // Display this info only once just to let know that something like this happened + AVB_LOGF_INFO("Getting TX frame (sock=%d): TX buffer busy", rawsock->sock); + } + + ++rawsock->txOutOfBuffer; + ++rawsock->txOutOfBufferCyclic; + } else if(1 == bBufferBusyReported) { + //Display this warning if buffer was busy more than once because it might influence late/lost + AVB_LOGF_WARNING("Getting TX frame (sock=%d): TX buffer busy after usleep(50) verify if there are any lost/late frames", rawsock->sock); + } + + ++bBufferBusyReported; + + usleep(50); +#endif + } + else { + AVB_LOG_DEBUG("Non-blocking, return NULL"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return NULL; + } + break; + case TP_STATUS_WRONG_FORMAT: + default: + pHdr->tp_status = TP_STATUS_AVAILABLE; + break; + } + } + + // Remind client how big the frame buffer is + if (len) + *len = rawsock->frameSize; + + // increment indexes to point to next buffer + if (++(rawsock->bufferIndex) >= (rawsock->frameCount/rawsock->blockCount)) { + rawsock->bufferIndex = 0; + if (++(rawsock->blockIndex) >= rawsock->blockCount) { + rawsock->blockIndex = 0; + } + } + + // increment the count of buffers held by client + rawsock->buffersOut += 1; + + // warn if too many are out + if (rawsock->buffersOut >= (rawsock->frameCount * 4)/5) { + AVB_LOGF_WARNING("Getting TX frame; consider increasing buffers: count=%d, out=%d", + rawsock->frameCount, rawsock->buffersOut); + } + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return (U8*)pBuffer; +} + +// Set the Firewall MARK on the socket +// The mark is used by FQTSS to identify AVTP packets in kernel. +// FQTSS creates a mark that includes the AVB class and stream index. +bool openavbRawsockTxSetMark(void *pvRawsock, int mark) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + bool retval = FALSE; + + if (!VALID_TX_RAWSOCK(rawsock)) { + AVB_LOG_ERROR("Setting TX mark; invalid argument passed"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return FALSE; + } + + if (setsockopt(rawsock->sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { + AVB_LOGF_ERROR("Setting TX mark; setsockopt failed: %s", strerror(errno)); + } + else { + AVB_LOGF_DEBUG("SO_MARK=%d OK", mark); + retval = TRUE; + } + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return retval; +} + +// Pre-set the ethernet header information that will be used on TX frames +bool openavbRawsockTxSetHdr(void *pvRawsock, hdr_info_t *pHdr) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + + if (!VALID_TX_RAWSOCK(rawsock)) { + AVB_LOG_ERROR("Setting TX header; invalid argument"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return FALSE; + } + + // source address + if (pHdr->shost) { + memcpy(&(rawsock->ethHdr.notag.shost), pHdr->shost, ETH_ALEN); + } + // destination address + if (pHdr->dhost) { + memcpy(&(rawsock->ethHdr.notag.dhost), pHdr->dhost, ETH_ALEN); + } + + // VLAN tag? + if (!pHdr->vlan) { + // No, set ethertype in normal location + rawsock->ethHdr.notag.ethertype = htons(rawsock->ethertype); + // and set ethernet header length + rawsock->ethHdrLen = sizeof(eth_hdr_t); + } + else { + // Add VLAN tag + AVB_LOGF_DEBUG("VLAN=%d pcp=%d vid=%d", pHdr->vlan_vid, pHdr->vlan_pcp, pHdr->vlan_vid); + + // Build bitfield with vlan_pcp and vlan_vid. + // I think CFI bit is alway 0 + u_int16_t bits = 0; + bits |= (pHdr->vlan_pcp << 13) & 0xE000; + bits |= pHdr->vlan_vid & 0x0FFF; + + // Create VLAN tag + rawsock->ethHdr.tagged.vlan.tpip = htons(ETHERTYPE_VLAN); + rawsock->ethHdr.tagged.vlan.bits = htons(bits); + rawsock->ethHdr.tagged.ethertype = htons(rawsock->ethertype); + // and set ethernet header length + rawsock->ethHdrLen = sizeof(eth_vlan_hdr_t); + } + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return TRUE; +} + +// Copy the pre-set header to the outgoing frame +inline bool openavbRawsockTxFillHdr(void *pvRawsock, U8 *pBuffer, unsigned int *hdrlen) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + if (!VALID_TX_RAWSOCK(rawsock)) { + AVB_LOG_ERROR("Filling TX header; invalid argument"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return FALSE; + } + + // Copy the default Ethernet header into the buffer + if (hdrlen) + *hdrlen = rawsock->ethHdrLen; + memcpy((char*)pBuffer, &(rawsock->ethHdr), rawsock->ethHdrLen); + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return TRUE; +} + +// Release a TX frame, without marking it as ready to send +bool openavbRawsockRelTxFrame(void *pvRawsock, U8 *pBuffer) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + if (!VALID_TX_RAWSOCK(rawsock) || pBuffer == NULL) { + AVB_LOG_ERROR("Releasing TX frame; invalid argument"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return FALSE; + } + + volatile struct tpacket2_hdr *pHdr = (struct tpacket2_hdr*)(pBuffer - rawsock->bufHdrSize); + AVB_LOGF_VERBOSE("pBuffer=%p, pHdr=%p", pBuffer, pHdr); + + pHdr->tp_len = 0; + pHdr->tp_status = TP_STATUS_KERNEL; + rawsock->buffersOut -= 1; + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return TRUE; +} + +// Release a TX frame, and mark it as ready to send +bool openavbRawsockTxFrameReady(void *pvRawsock, U8 *pBuffer, unsigned int len) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + + if (!VALID_TX_RAWSOCK(rawsock)) { + AVB_LOG_ERROR("Marking TX frame ready; invalid argument"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return FALSE; + } + + volatile struct tpacket2_hdr *pHdr = (struct tpacket2_hdr*)(pBuffer - rawsock->bufHdrSize); + AVB_LOGF_VERBOSE("pBuffer=%p, pHdr=%p szFrame=%d, len=%d", pBuffer, pHdr, rawsock->frameSize, len); + + assert(len <= rawsock->bufferSize); + pHdr->tp_len = len; + pHdr->tp_status = TP_STATUS_SEND_REQUEST; + rawsock->buffersReady += 1; + + if (rawsock->buffersReady >= rawsock->frameCount) { + AVB_LOG_WARNING("All buffers in ready/unsent state, calling send"); + openavbRawsockSend(pvRawsock); + } + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return TRUE; +} + +// Send all packets that are ready (i.e. tell kernel to send them) +int openavbRawsockSend(void *pvRawsock) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + if (!VALID_TX_RAWSOCK(rawsock)) { + AVB_LOG_ERROR("Send; invalid argument"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return -1; + } + + // Linux does something dumb to wait for frames to be sent. + // Without MSG_DONTWAIT, CPU usage is bad. + int flags = MSG_DONTWAIT; + int sent = send(rawsock->sock, NULL, 0, flags); + if (errno == EINTR) { + // ignore + } + else if (sent < 0) { + AVB_LOGF_ERROR("Send failed: %s", strerror(errno)); + assert(0); + } + else { + AVB_LOGF_VERBOSE("Sent %d bytes, %d frames", sent, rawsock->buffersReady); + rawsock->buffersOut -= rawsock->buffersReady; + rawsock->buffersReady = 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return sent; +} + +// Count used TX buffers in ring +int openavbRawsockTxBufLevel(void *pvRawsock) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + + int buffersPerBlock = rawsock->blockSize / rawsock->bufferSize; + + int iBlock, iBuffer, nInUse = 0; + volatile struct tpacket2_hdr *pHdr; + + if (!VALID_RAWSOCK(rawsock)) { + AVB_LOG_ERROR("getting buffer level; invalid arguments"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return FALSE; + } + + for (iBlock = 0; iBlock < rawsock->blockCount; iBlock++) { + for (iBuffer = 0; iBuffer < buffersPerBlock; iBuffer++) { + + pHdr = (struct tpacket2_hdr*)(rawsock->pMem + + (iBlock * rawsock->blockSize) + + (iBuffer * rawsock->bufferSize)); + + if (rawsock->txMode) { + if (pHdr->tp_status == TP_STATUS_SEND_REQUEST + || pHdr->tp_status == TP_STATUS_SENDING) + nInUse++; + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return nInUse; +} + + +// Count used TX buffers in ring +int openavbRawsockRxBufLevel(void *pvRawsock) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + + int buffersPerBlock = rawsock->blockSize / rawsock->bufferSize; + + int iBlock, iBuffer, nInUse = 0; + volatile struct tpacket2_hdr *pHdr; + + if (!VALID_RAWSOCK(rawsock)) { + AVB_LOG_ERROR("getting buffer level; invalid arguments"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return FALSE; + } + + for (iBlock = 0; iBlock < rawsock->blockCount; iBlock++) { + for (iBuffer = 0; iBuffer < buffersPerBlock; iBuffer++) { + + pHdr = (struct tpacket2_hdr*)(rawsock->pMem + + (iBlock * rawsock->blockSize) + + (iBuffer * rawsock->bufferSize)); + + if (!rawsock->txMode) { + if (pHdr->tp_status & TP_STATUS_USER) + nInUse++; + } + } + } + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return nInUse; +} + + +// Get a RX frame +U8 *openavbRawsockGetRxFrame(void *pvRawsock, U32 timeout, unsigned int *offset, unsigned int *len) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + if (!VALID_RX_RAWSOCK(rawsock)) { + AVB_LOG_ERROR("Getting RX frame; invalid arguments"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return NULL; + } + if (rawsock->buffersOut >= rawsock->frameCount) { + AVB_LOG_ERROR("Too many RX buffers in use"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return NULL; + } + + // Get pointer to active buffer in ring + volatile struct tpacket2_hdr *pHdr = + (struct tpacket2_hdr*)(rawsock->pMem + + (rawsock->blockIndex * rawsock->blockSize) + + (rawsock->bufferIndex * rawsock->bufferSize)); + volatile U8 *pBuffer = (U8*)pHdr + rawsock->bufHdrSize; + + AVB_LOGF_VERBOSE("block=%d, buffer=%d, out=%d, pHdr=%p, pBuffer=%p", + rawsock->blockIndex, rawsock->bufferIndex, rawsock->buffersOut, + pHdr, pBuffer); + + // Check if buffer ready for user + // In receive mode, we want TP_STATUS_USER flag set + if ((pHdr->tp_status & TP_STATUS_USER) == 0) + { + struct timespec ts, *pts = NULL; + struct pollfd pfd; + + // Use poll to wait for "ready to read" condition + + // Poll even if our timeout is 0 - to catch the case where + // kernel is writing to the wrong slot (see below.) + if (timeout != OPENAVB_RAWSOCK_BLOCK) { + ts.tv_sec = timeout / MICROSECONDS_PER_SECOND; + ts.tv_nsec = (timeout % MICROSECONDS_PER_SECOND) * NANOSECONDS_PER_USEC; + pts = &ts; + } + + pfd.fd = rawsock->sock; + pfd.events = POLLIN; + pfd.revents = 0; + + int ret = ppoll(&pfd, 1, pts, NULL); + if (ret < 0) { + if (errno != EINTR) { + AVB_LOGF_ERROR("Getting RX frame; poll failed: %s", strerror(errno)); + } + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return NULL; + } + if ((pfd.revents & POLLIN) == 0) { + // timeout + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return NULL; + } + + if ((pHdr->tp_status & TP_STATUS_USER) == 0) { + // Hmmm, this is unexpected. poll indicated that the + // socket was ready to read, but the slot in the TX ring + // that we're looking for the kernel to fill isn't filled. + + // If there aren't any RX buffers held by the application, + // we can try to fix this sticky situation... + if (rawsock->buffersOut == 0) { + // Scan forward through the RX ring, and look for a + // buffer that's ready for us to read. The kernel has + // a bad habit of not starting at the beginning of the + // ring when the listener process is restarted. + int nSkipped = 0; + while((pHdr->tp_status & TP_STATUS_USER) == 0) { + // Move to next slot in ring. + // (Increment buffer/block indexes) + if (++(rawsock->bufferIndex) >= (rawsock->frameCount/rawsock->blockCount)) { + rawsock->bufferIndex = 0; + if (++(rawsock->blockIndex) >= rawsock->blockCount) { + rawsock->blockIndex = 0; + } + } + + // Adjust pHdr, pBuffer to point to the new slot + pHdr = (struct tpacket2_hdr*)(rawsock->pMem + + (rawsock->blockIndex * rawsock->blockSize) + + (rawsock->bufferIndex * rawsock->bufferSize)); + pBuffer = (U8*)pHdr + rawsock->bufHdrSize; + + // If we've scanned all the way around the ring, bail out. + if (++nSkipped > rawsock->frameCount) { + AVB_LOG_WARNING("Getting RX frame; no frame after poll"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return NULL; + } + } + + // We found a slot that's ready. Hopefully, we're good now. + AVB_LOGF_WARNING("Getting RX frame; skipped %d empty slots (rawsock=%p)", nSkipped, rawsock); + } + else { + AVB_LOG_WARNING("Getting RX frame; no frame after poll"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return NULL; + } + } + } + + AVB_LOGF_VERBOSE("Buffer status=0x%4.4lx", (unsigned long)pHdr->tp_status); + if (pHdr->tp_status & TP_STATUS_COPY) { + AVB_LOG_WARNING("Frame too big for receive buffer"); + } + + // Check the "losing" flag. That indicates that the ring is full, + // and the kernel had to toss some frames. There is no "winning" flag. + if ((pHdr->tp_status & TP_STATUS_LOSING)) { + if (!rawsock->bLosing) { + AVB_LOG_WARNING("Getting RX frame; mmap buffers full"); + rawsock->bLosing = TRUE; + } + } + else { + rawsock->bLosing = FALSE; + } + + // Return pointer to the buffer and length + *offset = pHdr->tp_mac - rawsock->bufHdrSize; + *len = pHdr->tp_len; + + // increment indexes for next time + if (++(rawsock->bufferIndex) >= (rawsock->frameCount/rawsock->blockCount)) { + rawsock->bufferIndex = 0; + if (++(rawsock->blockIndex) >= rawsock->blockCount) { + rawsock->blockIndex = 0; + } + } + + // Remember that the client has another buffer + rawsock->buffersOut += 1; + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return (U8*)pBuffer; +} + +// Parse the ethernet frame header. Returns length of header, or -1 for failure +int openavbRawsockRxParseHdr(void *pvRawsock, U8 *pBuffer, hdr_info_t *pInfo) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + int hdrLen; + if (!VALID_RX_RAWSOCK(rawsock)) { + AVB_LOG_ERROR("Parsing Ethernet headers; invalid arguments"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return -1; + } + + volatile struct tpacket2_hdr *pHdr = (struct tpacket2_hdr*)(pBuffer - rawsock->bufHdrSize); + AVB_LOGF_VERBOSE("pBuffer=%p, pHdr=%p", pBuffer, pHdr); + + memset(pInfo, 0, sizeof(hdr_info_t)); + + eth_hdr_t *pNoTag = (eth_hdr_t*)((U8*)pHdr + pHdr->tp_mac); + hdrLen = pHdr->tp_net - pHdr->tp_mac; + pInfo->shost = pNoTag->shost; + pInfo->dhost = pNoTag->dhost; + pInfo->ethertype = ntohs(pNoTag->ethertype); + + if (pInfo->ethertype == ETHERTYPE_8021Q) { + pInfo->vlan = TRUE; + pInfo->vlan_vid = pHdr->tp_vlan_tci & 0x0FFF; + pInfo->vlan_pcp = (pHdr->tp_vlan_tci >> 13) & 0x0007; + pInfo->ethertype = ntohs(*(U16*)( ((U8*)(&pNoTag->ethertype)) + 4)); + hdrLen += 4; + } + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return hdrLen; +} + +// Release a RX frame held by the client +bool openavbRawsockRelRxFrame(void *pvRawsock, U8 *pBuffer) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + + if (!VALID_RX_RAWSOCK(rawsock)) { + AVB_LOG_ERROR("Releasing RX frame; invalid arguments"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return FALSE; + } + + volatile struct tpacket2_hdr *pHdr = (struct tpacket2_hdr*)(pBuffer - rawsock->bufHdrSize); + AVB_LOGF_VERBOSE("pBuffer=%p, pHdr=%p", pBuffer, pHdr); + + pHdr->tp_status = TP_STATUS_KERNEL; + rawsock->buffersOut -= 1; + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return TRUE; +} + +// Setup the rawsock to receive multicast packets +bool openavbRawsockRxMulticast(void *pvRawsock, bool add_membership, const U8 addr[ETH_ALEN]) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); + + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + if (!VALID_RX_RAWSOCK(rawsock)) { + AVB_LOG_ERROR("Setting multicast; invalid arguments"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return FALSE; + } + + struct ether_addr mcast_addr; + memcpy(mcast_addr.ether_addr_octet, addr, ETH_ALEN); + + // Fill in the structure for the multicast ioctl + struct packet_mreq mreq; + memset(&mreq, 0, sizeof(struct packet_mreq)); + mreq.mr_ifindex = rawsock->ifInfo.index; + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = ETH_ALEN; + memcpy(&mreq.mr_address, &mcast_addr.ether_addr_octet, ETH_ALEN); + + // And call the ioctl to add/drop the multicast address + int action = (add_membership ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP); + if (setsockopt(rawsock->sock, SOL_PACKET, action, + (void*)&mreq, sizeof(struct packet_mreq)) < 0) { + AVB_LOGF_ERROR("Setting multicast; setsockopt(%s) failed: %s", + (add_membership ? "PACKET_ADD_MEMBERSHIP" : "PACKET_DROP_MEMBERSHIP"), + strerror(errno)); + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return FALSE; + } + + // In addition to adding the multicast membership, we also want to + // add a packet filter to restrict the packets that we'll receive + // on this socket. Multicast memeberships are global - not + // per-socket, so without the filter, this socket would receieve + // packets for all the multicast addresses added by all other + // sockets. + // + if (add_membership) + { + // Here's the template packet filter code. + // It was produced by running: + // tcpdump -dd ether dest host 91:e0:01:02:03:04 + struct sock_filter bpfCode[] = { + { 0x20, 0, 0, 0x00000002 }, + { 0x15, 0, 3, 0x01020304 }, // last 4 bytes of dest mac + { 0x28, 0, 0, 0x00000000 }, + { 0x15, 0, 1, 0x000091e0 }, // first 2 bytes of dest mac + { 0x06, 0, 0, 0x0000ffff }, + { 0x06, 0, 0, 0x00000000 } + }; + + // We need to graft our multicast dest address into bpfCode + U32 tmp; U8 *buf = (U8*)&tmp; + memcpy(buf, mcast_addr.ether_addr_octet + 2, 4); + bpfCode[1].k = ntohl(tmp); + memset(buf, 0, 4); + memcpy(buf + 2, mcast_addr.ether_addr_octet, 2); + bpfCode[3].k = ntohl(tmp); + + // Now wrap the filter code in the appropriate structure + struct sock_fprog filter; + memset(&filter, 0, sizeof(filter)); + filter.len = 6; + filter.filter = bpfCode; + + // And attach it to our socket + if (setsockopt(rawsock->sock, SOL_SOCKET, SO_ATTACH_FILTER, + &filter, sizeof(filter)) < 0) { + AVB_LOGF_ERROR("Setting multicast; setsockopt(SO_ATTACH_FILTER) failed: %s", strerror(errno)); + } + } + else { + if (setsockopt(rawsock->sock, SOL_SOCKET, SO_DETACH_FILTER, NULL, 0) < 0) { + AVB_LOGF_ERROR("Setting multicast; setsockopt(SO_DETACH_FILTER) failed: %s", strerror(errno)); + } + } + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); + return TRUE; +} + +// Get the socket used for this rawsock; can be used for poll/select +int openavbRawsockGetSocket(void *pvRawsock) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + if (!rawsock) { + AVB_LOG_ERROR("Getting socket; invalid arguments"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return -1; + } + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return rawsock->sock; +} + +// Get the ethernet address of the interface +bool openavbRawsockGetAddr(void *pvRawsock, U8 addr[ETH_ALEN]) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK); + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + if (!rawsock) { + AVB_LOG_ERROR("Getting address; invalid arguments"); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return FALSE; + } + + memcpy(addr, &rawsock->ifInfo.mac.ether_addr_octet, ETH_ALEN); + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return TRUE; +} + +unsigned long openavbRawsockGetTXOutOfBuffers(void *pvRawsock) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK); + unsigned long counter = 0; + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + + if(VALID_TX_RAWSOCK(rawsock)) { + counter = rawsock->txOutOfBuffer; + } + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return counter; +} + +unsigned long openavbRawsockGetTXOutOfBuffersCyclic(void *pvRawsock) +{ + AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK); + unsigned long counter = 0; + raw_sock_t *rawsock = (raw_sock_t*)pvRawsock; + + if(VALID_TX_RAWSOCK(rawsock)) { + counter = rawsock->txOutOfBufferCyclic; + rawsock->txOutOfBufferCyclic = 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); + return counter; +} diff --git a/lib/avtp_pipeline/platform/Linux/rawsock/openavb_rawsock_igb.c b/lib/avtp_pipeline/platform/Linux/rawsock/openavb_rawsock_igb.c new file mode 100644 index 00000000..0c4d852f --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/rawsock/openavb_rawsock_igb.c @@ -0,0 +1,654 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#include "stc_avb_platform.h"
+#include "stc_sockets.h"
+#include <stdlib.h>
+#include <string.h>
+#include "stc_rawsock.h"
+#include "stc_avb_trace.h"
+
+#define AVB_LOG_COMPONENT "Raw Socket"
+#include "stc_avb_log.h"
+#include "stc_rawsock_tcal.h"
+
+// State information for raw socket
+//
+typedef struct {
+
+ // interface info
+ if_info_t ifInfo;
+
+ // saved Ethernet header for TX frames
+ union {
+ eth_hdr_t notag;
+ eth_vlan_hdr_t tagged;
+ }
+ ethHdr;
+ unsigned ethHdrLen;
+
+ // Ethertype for TX/RX frames
+ unsigned ethertype;
+
+ // the underlying socket
+ int sock;
+
+ // number and size of the frames requested
+ int frameSize;
+
+ eth_frame_t *pTxFrame;
+ eth_frame_t *pRxFrame;
+
+ // Task
+ thread_type *pTask; // Used by Task Manager suspend/resume
+
+ // TX usage of the socket
+ bool txMode;
+
+ // RX usage of the socket
+ bool rxMode;
+} raw_sock_t;
+
+// Kernel block size
+#define BLOCK_SIZE 4096
+
+// Argument validation
+#define VALID_RAWSOCK(s) ((s) != NULL && (s)->sock != -1)
+#define VALID_TX_RAWSOCK(s) (VALID_RAWSOCK(s) && (s)->txMode)
+#define VALID_RX_RAWSOCK(s) (VALID_RAWSOCK(s) && (s)->rxMode)
+
+// Get information about an interface
+bool stcAvbCheckInterface(const char *ifname, if_info_t *info){
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK);
+ bool retVal = FALSE;
+
+ if (!ifname || !info) {
+ AVB_LOG_ERROR("Checking interface; invalid arguments");
+ }
+ else {
+ // zap the result struct
+ memset(info, 0, sizeof(if_info_t));
+
+ AVB_LOGF_DEBUG("ifname=%s", ifname);
+ strncpy(info->name, ifname, IFNAMSIZ - 1);
+
+ // TODO_OPENAVB: Link is checked at Ethernet init time.
+ // is interface up?
+ //if(!halIsLinkUp()) {
+ // AVB_LOGF_ERROR("Checking interface; interface is not up: %s", ifname);
+ //}
+ //else {
+ info->index = 0; // index not used
+
+ osalGetMacAddr(info->mac.ether_addr_octet);
+ info->mtu = halGetMTU();
+
+ retVal = TRUE;
+ //}
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK);
+ return retVal;
+}
+
+
+// Open a rawsock for TX or RX
+void *stcRawsockOpen(const char *ifname, bool rx_mode, bool tx_mode, U16 ethertype, U32 frame_size, U32 num_frames) {
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK);
+ raw_sock_t *rawsock = NULL;
+
+ AVB_LOGF_DEBUG("Open, rx=%d, tx=%d, ethertype=%x size=%d, num=%d", rx_mode, tx_mode, ethertype, frame_size, num_frames);
+
+ do {
+ /* Allocate struct to hold info about the raw socket
+ * that we're going to create.
+ */
+ rawsock = calloc(1, sizeof(raw_sock_t));
+ if (!rawsock) {
+ AVB_LOG_ERROR("Creating rawsock; malloc failed");
+ break;
+ }
+
+ rawsock->sock = -1;
+ rawsock->rxMode = rx_mode;
+ rawsock->txMode = tx_mode;
+ rawsock->frameSize = frame_size;
+ rawsock->ethertype = ethertype;
+
+ // Get info about the network device
+ if (!stcAvbCheckInterface(ifname, &(rawsock->ifInfo))) {
+ AVB_LOGF_ERROR("Creating rawsock; bad interface name: %s", ifname);
+ free(rawsock);
+ rawsock = NULL;
+ break;
+ }
+
+ // Deal with frame size.
+ if (rawsock->frameSize == 0) {
+ // use interface MTU as max frames size, if none specified
+ rawsock->frameSize = rawsock->ifInfo.mtu;
+ }
+ else if (rawsock->frameSize > rawsock->ifInfo.mtu) {
+ AVB_LOG_ERROR("Creating raswsock; requested frame size exceeds MTU");
+ free(rawsock);
+ rawsock = NULL;
+ break;
+ }
+ else {
+ rawsock->frameSize = TPACKET_ALIGN(rawsock->frameSize);
+ }
+
+ // Prepare default Ethernet header.
+ rawsock->ethHdrLen = sizeof(eth_hdr_t);
+ memset(&(rawsock->ethHdr.notag.dhost), 0xFF, ETH_ALEN);
+ stc_safe_memcpy(&(rawsock->ethHdr.notag.shost), &(rawsock->ifInfo.mac), ETH_ALEN);
+ rawsock->ethHdr.notag.ethertype = htons(rawsock->ethertype);
+
+ // Create socket
+ rawsock->sock = osalSocket(PF_PACKET, SOCK_RAW, htons(rawsock->ethertype));
+ if (rawsock->sock == -1) {
+ AVB_LOG_ERROR("Creating rawsock; opening socket:");
+ free(rawsock);
+ rawsock = NULL;
+ break;
+ }
+
+ // request Tx frames
+ if (tx_mode) {
+ if (osalSetTxMode(rawsock->sock, num_frames, frame_size) == -1) {
+ AVB_LOG_ERROR("Creating rawsock: Allocating tx buffers");
+ stcRawsockClose(rawsock);
+ rawsock = NULL;
+ break;
+ }
+ }
+
+ // request Rx frames
+ if (rx_mode) {
+ if (osalSetRxMode(rawsock->sock, num_frames, frame_size) == -1) {
+ AVB_LOG_ERROR("Creating rawsock: Allocating rx buffers");
+ stcRawsockClose(rawsock);
+ rawsock = NULL;
+ break;
+ }
+ }
+
+ rawsock->pTask = stcTaskMgrGetCurrentTask();
+
+ if (osalBind(rawsock->sock) == -1) {
+ AVB_LOG_ERROR("Creating rawsock; bind socket");
+ stcRawsockClose(rawsock);
+ rawsock = NULL;
+ break;
+ }
+
+ } while (0);
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK);
+ return rawsock;
+}
+
+// Set signal on RX mode
+void stcSetRxSignalMode(void *pvRawsock, bool rxSignalMode)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK);
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+
+ if (rawsock) {
+ if (rawsock->sock != -1) {
+ osalSetRxSignalMode(rawsock->sock, rxSignalMode);
+ }
+ }
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK);
+}
+
+
+// Close the rawsock
+void stcRawsockClose(void *pvRawsock){
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK);
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+
+ if (rawsock) {
+ // close the socket
+ if (rawsock->sock != -1) {
+ osalCloseSocket(rawsock->sock);
+ rawsock->sock = -1;
+ }
+
+ // free the state struct
+ free(rawsock);
+ }
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK);
+}
+
+// Get a buffer from the ring to use for TX
+U8 *stcRawsockGetTxFrame(void *pvRawsock, bool blocking, U32 *len){
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ U8 *pBuffer = NULL;
+
+ if (!VALID_TX_RAWSOCK(rawsock) || len == NULL) {
+ AVB_LOG_ERROR("Getting TX frame; bad arguments");
+ }
+ else {
+ do {
+ rawsock->pTxFrame = osalSocketGetTxBuf(rawsock->sock);
+ if (rawsock->pTxFrame) {
+ pBuffer = rawsock->pTxFrame->data;
+ break;
+ }
+ else {
+ IF_LOG_INTERVAL(1000) {
+ AVB_LOG_ERROR("Getting TX frame: too many TX buffers in use");
+ }
+ break;
+ }
+ //if (blocking) {
+ // SLEEP_MSEC(1);
+ //}
+ } while (1);
+
+ if (pBuffer) {
+ // Remind client how big the frame buffer is
+ if (len) {
+ *len = rawsock->pTxFrame->length;
+ }
+ }
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return pBuffer;
+}
+
+// Set the Firewall MARK on the socket
+// The mark is used by FQTSS to identify AVTP packets.
+// FQTSS creates a mark that includes the AVB class and streamBytesPerSec
+bool stcRawsockTxSetMark(void *pvRawsock, int mark){
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ bool retval = FALSE;
+
+ if (!VALID_TX_RAWSOCK(rawsock)) {
+ AVB_LOG_ERROR("Setting TX mark; invalid argument passed");
+ }
+ else if (osalSetsockopt(rawsock->sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) {
+ AVB_LOG_ERROR("Setting TX mark; osalSetsockopt failed");
+ }
+ else {
+ AVB_LOGF_DEBUG("SO_MARK=%d OK", mark);
+ retval = TRUE;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return retval;
+}
+
+// Pre-set the Ethernet header information that will be used on TX frames
+bool stcRawsockTxSetHdr(void *pvRawsock, hdr_info_t *pHdr){
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ bool retVal = FALSE;
+
+ if (!VALID_TX_RAWSOCK(rawsock)) {
+ AVB_LOG_ERROR("Setting TX header; invalid argument");
+ }
+ else {
+ // source address
+ if (pHdr->shost) {
+ stc_safe_memcpy(&(rawsock->ethHdr.notag.shost), pHdr->shost, ETH_ALEN);
+ }
+ // destination address
+ if (pHdr->dhost) {
+ stc_safe_memcpy(&(rawsock->ethHdr.notag.dhost), pHdr->dhost, ETH_ALEN);
+ }
+
+ // VLAN tag?
+ if (!pHdr->vlan) {
+ // No, set ethertype in normal location
+ rawsock->ethHdr.notag.ethertype = htons(rawsock->ethertype);
+ // and set ethernet header length
+ rawsock->ethHdrLen = sizeof(eth_hdr_t);
+ }
+ else {
+ // Add VLAN tag
+ AVB_LOGF_DEBUG("VLAN=%d pcp=%d vid=%d", pHdr->vlan_vid, pHdr->vlan_pcp, pHdr->vlan_vid);
+
+ // Build bitfield with vlan_pcp and vlan_vid.
+ // I think CFI bit is alway 0
+ uint16_t bits = 0;
+ bits |= (pHdr->vlan_pcp << 13) & 0xE000;
+ bits |= pHdr->vlan_vid & 0x0FFF;
+
+ // Create VLAN tag
+ rawsock->ethHdr.tagged.vlan.tpip = htons(ETHERTYPE_VLAN);
+ rawsock->ethHdr.tagged.vlan.bits = htons(bits);
+ rawsock->ethHdr.tagged.ethertype = htons(rawsock->ethertype);
+ // and set Ethernet header length
+ rawsock->ethHdrLen = sizeof(eth_vlan_hdr_t);
+ }
+ retVal = TRUE;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return retVal;
+}
+
+// Copy the pre-set header to the outgoing frame
+bool stcRawsockTxFillHdr(void *pvRawsock, U8 *pBuffer, U32 *hdrlen) {
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ bool retVal = FALSE;
+
+ if (!VALID_TX_RAWSOCK(rawsock)) {
+ AVB_LOG_ERROR("Filling TX header; invalid argument");
+ }
+ else {
+ // Copy the default Ethernet header into the buffer
+ if (hdrlen)
+ *hdrlen = rawsock->ethHdrLen;
+ stc_safe_memcpy((char*)pBuffer, &(rawsock->ethHdr), rawsock->ethHdrLen);
+
+ retVal = TRUE;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return retVal;
+}
+
+// Release a TX frame, without marking it as ready to send
+bool stcRawsockRelTxFrame(void *pvRawsock, U8 *pBuffer){
+ bool retVal = TRUE;
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+
+ if (!VALID_TX_RAWSOCK(rawsock) || pBuffer == NULL) {
+ AVB_LOG_ERROR("Releasing TX frame; invalid argument");
+ retVal = FALSE;
+ }
+ else if (osalSocketRelTxBuf(rawsock->sock)) {
+ AVB_LOG_ERROR("Releasing TX frame");
+ retVal = FALSE;
+ }
+
+ rawsock->pTxFrame = NULL;
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return retVal;
+}
+
+// Ready Tx frame. pBuffer parameter not used in this implementation.
+bool stcRawsockTxFrameReady(void *pvRawsock, U8 *pBuffer, U32 len)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ bool retVal = FALSE;
+
+ if (!VALID_TX_RAWSOCK(rawsock)) {
+ AVB_LOG_ERROR("Marking TX frame ready; invalid argument");
+ }
+ else {
+ rawsock->pTxFrame->length = len;
+
+ retVal = TRUE;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return retVal;
+}
+
+// Send all packets that are ready (i.e. tell kernel to send them)
+int stcRawsockSend(void *pvRawsock) {
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ int sent = -1;
+
+ if (!VALID_TX_RAWSOCK(rawsock)) {
+ AVB_LOG_ERROR("Send; invalid argument");
+ }
+ else {
+ osalSocketPushTxBuf(rawsock->sock);
+ rawsock->pTxFrame = NULL;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return sent;
+}
+
+int stcRawsockTxBufLevel(void *pvRawsock){
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ int retVal = 0;
+
+ if (!VALID_RAWSOCK(rawsock)) {
+ AVB_LOG_ERROR("getting buffer level; invalid arguments");
+ }
+ else {
+ retVal = osalGetTxSockLevel(rawsock->sock);
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return retVal;
+}
+
+int stcRawsockRxBufLevel(void *pvRawsock){
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ int retVal = 0;
+
+ if (!VALID_RAWSOCK(rawsock)) {
+ AVB_LOG_ERROR("getting buffer level; invalid arguments");
+ }
+ else {
+ retVal = osalGetRxSockLevel(rawsock->sock);
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return retVal;
+}
+
+// Get a RX frame
+U8 *stcRawsockGetRxFrame(void *pvRawsock, U32 timeoutUSec, U32 *offset, U32 *len) {
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ U8 *pBuffer = NULL;
+
+ if (!VALID_RX_RAWSOCK(rawsock)) {
+ AVB_LOG_ERROR("Getting RX frame; invalid arguments");
+ }
+ else {
+ while (!rawsock->pRxFrame) {
+ rawsock->pRxFrame = osalSocketGetRxBuf(rawsock->sock);
+ if (!rawsock->pRxFrame) {
+ if (timeoutUSec == STC_RAWSOCK_NONBLOCK) {
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return NULL;
+ }
+ if (timeoutUSec == (U32)STC_RAWSOCK_BLOCK) {
+ TASK_MGR_SUSPEND(rawsock->pTask);
+ rawsock->pRxFrame = osalSocketGetRxBuf(rawsock->sock);
+ }
+ else {
+ TASK_MGR_NANOSLEEP(rawsock->pTask, timeoutUSec * 1000);
+ rawsock->pRxFrame = osalSocketGetRxBuf(rawsock->sock);
+ break; // Assumed timed out or packet ready. Caller will sort it out.
+ }
+ }
+ }
+
+ if (rawsock->pRxFrame) {
+ *offset = 0;
+ *len = rawsock->pRxFrame->length;
+ pBuffer = rawsock->pRxFrame->data;
+ }
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return pBuffer;
+}
+
+// Parse the Ethernet frame header. Returns length of header, or -1 for failure
+int stcRawsockRxParseHdr(void *pvRawsock, U8 *pBuffer, hdr_info_t *pInfo){
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+
+ // Current usage of this function of only to determine the header length.
+ // and the only usage of this port is always has a VLAN tag therefore
+ // the implementation is hidden
+ // ATNEL_TODO PERF this block isn't really needed consider using the simple case.
+#if 0
+ int pduOffset = -1;
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ if (!VALID_RX_RAWSOCK(rawsock)) {
+ AVB_LOG_ERROR("Parsing Ethernet headers; invalid arguments");
+ }
+ else {
+
+ memset(pInfo, 0, sizeof(hdr_info_t));
+
+ pduOffset = sizeof(eth_hdr_t);
+
+ eth_hdr_t *pNoTag = (eth_hdr_t*)pBuffer;
+ pInfo->shost = pNoTag->shost;
+ pInfo->dhost = pNoTag->dhost;
+ pInfo->ethertype = ntohs(pNoTag->ethertype);
+
+ if (pInfo->ethertype == ETHERTYPE_VLAN) {
+ uint16_t tp_vlan_tci = ntohs(*(uint16_t *)(pBuffer + pduOffset));
+ pInfo->vlan = TRUE;
+ pInfo->vlan_vid = tp_vlan_tci & 0x0FFF;
+ pInfo->vlan_pcp = (tp_vlan_tci >> 13) & 0x0007;
+ pInfo->ethertype = ntohs(*(uint16_t *)(pBuffer + pduOffset + 2));
+ pduOffset += 4; // sizeof 802.1Q header
+ }
+ }
+#else
+ int pduOffset = sizeof(eth_hdr_t) + 4;
+#endif
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return pduOffset;
+}
+
+// Release a RX frame held by the client
+bool stcRawsockRelRxFrame(void *pvRawsock, U8 *pBuffer){
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ bool retVal = FALSE;
+
+ if (!VALID_RX_RAWSOCK(rawsock)) {
+ AVB_LOG_ERROR("Releasing RX frame; invalid arguments");
+ }
+ else {
+ retVal = osalSocketPullRxBuf(rawsock->sock);
+ rawsock->pRxFrame = NULL;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return retVal;
+}
+
+// Setup the rawsock to receive multicast packets
+bool stcRawsockRxMulticast(void *pvRawsock, bool add_membership, const U8 addr[ETH_ALEN]){
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+
+ bool retVal = FALSE;
+
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ if (!VALID_RX_RAWSOCK(rawsock)) {
+ AVB_LOG_ERROR("Setting multicast; invalid arguments");
+ }
+ else {
+ if (osalSetsockopt(rawsock->sock, SOL_SOCKET, (add_membership ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP), addr, ETH_ALEN) < 0) {
+ AVB_LOG_ERROR("Setting multicast at HAL");
+ }
+ else {
+ retVal = TRUE;
+ }
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return retVal;
+}
+
+bool stcRawsockRxAVTPSubtype(void *pvRawsock, U8 subtype){
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+
+ bool retVal = FALSE;
+
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ if (!VALID_RX_RAWSOCK(rawsock)) {
+ AVB_LOG_ERROR("Setting AVTP subtype: invalid arguments");
+ }
+ else {
+ if (osalSetAvtpSubtype(rawsock->sock, subtype) < 0) {
+ AVB_LOG_ERROR("Setting AVTP subtype filter");
+ }
+ else {
+ retVal = TRUE;
+ }
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return retVal;
+}
+
+// Get the socket used for this rawsock; can be used for poll/select
+int stcRawsockGetSocket(void *pvRawsock){
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK);
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ int retVal = -1;
+
+ if (!rawsock) {
+ AVB_LOG_ERROR("Getting socket; invalid arguments");
+ }
+ else {
+ retVal = rawsock->sock;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK);
+ return retVal;
+}
+
+// Get the Ethernet address of the interface
+bool stcRawsockGetAddr(void *pvRawsock, U8 addr[ETH_ALEN]){
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK);
+ bool retVal = FALSE;
+ raw_sock_t *rawsock = (raw_sock_t*)pvRawsock;
+ if (!rawsock) {
+ AVB_LOG_ERROR("Getting address; invalid arguments");
+ }
+ else {
+ stc_safe_memcpy(addr, &rawsock->ifInfo.mac.ether_addr_octet, ETH_ALEN);
+ retVal = TRUE;
+ }
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK);
+ return retVal;
+}
diff --git a/lib/avtp_pipeline/platform/Linux/rawsock/openavb_sockets.c b/lib/avtp_pipeline/platform/Linux/rawsock/openavb_sockets.c new file mode 100644 index 00000000..e88d8665 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/rawsock/openavb_sockets.c @@ -0,0 +1,625 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include "stc_avb_platform.h"
+
+#include "stc_avb_ether_hal.h"
+#include "stc_qmgr.h"
+#include "stc_sockets.h"
+#include "stc_avb_tasks.h"
+#include "stc_avb_types.h"
+#include "stc_array.h"
+#include "stc_queue.h"
+#include "stc_debug.h"
+
+#define AVB_LOG_COMPONENT "OSAL Socket Layer"
+#include "stc_avb_pub.h"
+#include "stc_avb_log.h"
+
+#define SOCK_VALID (1<<0)
+#define SOCK_BIND (1<<1)
+#define SOCK_MULTICAST (1<<2)
+
+#define VLAN_TYPE_NS 0x0081
+
+STC_CODE_MODULE_PRI
+
+THREAD_TYPE(socketRXThread);
+THREAD_DEFINITON(socketRXThread);
+
+THREAD_TYPE(socketTXThread);
+THREAD_DEFINITON(socketTXThread);
+
+STC_DATA_PRI COMPILER_ALIGNED(8) static MUTEX_HANDLE_ALT(gSocketMutex);
+#define SOCKET_LOCK() MUTEX_LOCK_ALT(gSocketMutex)
+#define SOCKET_UNLOCK() MUTEX_UNLOCK_ALT(gSocketMutex)
+
+STC_DATA_PRI COMPILER_ALIGNED(8) static MUTEX_HANDLE_ALT(gSocketTxMutex);
+#define SOCKET_TX_LOCK() MUTEX_LOCK_ALT(gSocketTxMutex)
+#define SOCKET_TX_UNLOCK() MUTEX_UNLOCK_ALT(gSocketTxMutex)
+
+typedef struct {
+ int flags;
+ uint16_t protocol; // in network format
+ int mark;
+ uint8_t multicastAddr[6];
+ bool bAvtpSubtype; // Filter based on AvtpSubType
+ uint8_t avtpSubtype; // AVTP subtype to filter on Including the CD indicator
+ bool bPollMode; // TRUE is socket RX using Poll FD
+ bool bRxSignalMode; // TRUE to send signal on RX packet
+ bool bRx; // Socket used for RX (used for filtering rx packets)
+ bool bTx; // Socket used for TX (used for filtering tx packets)
+ STC_POLLFD_DEF pollFd;
+
+ thread_type *pTask; // Direct task resume via Task Manager
+ rxSockBufStruct *rxSockBuf;
+ stc_queue_t rxQueue;
+ stc_queue_t txQueue; // Currently not used
+ int sockId;
+} socket_t;
+
+// Array of sockets
+STC_DATA_PRI COMPILER_ALIGNED(8) stc_array_t socketArray;
+
+thread_type *pSocketRXTask = NULL;
+thread_type *pSocketTXTask = NULL;
+
+static inline socket_t *findSocket(int sk)
+{
+ return stcArrayDataIdx(socketArray, sk);
+}
+
+void socketRXDirectAVTP(void)
+{
+ U32 frameSize;
+ U8 *pRxBuf = halGetRxBufAVTP(&frameSize);
+ while (pRxBuf) {
+ // If AVB isn't running yet toss AVTP packets
+ if (bAVBRunning) {
+ ethFrameStruct *ethFrame;
+ ethFrame = (ethFrameStruct *)pRxBuf;
+
+ U32 iter;
+ stc_array_elem_t elem;
+ elem = stcArrayIterFirstAlt(socketArray, &iter);
+ while (elem) {
+ socket_t *sock = stcArrayData(elem);
+
+ if (sock &&
+ (sock->flags & SOCK_BIND) &&
+ (sock->bRx ) &&
+ ((memcmp((void *)sock->multicastAddr, ethFrame->destAddr, 6) == 0)))
+ {
+ stc_queue_elem_t elem = stcQueueHeadLock(sock->rxQueue);
+ if (elem) {
+ eth_frame_t *pFrame = stcQueueData(elem);
+ pFrame->length = frameSize;
+ stc_safe_memcpy(pFrame->data, pRxBuf, frameSize);
+ stcQueueHeadPush(sock->rxQueue);
+
+ if (sock->bRxSignalMode) {
+ TASK_MGR_SIGNAL(sock->pTask);
+ }
+ else {
+ if (stcQueueGetElemCount(sock->rxQueue) >= (stcQueueGetQueueSize(sock->rxQueue) >> 1)) {
+ // rx buffer 1/2 full start signalling anyway
+ TASK_MGR_SIGNAL(sock->pTask);
+ }
+ }
+ }
+ }
+
+ elem = stcArrayIterNextAlt(socketArray, &iter);
+ }
+ }
+ // Read next frame
+ pRxBuf = halGetRxBufAVTP(&frameSize);
+ }
+}
+
+// Socket RX Task
+static void socketRXThreadFn(void *p_arg)
+{
+ pSocketRXTask = TASK_MGR_TASKDATA(socketRXThread);
+ stcTaskMgrAdd(pSocketRXTask);
+
+ while (bAVBRunning) {
+ // The HAL Ethernet driver is the only way this task will wakeup.
+ // NOTE: Using a semaphore here is costly therefore task suspend is used in combination with TaskMgr.
+ TASK_MGR_SUSPEND(pSocketRXTask);
+
+ U32 frameSize;
+ bool bPtpCBUsed;
+ U8 *pRxBuf = halGetRxBufAVB(&frameSize, &bPtpCBUsed);
+ while (pRxBuf || bPtpCBUsed) {
+ if (pRxBuf) {
+ ethFrameStruct *ethFrame;
+ U16 ethernetType;
+ U16 typeVlan;
+
+ ethFrame = (ethFrameStruct *)pRxBuf;
+ typeVlan = ntohs(*(U16 *)(ðFrame->payload + 2));
+ ethernetType = ntohs(ethFrame->type);
+
+ U32 iter;
+ stc_array_elem_t elem;
+ elem = stcArrayIterFirstAlt(socketArray, &iter);
+ while (elem) {
+ socket_t *sock = stcArrayData(elem);
+
+ if (sock &&
+ (sock->flags & SOCK_BIND) &&
+ (sock->bRx ) &&
+ (((ethernetType == VLAN_TYPE_NS) && (sock->protocol == typeVlan)) || (ethernetType == sock->protocol)) &&
+ (((sock->flags & SOCK_MULTICAST) == 0) || (memcmp((void *)sock->multicastAddr, ethFrame->destAddr, 6) == 0)) &&
+ ((sock->bAvtpSubtype == FALSE) || (pRxBuf[18] == sock->avtpSubtype)))
+ {
+ stc_queue_elem_t elem = stcQueueHeadLock(sock->rxQueue);
+ if (elem) {
+ eth_frame_t *pFrame = stcQueueData(elem);
+ pFrame->length = frameSize;
+ stc_safe_memcpy(pFrame->data, pRxBuf, frameSize);
+ stcQueueHeadPush(sock->rxQueue);
+
+ if (sock->bRxSignalMode) {
+ if (sock->bPollMode) {
+ STC_POLLFD_SIG(&sock->pollFd);
+ }
+ else {
+ TASK_MGR_SIGNAL(sock->pTask);
+ }
+ }
+ else {
+ if (stcQueueGetElemCount(sock->rxQueue) >= (stcQueueGetQueueSize(sock->rxQueue) >> 1)) {
+ // rx buffer 1/2 full start signalling anyway
+ if (sock->bPollMode) {
+ STC_POLLFD_SIG(&sock->pollFd);
+ }
+ else {
+ TASK_MGR_SIGNAL(sock->pTask);
+ }
+ }
+ }
+ }
+ else {
+ // TODO: This output causes lockup. Why?
+ // AVB_LOG_ERROR("out of socket RX Queue buffers");
+ }
+ }
+
+ elem = stcArrayIterNextAlt(socketArray, &iter);
+ }
+ }
+
+ // Read next frame
+ pRxBuf = halGetRxBufAVB(&frameSize, &bPtpCBUsed);
+ }
+ }
+}
+
+// Socket TX Task : Currently not used
+#if 0
+static void socketTXThreadFn(void *p_arg)
+{
+ pSocketTXTask = TASK_MGR_TASKDATA(socketTXThread);
+ stcTaskMgrAdd(pSocketTXTask);
+
+ while (bAVBRunning) {
+ TASK_MGR_SUSPEND(pSocketTXTask);
+
+ // TODO: This algorithm for sending is minimalistic and inefficient and will need to be revisited
+ // when more than 1 stream is supported.
+ // Send as many frames as the Hal Ether will take
+ U32 iter;
+ stc_array_elem_t sockElem;
+ sockElem = stcArrayIterFirstAlt(socketArray, &iter);
+ while (sockElem) {
+ socket_t *sock = stcArrayData(sockElem);
+
+ stc_queue_elem_t elem = stcQueueTailLock(sock->txQueue);
+ while (elem) {
+ eth_frame_t *pFrame = stcQueueData(elem);
+
+ if (!halSendTxBuffer(pFrame->data, pFrame->length, TC_AVB_MARK_CLASS(sock->mark))) {
+ // No buffers were available. Release the queue item and process next time.
+ stcQueueTailUnlock(sock->txQueue);
+ break;
+ }
+
+ stcQueueTailPull(sock->txQueue);
+ elem = stcQueueTailLock(sock->txQueue);
+ }
+
+ sockElem = stcArrayIterNextAlt(socketArray, &iter);
+ }
+ }
+}
+#endif
+
+bool osalInitSockets(U32 maxSockets)
+{
+ // Check parameter
+ if (!maxSockets)
+ return FALSE;
+
+ // Create array to hold sockets
+ socketArray = stcArrayNewArray(sizeof(socket_t));
+ if (!socketArray)
+ return FALSE;
+ if (!stcArraySetInitSize(socketArray, maxSockets))
+ return FALSE;
+
+ MUTEX_CREATE_ALT(gSocketMutex);
+ MUTEX_CREATE_ALT(gSocketTxMutex);
+
+ // Start the Rx Parser Task
+ {
+ bool errResult;
+ THREAD_CREATE(socketRXThread, socketRXThread, NULL, socketRXThreadFn, NULL);
+ THREAD_CHECK_ERROR(socketRXThread, "Task create failed", errResult);
+ if (errResult); // Already reported
+ }
+
+ // Start the Tx Parser Task : Currently not used
+#if 0
+ {
+ bool errResult;
+ THREAD_CREATE(socketTXThread, socketTXThread, NULL, socketTXThreadFn, NULL);
+ THREAD_CHECK_ERROR(socketTXThread, "Task create failed", errResult);
+ if (errResult); // Already reported
+ }
+#endif
+
+ return TRUE;
+}
+
+int osalSocket(int domain, int type, int protocol)
+{
+ int retSk = -1;
+
+ // only support PF_PACKET and SOCK_RAW
+ if ((domain == PF_PACKET) && (type == SOCK_RAW)) {
+ SOCKET_LOCK();
+
+ stc_array_elem_t elem = stcArrayNew(socketArray);
+ if (elem) {
+ socket_t *newSocket = stcArrayData(elem);
+ newSocket->sockId = stcArrayGetIdx(elem);
+ newSocket->flags = SOCK_VALID; // socket is valid
+ newSocket->protocol = ntohs((uint16_t)protocol); // store protocol in network format
+ newSocket->bRxSignalMode = TRUE;
+
+ newSocket->pTask = stcTaskMgrGetCurrentTask();
+
+ retSk = newSocket->sockId;
+ }
+ else {
+ AVB_LOG_ERROR("Too many sockets");
+ }
+ SOCKET_UNLOCK();
+ }
+
+ return retSk;
+}
+
+int osalSetsockopt(int sk, int level, int optname, const void *optval, socklen_t optlen)
+{
+ int retSk = -1;
+ socket_t *sock = findSocket(sk);
+
+ if (sock && (optval != NULL)) {
+ if ((level == SOL_SOCKET) && (optname == SO_MARK) && (optlen == sizeof(int))) {
+ sock->mark = *(int *)optval;
+ retSk = sk;
+ }
+ else if ((level == SOL_SOCKET) && (optname == PACKET_ADD_MEMBERSHIP) && (optlen == 6)) {
+ stc_safe_memcpy(sock->multicastAddr, optval, 6);
+ sock->flags |= SOCK_MULTICAST;
+ if (halEtherAddMulticast(sock->multicastAddr, TRUE))
+ retSk = sk;
+ }
+ else if ((level == SOL_SOCKET) && (optname == PACKET_DROP_MEMBERSHIP)) {
+ if (halEtherAddMulticast(sock->multicastAddr, FALSE))
+ retSk = sk;
+ memset(sock->multicastAddr, 0, 6);
+ sock->flags &= ~SOCK_MULTICAST;
+ }
+ }
+
+ return retSk;
+}
+
+int osalSetAvtpSubtype(int sk, uint8_t avtpSubtype)
+{
+ int retSk = -1;
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ sock->bAvtpSubtype = TRUE;
+ sock->avtpSubtype = avtpSubtype;
+ retSk = sk;
+ }
+
+ return retSk;
+}
+
+int osalEnablePoll(int sk)
+{
+ int retSk = -1;
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ sock->bPollMode = TRUE;
+ STC_POLLFD_INIT(&sock->pollFd);
+ retSk = sk;
+ }
+
+ return retSk;
+}
+
+
+int osalSetRxMode(int sk, int bufCount, int bufSize)
+{
+ int retSk = -1;
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ sock->bRx = TRUE;
+
+ sock->rxQueue = stcQueueNewQueue(sizeof(eth_frame_t) + bufSize, bufCount);
+ if (!sock->rxQueue) {
+ AVB_LOG_ERROR("Out of memory creating socket rxQueue");
+ }
+ else {
+ retSk = sk;
+ }
+ }
+
+ return retSk;
+}
+
+int osalSetRxSignalMode(int sk, bool rxSignalMode)
+{
+ int retSk = -1;
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ sock->bRxSignalMode = rxSignalMode;
+ }
+
+ return retSk;
+}
+
+
+int osalSetTxMode(int sk, int bufCount, int bufSize)
+{
+ int retSk = -1;
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ sock->bTx = TRUE;
+
+ sock->txQueue = stcQueueNewQueue(sizeof(eth_frame_t) + bufSize, bufCount);
+ if (!sock->txQueue) {
+ AVB_LOG_ERROR("Out of memory creating socket txQueue");
+ }
+ else {
+ retSk = sk;
+ }
+ }
+
+ return retSk;
+}
+
+int osalBind(int sk)
+{
+ int retSk = -1;
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ sock->flags |= SOCK_BIND;
+ retSk = sk;
+ }
+
+ return retSk;
+}
+
+int osalCloseSocket(int sk)
+{
+ int retSk = -1;
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ SOCKET_LOCK();
+
+ // check to see if there is a multicastAddr, if so do a halEtherAddMulticast(del)
+ if (sock->flags & SOCK_MULTICAST) {
+ // delete multicast addr
+ if (!halEtherAddMulticast(sock->multicastAddr, 0)) {
+ AVB_LOG_ERROR("Sock Close: HAL delete multicast");
+ }
+ }
+
+ STC_POLLFD_DESTROY(&sock->pollFd);
+
+ if (sock->bRx) {
+ stcQueueDeleteQueue(sock->rxQueue);
+ }
+ if (sock->bTx) {
+ stcQueueDeleteQueue(sock->txQueue);
+ }
+
+ stc_array_elem_t elem = stcArrayIdx(socketArray, sk);
+ if (elem) {
+ stcArrayDelete(socketArray, elem);
+ }
+
+ SOCKET_UNLOCK();
+
+ retSk = sk;
+ }
+
+ return retSk;
+}
+
+STC_POLLFD_PTR osalGetSocketPollFd(int sk)
+{
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ return &sock->pollFd;
+ }
+ return NULL;
+}
+
+
+int osalGetRxSockLevel(int sk)
+{
+ int retLevel = 0;
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ retLevel = stcQueueGetElemCount(sock->rxQueue);
+ }
+
+ return retLevel;
+}
+
+int osalGetTxSockLevel(int sk)
+{
+ int retLevel = 0;
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ retLevel = stcQueueGetElemCount(sock->txQueue);
+ }
+
+ return retLevel;
+}
+
+// Get the next Rx buffer locking it. Null if none available
+eth_frame_t *osalSocketGetRxBuf(int sk)
+{
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ stc_queue_elem_t elem = stcQueueTailLock(sock->rxQueue);
+ if (elem) {
+ return (eth_frame_t *)stcQueueData(elem);
+ }
+ }
+
+ return NULL;
+}
+
+// Release (unlock) the Rx buffer for a socket keeping it in the queue. Returns TRUE on success
+bool osalSocketRelRxBuf(int sk)
+{
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ stcQueueTailUnlock(sock->rxQueue);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// Pulls the Rx buffer off the queue. Returns TRUE on success.
+bool osalSocketPullRxBuf(int sk)
+{
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ stcQueueTailPull(sock->rxQueue);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// Get the next Tx buffer locking it. Null if none available
+eth_frame_t *osalSocketGetTxBuf(int sk)
+{
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ stc_queue_elem_t elem = stcQueueHeadLock(sock->txQueue);
+ if (elem) {
+ eth_frame_t *pEthFrame = stcQueueData(elem);
+ pEthFrame->length = stcQueueGetElemSize(sock->txQueue);
+ return pEthFrame;
+ }
+ }
+
+ return NULL;
+}
+
+// Release (unlock) the Tx buffer. Returns TRUE on success
+bool osalSocketRelTxBuf(int sk)
+{
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ stcQueueHeadUnlock(sock->txQueue);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// Pushes the Tx buffer onto the queue. Returns TRUE on success.
+bool osalSocketPushTxBuf(int sk)
+{
+ socket_t *sock = findSocket(sk);
+
+ if (sock) {
+ stcQueueHeadPush(sock->txQueue);
+
+ SOCKET_TX_LOCK();
+ stc_queue_elem_t elem = stcQueueTailLock(sock->txQueue);
+ while (elem) {
+ eth_frame_t *pFrame = stcQueueData(elem);
+
+ if (!halSendTxBuffer(pFrame->data, pFrame->length, TC_AVB_MARK_CLASS(sock->mark))) {
+ // No buffers were available. Release the queue item and process next time.
+ stcQueueTailUnlock(sock->txQueue);
+ break;
+ }
+
+ stcQueueTailPull(sock->txQueue);
+ elem = stcQueueTailLock(sock->txQueue);
+ }
+ SOCKET_TX_UNLOCK();
+
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/lib/avtp_pipeline/platform/Linux/rawsock/openavb_sockets.h b/lib/avtp_pipeline/platform/Linux/rawsock/openavb_sockets.h new file mode 100644 index 00000000..ec9f6ba9 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/rawsock/openavb_sockets.h @@ -0,0 +1,173 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#ifndef STC_SOCKETS_H
+#define STC_SOCKETS_H 1
+
+#include <stdint.h>
+#include <stddef.h>
+#include "stc_avb_platform.h"
+#include "stc_avb_time_osal.h"
+
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 1
+#endif
+
+typedef struct {
+ U16 length;
+ U8 data[]; // Empty array points to data. struct memory must be manually allocated.
+} eth_frame_t;
+
+typedef struct ethFrameStruct {
+ uint8_t destAddr[6];
+ uint8_t srcAddr[6];
+ uint16_t type;
+ uint8_t payload;
+} ethFrameStruct;
+
+typedef struct ethFrameVlanStruct {
+ uint8_t destAddr[6];
+ uint8_t srcAddr[6];
+ uint8_t tag[4];
+ uint16_t type;
+ uint8_t payload;
+}ethFrameVlanStruct;
+
+typedef struct rxSockBufStruct {
+ struct rxSockBufStruct *next;
+ uint8_t *packet; // pointer to start of packet (which starts with length)
+}rxSockBufStruct;
+
+#define AF_INET 2
+#define PF_PACKET 17 // Packet family.
+
+// Socket protocol types (TCP/UDP/RAW)
+#define SOCK_STREAM 1
+#define SOCK_DGRAM 2
+#define SOCK_RAW 3
+
+struct sockaddr {
+ uint8_t sa_len;
+ uint8_t sa_family;
+ char sa_data[14];
+};
+
+#define PACKET_MR_MULTICAST 0
+#define PACKET_MR_PROMISC 1
+#define PACKET_MR_ALLMULTI 2
+
+#define PACKET_ADD_MEMBERSHIP 1
+#define PACKET_DROP_MEMBERSHIP 2
+#define PACKET_RECV_OUTPUT 3
+#define PACKET_STATISTICS 6
+
+#define IFF_UP 0x1 // interface is up
+
+#define TPACKET_ALIGNMENT 16
+#define TPACKET_ALIGN(x) (((x)+TPACKET_ALIGNMENT-1)&~(TPACKET_ALIGNMENT-1))
+
+#ifndef socklen_t
+# define socklen_t int
+#endif
+
+#define SOL_SOCKET 1
+#define SOL_PACKET 263
+#define SO_MARK 36
+#define ETHERTYPE_VLAN 0x8100 // IEEE 802.1Q VLAN tagging
+#define MSG_DONTWAIT 0x08 // Nonblocking i/o for this operation only
+#define EINTR 4 // Interrupted system call
+
+// Supported error numbers
+#define ENOMEM 12 // Out of memory
+#define EINVAL 22 // Invalid argument
+
+// initialize sockets and the Rx buffer task
+bool osalInitSockets(U32 maxSockets);
+
+// Create a new socket of type TYPE in domain DOMAIN, using
+// protocol PROTOCOL. If PROTOCOL is zero, one is chosen automatically.
+// Returns a file descriptor for the new socket, or -1 for errors.
+int osalSocket(int domain, int type, int protocol);
+
+// Set socket options
+int osalSetsockopt(int sk, int level, int optname, const void *optval, socklen_t optlen);
+
+// Set the AVTP subtype for filtering
+int osalSetAvtpSubtype(int sk, uint8_t avtpSubtype);
+
+// Enable Poll Mode
+int osalEnablePoll(int sk);
+
+// Set socket as RX
+int osalSetRxMode(int sk, int bufCount, int bufSize);
+
+// Set socket signal on RX mode
+int osalSetRxSignalMode(int sk, bool rxSignalMode);
+
+// Set socket as TX
+int osalSetTxMode(int sk, int bufCount, int bufSize);
+
+// bind, allows transmission, and reception of socket data
+int osalBind(int sk);
+
+// close socket
+int osalCloseSocket(int sk);
+
+// Get the PollFd for the socket
+STC_POLLFD_PTR osalGetSocketPollFd(int sk);
+
+// get unprocessed Rx buffers
+int osalGetRxSockLevel(int sk);
+
+// get unprocessed Tx buffers
+int osalGetTxSockLevel(int sk);
+
+// Get the next Rx buffer locking it. Null if none available
+eth_frame_t *osalSocketGetRxBuf(int sk);
+
+// Release (unlock) the Rx buffer for a socket keeping it in the queue. Returns TRUE on success
+bool osalSocketRelRxBuf(int sk);
+
+// Pulls the Rx buffer off the queue. Returns TRUE on success.
+bool osalSocketPullRxBuf(int sk);
+
+// Get the next Tx buffer locking it. Null if none available
+eth_frame_t *osalSocketGetTxBuf(int sk);
+
+// Release (unlock) the Tx buffer. Returns TRUE on success
+bool osalSocketRelTxBuf(int sk);
+
+// Pushes the Tx buffer onto the queue. Returns TRUE on success.
+bool osalSocketPushTxBuf(int sk);
+
+// Direct call (not in task) to process a AVTP packet or packets
+void socketRXDirectAVTP(void);
+
+#endif // STC_SOCKETS_H
diff --git a/lib/avtp_pipeline/platform/Linux/rawsock/rawsock_rx.c b/lib/avtp_pipeline/platform/Linux/rawsock/rawsock_rx.c new file mode 100644 index 00000000..aed29f2d --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/rawsock/rawsock_rx.c @@ -0,0 +1,194 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <glib.h> +#include "./openavb_rawsock.h" + +// Common usage with VTAG 0x8100: ./rawsock_rx -i eth0 -t 33024 -d 1 -s 1 +// Common usage without VTAG 0x22F0: ./rawsock_rx -i eth0 -t 8944 -d 1 -s 1 + +#define MAX_NUM_FRAMES 10 +#define TIMESPEC_TO_NSEC(ts) (((uint64_t)ts.tv_sec * (uint64_t)NANOSECONDS_PER_SECOND) + (uint64_t)ts.tv_nsec) + +static bool bRunning = TRUE; + +static char* interface = NULL; +static int ethertype = -1; +static char* macaddr_s = NULL; +static int dumpFlag = 0; +static int reportSec = 1; + +static GOptionEntry entries[] = +{ + { "interface", 'i', 0, G_OPTION_ARG_STRING, &interface, "network interface", "NAME" }, + { "ethertype", 't', 0, G_OPTION_ARG_INT, ðertype, "ethernet protocol", "NUM" }, + { "mac", 'a', 0, G_OPTION_ARG_STRING, &macaddr_s, "MAC address", "MAC" }, + { "dump", 'd', 0, G_OPTION_ARG_INT, &dumpFlag, "Dump packets (1=yes, 0=no)", "DUMP" }, + { "rptsec", 's', 0, G_OPTION_ARG_INT, &reportSec, "report interval in seconds", "RPTSEC" }, + { NULL } +}; + +void dumpAscii(U8 *pFrame, int i, int *j) +{ + char c; + + printf(" "); + + while (*j <= i) { + c = pFrame[*j]; + *j += 1; + if (!isprint(c) || isspace(c)) + c = '.'; + printf("%c", c); + } +} + +void dumpFrameContent(U8 *pFrame, U32 len) +{ + int i = 0, j = 0; + while (TRUE) { + if (i % 16 == 0) { + if (i != 0 ) { + // end of line stuff + dumpAscii(pFrame, (i < len ? i : len), &j); + printf("\n"); + + if (i >= len) + break; + } + if (i+1 < len) { + // start of line stuff + printf("0x%4.4d: ", i); + } + } + else if (i % 2 == 0) { + printf(" "); + } + + if (i >= len) + printf(" "); + else + printf("%2.2x", pFrame[i]); + + i += 1; + } +} + +void dumpFrame(U8 *pFrame, U32 len, hdr_info_t *hdr) +{ + printf("Frame received, ethertype=0x%x len=%u\n", hdr->ethertype, len); + printf("src: %s\n", ether_ntoa((const struct ether_addr*)hdr->shost)); + printf("dst: %s\n", ether_ntoa((const struct ether_addr*)hdr->dhost)); + if (hdr->vlan) { + printf("VLAN pcp=%u, vid=%u\n", (unsigned)hdr->vlan_pcp, hdr->vlan_vid); + } + dumpFrameContent(pFrame, len); + printf("\n"); +} + +int main(int argc, char* argv[]) +{ + GError *error = NULL; + GOptionContext *context; + //U8 *macaddr; + struct ether_addr *macaddr; + + context = g_option_context_new("- rawsock listenr"); + g_option_context_add_main_entries(context, entries, NULL); + if (!g_option_context_parse(context, &argc, &argv, &error)) + { + printf("error: %s\n", error->message); + exit(1); + } + + if (interface == NULL || ethertype == -1) { + printf("error: must specify network interface and ethertype\n"); + exit(2); + } + + void* rs = openavbRawsockOpen(interface, TRUE, FALSE, ethertype, 0, MAX_NUM_FRAMES); + if (!rs) { + printf("error: failed to open raw socket (are you root?)\n"); + exit(3); + } + + if (macaddr_s) { + macaddr = ether_aton(macaddr_s); + if (macaddr) { + // if (openavbRawsockRxMulticast(rs, TRUE, macaddr) == FALSE) { + if (openavbRawsockRxMulticast(rs, TRUE, macaddr->ether_addr_octet) == FALSE) { + printf("error: failed to add multicast mac address\n"); + exit(4); + } + } + else + printf("warning: failed to convert multicast mac address\n"); + } + + U8 *pBuf, *pFrame; + U32 offset, len; + hdr_info_t hdr; + + struct timespec now; + static U32 packetCnt = 0; + static U64 nextReportInterval = 0; + + clock_gettime(CLOCK_MONOTONIC, &now); + nextReportInterval = TIMESPEC_TO_NSEC(now) + (NANOSECONDS_PER_SECOND * reportSec); + + while (bRunning) { + pBuf = openavbRawsockGetRxFrame(rs, OPENAVB_RAWSOCK_BLOCK, &offset, &len); + pFrame = pBuf + offset; + openavbRawsockRxParseHdr(rs, pBuf, &hdr); + if (dumpFlag) { + dumpFrame(pFrame, len, &hdr); + } + openavbRawsockRelRxFrame(rs, pBuf); + + packetCnt++; + + clock_gettime(CLOCK_MONOTONIC, &now); + U64 nowNSec = TIMESPEC_TO_NSEC(now);; + + if (reportSec > 0) { + if (nowNSec > nextReportInterval) { + printf("RX Packets: %d\n", packetCnt); + packetCnt = 0; + nextReportInterval = nowNSec + (NANOSECONDS_PER_SECOND * reportSec); + } + } + } + + openavbRawsockClose(rs); + return 0; +} diff --git a/lib/avtp_pipeline/platform/Linux/rawsock/rawsock_tx.c b/lib/avtp_pipeline/platform/Linux/rawsock/rawsock_tx.c new file mode 100644 index 00000000..1e683bab --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/rawsock/rawsock_tx.c @@ -0,0 +1,219 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <glib.h> +#include "./openavb_rawsock.h" + +//Common usage: ./rawsock_tx -i eth0 -t 8944 -r 8000 -s 1 -c 1 -m 1 -l 100 + +#define MAX_NUM_FRAMES 100 +#define NANOSECONDS_PER_SECOND (1000000000ULL) +#define TIMESPEC_TO_NSEC(ts) (((uint64_t)ts.tv_sec * (uint64_t)NANOSECONDS_PER_SECOND) + (uint64_t)ts.tv_nsec) + +#define RAWSOCK_TX_MODE_FILL (0) +#define RAWSOCK_TX_MODE_SEQ (1) + +static char* interface = NULL; +static int ethertype = -1; +static int txlen = 64; +static int txRate = 8000; +static int chunkSize = 1; +static int reportSec = 1; +static int mode = RAWSOCK_TX_MODE_FILL; + +static GOptionEntry entries[] = +{ + { "interface", 'i', 0, G_OPTION_ARG_STRING, &interface, "network interface", "NAME" }, + { "ethertype", 't', 0, G_OPTION_ARG_INT, ðertype, "ethernet protocol", "NUM" }, + { "length", 'l', 0, G_OPTION_ARG_INT, &txlen, "frame length", "LEN" }, + { "rate", 'r', 0, G_OPTION_ARG_INT, &txRate, "tx rate", "RATE" }, + { "chunk", 'c', 0, G_OPTION_ARG_INT, &chunkSize, "Chunk size", "CHUNKSIZE" }, + { "rptsec", 's', 0, G_OPTION_ARG_INT, &reportSec, "report interval in seconds", "RPTSEC" }, + { "mode", 'm', 0, G_OPTION_ARG_INT, &mode, "mode: 0 = fill, 1 = sequence number", "MODE" }, + { NULL } +}; + +void dumpAscii(U8 *pFrame, int i, int *j) +{ + char c; + + printf(" "); + + while (*j <= i) { + c = pFrame[*j]; + *j += 1; + if (!isprint(c) || isspace(c)) + c = '.'; + printf("%c", c); + } +} + +void dumpFrameContent(U8 *pFrame, U32 len) +{ + int i = 0, j = 0; + while (TRUE) { + if (i % 16 == 0) { + if (i != 0 ) { + // end of line stuff + dumpAscii(pFrame, (i < len ? i : len), &j); + printf("\n"); + + if (i >= len) + break; + } + if (i+1 < len) { + // start of line stuff + printf("0x%4.4d: ", i); + } + } + else if (i % 2 == 0) { + printf(" "); + } + + if (i >= len) + printf(" "); + else + printf("%2.2x", pFrame[i]); + + i += 1; + } +} + +void dumpFrame(U8 *pFrame, U32 len, hdr_info_t *hdr) +{ + printf("Frame received, ethertype=0x%x len=%u\n", hdr->ethertype, len); + printf("src: %s\n", ether_ntoa((const struct ether_addr*)hdr->shost)); + printf("dst: %s\n", ether_ntoa((const struct ether_addr*)hdr->dhost)); + if (hdr->vlan) { + printf("VLAN pcp=%u, vid=%u\n", (unsigned)hdr->vlan_pcp, hdr->vlan_vid); + } + dumpFrameContent(pFrame, len); + printf("\n"); +} + +int main(int argc, char* argv[]) +{ + GError *error = NULL; + GOptionContext *context; + + context = g_option_context_new("- rawsock listenr"); + g_option_context_add_main_entries(context, entries, NULL); + if (!g_option_context_parse(context, &argc, &argv, &error)) + { + printf("error: %s\n", error->message); + exit(1); + } + + if (interface == NULL || ethertype < 0) { + printf("error: must specify network interface and ethertype\n"); + exit(2); + } + + void* rs = openavbRawsockOpen(interface, FALSE, TRUE, ethertype, 0, MAX_NUM_FRAMES); + if (!rs) { + printf("error: failed to open raw socket (are you root?)\n"); + exit(3); + } + + U8 *pBuf, *pData; + U32 buflen, hdrlen, datalen; + hdr_info_t hdr; + + memset(&hdr, 0, sizeof(hdr_info_t)); + openavbRawsockTxSetHdr(rs, &hdr); + + struct timespec now; + static U64 packetIntervalNSec = 0; + static U64 nextCycleNSec = 0; + static U32 packetCnt = 0; + static U64 nextReportInterval = 0; + + packetIntervalNSec = NANOSECONDS_PER_SECOND / txRate; + clock_gettime(CLOCK_MONOTONIC, &now); + nextCycleNSec = TIMESPEC_TO_NSEC(now); + nextReportInterval = TIMESPEC_TO_NSEC(now) + (NANOSECONDS_PER_SECOND * reportSec); + + while (1) { + pBuf = (U8*)openavbRawsockGetTxFrame(rs, TRUE, &buflen); + if (!pBuf) { + printf("failed to get TX frame buffer\n"); + exit(4); + } + + if (buflen < txlen || txlen == -1) + txlen = buflen; + + openavbRawsockTxFillHdr(rs, pBuf, &hdrlen); + pData = pBuf + hdrlen; + datalen = txlen - hdrlen; + + if (mode == RAWSOCK_TX_MODE_FILL) { + int i; + for (i=0; i<datalen; i++) + pData[i] = (i & 0xff); + } + else { // RAWSOCK_TX_MODE_sEQ + static unsigned char seq = 0; + pData[0] = 0x7F; // Experimental subtype + pData[1] = 0x00; + pData[2] = seq++; + txlen = hdrlen + 3; + } + + openavbRawsockTxFrameReady(rs, pBuf, txlen); + + packetCnt++; + + if ((packetCnt % chunkSize) == 0) { + openavbRawsockSend(rs); + } + + nextCycleNSec += packetIntervalNSec; + clock_gettime(CLOCK_MONOTONIC, &now); + U64 nowNSec = TIMESPEC_TO_NSEC(now);; + + if (nowNSec > nextReportInterval) { + printf("TX Packets: %d\n", packetCnt); + packetCnt = 0; + nextReportInterval = nowNSec + (NANOSECONDS_PER_SECOND * reportSec); + } + + if (nowNSec < nextCycleNSec) { + usleep((nextCycleNSec - nowNSec) / 1000); + } + } + + openavbRawsockClose(rs); + return 0; +} diff --git a/lib/avtp_pipeline/platform/Linux/tl/openavb_tl_osal.c b/lib/avtp_pipeline/platform/Linux/tl/openavb_tl_osal.c new file mode 100644 index 00000000..2911ce98 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/tl/openavb_tl_osal.c @@ -0,0 +1,443 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#include <unistd.h> +#include <pthread.h> +#include <signal.h> +#include <dlfcn.h> +#include "ini.h" + +#include "openavb_platform.h" + +#include "openavb_osal.h" +#include "openavb_intf_pub.h" +#include "openavb_map_pub.h" +#include "openavb_trace.h" +#include "openavb_rawsock.h" +#include "openavb_mediaq.h" +#include "openavb_tl.h" + +#define AVB_LOG_COMPONENT "Talker / Listener" +#include "openavb_log.h" + +#define MATCH(A, B)(strcasecmp((A), (B)) == 0) +#define MATCH_LEFT(A, B, C)(strncasecmp((A), (B), (C)) == 0) + +typedef struct { + tl_state_t *pTLState; + openavb_tl_cfg_t *pCfg; + openavb_tl_cfg_name_value_t *pNVCfg; +} parse_ini_data_t; + +bool parse_mac(const char *str, cfg_mac_t *mac) +{ + memset(&mac->buffer, 0, sizeof(struct ether_addr)); + + mac->mac = ether_aton_r(str, &mac->buffer); + if (mac->mac) + return TRUE; + + AVB_LOGF_ERROR("Failed to parse addr: %s", str); + return FALSE; +} + +static bool openMapLib(tl_state_t *pTLState) +{ +// OpenAVB using static mapping plugins therefore don't attempt to open a library +#if 0 + // Opening library + if (pTLState->mapLib.libName) { + AVB_LOGF_INFO("Attempting to open library: %s", pTLState->mapLib.libName); + pTLState->mapLib.libHandle = dlopen(pTLState->mapLib.libName, RTLD_LAZY); + if (!pTLState->mapLib.libHandle) { + AVB_LOG_ERROR("Unable to open the mapping library."); + return FALSE; + } + } +#endif + + // Looking up function entry + if (!pTLState->mapLib.funcName) { + AVB_LOG_ERROR("Mapping initialize function not set."); + return FALSE; + } + + char *error; + AVB_LOGF_INFO("Looking up symbol for function: %s", pTLState->mapLib.funcName); + if (pTLState->mapLib.libHandle) { + pTLState->cfg.pMapInitFn = dlsym(pTLState->mapLib.libHandle, pTLState->mapLib.funcName); + } + else { + pTLState->cfg.pMapInitFn = dlsym(RTLD_DEFAULT, pTLState->mapLib.funcName); + } + if ((error = dlerror()) != NULL) { + AVB_LOGF_ERROR("Mapping initialize function lookup error: %s.", error); + return FALSE; + } + + return TRUE; +} + +static bool openIntfLib(tl_state_t *pTLState) +{ +// OpenAVB using static interface plugins therefore don't attempt to open a library +#if 0 + // Opening library + if (pTLState->intfLib.libName) { + AVB_LOGF_INFO("Attempting to open library: %s", pTLState->intfLib.libName); + pTLState->intfLib.libHandle = dlopen(pTLState->intfLib.libName, RTLD_LAZY); + if (!pTLState->intfLib.libHandle) { + AVB_LOG_ERROR("Unable to open the interface library."); + return FALSE; + } + } +#endif + + // Looking up function entry + if (!pTLState->intfLib.funcName) { + AVB_LOG_ERROR("Interface initialize function not set."); + return FALSE; + } + + char *error; + AVB_LOGF_INFO("Looking up symbol for function: %s", pTLState->intfLib.funcName); + if (pTLState->intfLib.libHandle) { + pTLState->cfg.pIntfInitFn = dlsym(pTLState->intfLib.libHandle, pTLState->intfLib.funcName); + } + else { + pTLState->cfg.pIntfInitFn = dlsym(RTLD_DEFAULT, pTLState->intfLib.funcName); + } + if ((error = dlerror()) != NULL) { + AVB_LOGF_ERROR("Interface initialize function lookup error: %s.", error); + return FALSE; + } + + return TRUE; +} + +// callback function - called for each name/value pair by ini parsing library +static int openavbTLCfgCallback(void *user, const char *tlSection, const char *name, const char *value) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + parse_ini_data_t *pParseIniData = (parse_ini_data_t *)user; + openavb_tl_cfg_t *pCfg = pParseIniData->pCfg; + openavb_tl_cfg_name_value_t *pNVCfg = pParseIniData->pNVCfg; + tl_state_t *pTLState = pParseIniData->pTLState; + + AVB_LOGF_DEBUG("name=[%s] value=[%s]", name, value); + + bool valOK = FALSE; + char *pEnd; + int i; + + if (MATCH(name, "role")) { + if (MATCH(value, "talker")) { + pCfg->role = AVB_ROLE_TALKER; + valOK = TRUE; + } + else if (MATCH(value, "listener")) { + pCfg->role = AVB_ROLE_LISTENER; + valOK = TRUE; + } + } + else if (MATCH(name, "dest_addr")) { + valOK = parse_mac(value, &pCfg->dest_addr); + } + else if (MATCH(name, "stream_addr")) { + valOK = parse_mac(value, &pCfg->stream_addr); + } + else if (MATCH(name, "stream_uid")) { + errno = 0; + pCfg->stream_uid = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0 + && pCfg->stream_uid <= UINT16_MAX) + valOK = TRUE; + } + else if (MATCH(name, "max_interval_frames")) { + errno = 0; + pCfg->max_interval_frames = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0 + && pCfg->max_interval_frames <= UINT16_MAX) + valOK = TRUE; + } + else if (MATCH(name, "max_frame_size")) { + errno = 0; + pCfg->max_frame_size = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0 + && pCfg->max_interval_frames <= UINT16_MAX) + valOK = TRUE; + } + else if (MATCH(name, "sr_class")) { + if (strlen(value) == 1) { + if (tolower(value[0]) == 'a') { + pCfg->sr_class = SR_CLASS_A; + valOK = TRUE; + } + else if (tolower(value[0]) == 'b') { + pCfg->sr_class = SR_CLASS_B; + valOK = TRUE; + } + } + } + else if (MATCH(name, "sr_rank")) { + if (strlen(value) == 1) { + if (value[0] == '1') { + pCfg->sr_rank = SR_RANK_REGULAR; + valOK = TRUE; + } + else if (value[0] == '0') { + pCfg->sr_rank = SR_RANK_EMERGENCY; + valOK = TRUE; + } + } + } + else if (MATCH(name, "max_transit_usec")) { + errno = 0; + pCfg->max_transit_usec = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0 + && pCfg->max_transit_usec <= UINT32_MAX) + valOK = TRUE; + } + else if (MATCH(name, "max_transmit_deficit_usec")) { + errno = 0; + pCfg->max_transmit_deficit_usec = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0 + && pCfg->max_transmit_deficit_usec <= UINT32_MAX) + valOK = TRUE; + } + else if (MATCH(name, "internal_latency")) { + errno = 0; + pCfg->internal_latency = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0 + && pCfg->internal_latency <= UINT32_MAX) + valOK = TRUE; + } + else if (MATCH(name, "batch_factor")) { + errno = 0; + pCfg->batch_factor = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0 + && pCfg->batch_factor > 0 + && pCfg->batch_factor <= INT32_MAX) + valOK = TRUE; + } + else if (MATCH(name, "max_stale")) { + errno = 0; + pCfg->max_stale = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0 + && pCfg->max_stale > 0 + && pCfg->max_stale <= INT32_MAX) + valOK = TRUE; + } + else if (MATCH(name, "raw_tx_buffers")) { + errno = 0; + pCfg->raw_tx_buffers = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0 + && pCfg->raw_tx_buffers <= UINT32_MAX) + valOK = TRUE; + } + else if (MATCH(name, "raw_rx_buffers")) { + errno = 0; + pCfg->raw_rx_buffers = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0 + && pCfg->raw_rx_buffers <= UINT32_MAX) + valOK = TRUE; + } + else if (MATCH(name, "report_seconds")) { + errno = 0; + pCfg->report_seconds = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0 + && (int)pCfg->report_seconds >= 0 + && pCfg->report_seconds <= INT32_MAX) + valOK = TRUE; + } + else if (MATCH(name, "start_paused")) { + // ignore this item - tl_host doesn't use it because + // it pauses before reading any of its streams. + errno = 0; + long tmp; + tmp = strtol(value, &pEnd, 10); + if (*pEnd == '\0' && errno == 0 + && tmp >= 0 + && tmp <= 1) { + pCfg->start_paused = (tmp == 1); + valOK = TRUE; + } + } + else if (MATCH(name, "ifname")) { + if_info_t ifinfo; + if (openavbCheckInterface(value, &ifinfo)) { + strncpy(pCfg->ifname, value, IFNAMSIZ - 1); + valOK = TRUE; + } + } + + else if (MATCH(name, "map_lib")) { + if (pTLState->mapLib.libName) + free(pTLState->mapLib.libName); + pTLState->mapLib.libName = strdup(value); + valOK = TRUE; + } + else if (MATCH(name, "map_fn")) { + if (pTLState->mapLib.funcName) + free(pTLState->mapLib.funcName); + pTLState->mapLib.funcName = strdup(value); + valOK = TRUE; + } + + else if (MATCH(name, "intf_lib")) { + if (pTLState->intfLib.libName) + free(pTLState->intfLib.libName); + pTLState->intfLib.libName = strdup(value); + valOK = TRUE; + } + else if (MATCH(name, "intf_fn")) { + if (pTLState->intfLib.funcName) + free(pTLState->intfLib.funcName); + pTLState->intfLib.funcName = strdup(value); + valOK = TRUE; + } + + else if (MATCH_LEFT(name, "intf_nv_", 8) + || MATCH_LEFT(name, "map_nv_", 7)) { + // Need to save the interface and mapping module configuration + // until later (after those libraries are loaded.) + + // check if this setting replaces an earlier one + for (i = 0; i < pNVCfg->nLibCfgItems; i++) { + if (MATCH(name, pNVCfg->libCfgNames[i])) { + if (pNVCfg->libCfgValues[i]) + free(pNVCfg->libCfgValues[i]); + pNVCfg->libCfgValues[i] = strdup(value); + valOK = TRUE; + } + } + if (i >= pNVCfg->nLibCfgItems) { + // is a new name/value + if (i >= MAX_LIB_CFG_ITEMS) { + AVB_LOG_ERROR("Too many INI settings for interface/mapping modules"); + } + else { + pNVCfg->libCfgNames[i] = strdup(name); + pNVCfg->libCfgValues[i] = strdup(value); + pNVCfg->nLibCfgItems++; + valOK = TRUE; + } + } + } + else { + // unmatched item, fail + AVB_LOGF_ERROR("Unrecognized configuration item: name=%s", name); + return 0; + } + + if (!valOK) { + // bad value + AVB_LOGF_ERROR("Invalid value: name=%s, value=%s", name, value); + return 0; + } + + AVB_TRACE_EXIT(AVB_TRACE_TL); + + return 1; // OK +} + +bool openavbTLThreadFnOsal(tl_state_t *pTLState) +{ + return TRUE; +} + + + + + +EXTERN_DLL_EXPORT bool openavbTLReadIniFileOsal(tl_handle_t TLhandle, const char *fileName, openavb_tl_cfg_t *pCfg, openavb_tl_cfg_name_value_t *pNVCfg) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + parse_ini_data_t parseIniData; + parseIniData.pTLState = (tl_state_t *)TLhandle; + parseIniData.pCfg = pCfg; + parseIniData.pNVCfg = pNVCfg; + + int result = ini_parse(fileName, openavbTLCfgCallback, &parseIniData); + if (result < 0) { + AVB_LOGF_ERROR("Couldn't parse INI file: %s", fileName); + return FALSE; + } + if (result > 0) { + AVB_LOGF_ERROR("Error in INI file: %s, line %d", fileName, result); + return FALSE; + } + + AVB_TRACE_EXIT(AVB_TRACE_TL); + return TRUE; +} + + +bool openavbTLOpenLinkLibsOsal(tl_state_t *pTLState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState->cfg.pMapInitFn) { + if (!openMapLib(pTLState)) { + return FALSE; + } + } + + if (!pTLState->cfg.pIntfInitFn) { + if (!openIntfLib(pTLState)) { + return FALSE; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_TL); + return TRUE; +} + +bool openavbTLCloseLinkLibsOsal(tl_state_t *pTLState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (pTLState->mapLib.libHandle) + dlclose(pTLState->mapLib.libHandle); + if (pTLState->intfLib.libHandle) + dlclose(pTLState->intfLib.libHandle); + + AVB_TRACE_EXIT(AVB_TRACE_TL); + return TRUE; +} + + diff --git a/lib/avtp_pipeline/platform/Linux/x86_i210_linux.cmake b/lib/avtp_pipeline/platform/Linux/x86_i210_linux.cmake new file mode 100644 index 00000000..a8d891e1 --- /dev/null +++ b/lib/avtp_pipeline/platform/Linux/x86_i210_linux.cmake @@ -0,0 +1,30 @@ +# and another kernel sources +#set ( LINUX_KERNEL_DIR "/usr/src/kernel" ) + +# build configuration +set ( OPENAVB_HAL "x86_i210" ) +set ( OPENAVB_OSAL "Linux" ) +set ( OPENAVB_TCAL "GNU" ) +set ( OPENAVB_PLATFORM "${OPENAVB_HAL}-${OPENAVB_OSAL}" ) + +# Platform Additions +set ( PLATFORM_INCLUDE_DIRECTORIES + ${CMAKE_SOURCE_DIR}/platform/x86_i210/include + ${CMAKE_SOURCE_DIR}/../igb + ${CMAKE_SOURCE_DIR}/openavb_common + ${CMAKE_SOURCE_DIR}/../../daemons/common + ${CMAKE_SOURCE_DIR}/../../daemons/mrpd +) + +set ( PLATFORM_LINK_DIRECTORIES + ${CMAKE_SOURCE_DIR}/../igb +) + +set ( PLATFORM_LINK_LIBRARIES + igb +) + + +# TODO_OPENAVB : need this? +# Set platform specific define +#set ( PLATFORM_DEFINE "AVB_DELAY_TWEAK_USEC=15" ) diff --git a/lib/avtp_pipeline/platform/generic/include/linux/ptp_clock.h b/lib/avtp_pipeline/platform/generic/include/linux/ptp_clock.h new file mode 100644 index 00000000..b829d8dd --- /dev/null +++ b/lib/avtp_pipeline/platform/generic/include/linux/ptp_clock.h @@ -0,0 +1,29 @@ +#ifndef _OPENAVB_GENERIC_LINUX_PTP_CLOCK_H +#define _OPENAVB_GENERIC_LINUX_PTP_CLOCK_H + +#include "/usr/include/linux/ptp_clock.h" + +#ifndef ptpas_offset_request + +#warning "Adding missing PTP declarations, it is just to make everything compile, AVB stack will not work properly." + +struct ptpas_offset_request { + unsigned long long syncReceiptTime; + unsigned long long syncReceiptLocalTime; + unsigned int gmRateRatioPPB; // gmRateRatio in ppb + unsigned int gmSeqNumber; // an indication of the combination of clockID and clockIndex +}; + +struct ptpas_get_wallclock { + long tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ + unsigned int gmRateRatioPPB; // gmRateRatio in ppm + unsigned int gmSeqNumber; // an indication of the combination of clockID and clockIndex +}; + +#define PTPAS_SET_OFFSET _IOW(PTP_CLK_MAGIC, 6, struct ptpas_offset_request) +#define PTPAS_GET_WALLCLOCK _IOR(PTP_CLK_MAGIC, 7, struct ptpas_get_wallclock) + +#endif + +#endif // _OPENAVB_GENERIC_LINUX_PTP_CLOCK_H diff --git a/lib/avtp_pipeline/platform/generic/openavb_hal.h b/lib/avtp_pipeline/platform/generic/openavb_hal.h new file mode 100644 index 00000000..56fdd691 --- /dev/null +++ b/lib/avtp_pipeline/platform/generic/openavb_hal.h @@ -0,0 +1,37 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#ifndef _OPENAVB_HAL_H +#define _OPENAVB_HAL_H + +// halPushMCR() API not defined +#define HAL_PUSH_MCR(mcrTimeStampPtr) FALSE + +#endif // _OPENAVB_HAL_H diff --git a/lib/avtp_pipeline/platform/platHAL/readme.txt b/lib/avtp_pipeline/platform/platHAL/readme.txt new file mode 100644 index 00000000..52b2d136 --- /dev/null +++ b/lib/avtp_pipeline/platform/platHAL/readme.txt @@ -0,0 +1 @@ +Consider migration of HAL implements into this folder.
\ No newline at end of file diff --git a/lib/avtp_pipeline/platform/platOSAL/readme.txt b/lib/avtp_pipeline/platform/platOSAL/readme.txt new file mode 100644 index 00000000..af269fcb --- /dev/null +++ b/lib/avtp_pipeline/platform/platOSAL/readme.txt @@ -0,0 +1 @@ +Consider migration of OSAL implements into this folder.
\ No newline at end of file diff --git a/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_mem_tcal.c b/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_mem_tcal.c new file mode 100644 index 00000000..637c7fbe --- /dev/null +++ b/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_mem_tcal.c @@ -0,0 +1,39 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#include <malloc.h>
+
+void tcalGetHeapInfo(unsigned int *ttlMallocHeap, unsigned int *freeMallocHeap)
+{
+ struct mallinfo minfo = mallinfo();
+
+ *ttlMallocHeap = (minfo.arena + minfo.fordblks);
+ *freeMallocHeap = minfo.fordblks;
+}
diff --git a/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_mem_tcal.h b/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_mem_tcal.h new file mode 100644 index 00000000..46de34ab --- /dev/null +++ b/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_mem_tcal.h @@ -0,0 +1,37 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#ifndef OPENAVB_MEM_TCAL_H
+#define OPENAVB_MEM_TCAL_H 1
+
+void tcalGetHeapInfo(unsigned int *ttlMallocHeap, unsigned int *freeMallocHeap);
+
+#endif // OPENAVB_MEM_TCAL_H
+
diff --git a/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_mem_tcal_pub.h b/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_mem_tcal_pub.h new file mode 100644 index 00000000..d6428845 --- /dev/null +++ b/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_mem_tcal_pub.h @@ -0,0 +1,38 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#ifndef OPENAVB_MEM_TCAL_PUB_H
+#define OPENAVB_MEM_TCAL_PUB_H 1
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#endif // OPENAVB_MEM_TCAL_PUB_H
+
diff --git a/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_tcal_pub.h b/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_tcal_pub.h new file mode 100644 index 00000000..dc107386 --- /dev/null +++ b/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_tcal_pub.h @@ -0,0 +1,38 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#ifndef OPENAVB_TCAL_PUB_H
+#define OPENAVB_TCAL_PUB_H 1
+
+// Logging Extra Newline. Some platforms libraries require an extra newline
+static const bool OPENAVB_TCAL_LOG_EXTRA_NEWLINE = TRUE;
+
+#endif // OPENAVB_TCAL_PUB_H
+
diff --git a/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_time_tcal_pub.h b/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_time_tcal_pub.h new file mode 100644 index 00000000..2ef4c096 --- /dev/null +++ b/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_time_tcal_pub.h @@ -0,0 +1,49 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#ifndef _OPENAVB_TIME_TCAL_PUB_H
+#define _OPENAVB_TIME_TCAL_PUB_H
+
+#if PROVIDED_BY_PLATFORM
+typedef int clockid_t;
+#endif
+
+#if PROVIDED_BY_PLATFORM
+struct timespec {
+ long tv_sec; /* seconds */
+ long tv_nsec; /* nanoseconds */
+};
+struct itimerspec {
+ struct timespec it_interval; /* timer period */
+ struct timespec it_value; /* timer expiration */
+};
+#endif
+
+#endif // _OPENAVB_TIME_TCAL_PUB_H
diff --git a/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_types_base_tcal_pub.h b/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_types_base_tcal_pub.h new file mode 100644 index 00000000..8cd00ecb --- /dev/null +++ b/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_types_base_tcal_pub.h @@ -0,0 +1,40 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#ifndef AVB_TYPES_BASE_TCAL_PUB_H
+#define AVB_TYPES_BASE_TCAL_PUB_H 1
+
+#define OPENAVB_PRAGMA(arg) _Pragma(#arg)
+
+#define OPENAVB_CODE_FUNCTION_PRI
+#define OPENAVB_CODE_MODULE_PRI
+#define OPENAVB_DATA_PRI
+
+#endif // AVB_TYPES_BASE_TCAL_PUB_H
diff --git a/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_warnings_tcal.h b/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_warnings_tcal.h new file mode 100644 index 00000000..058ae0e4 --- /dev/null +++ b/lib/avtp_pipeline/platform/platTCAL/GNU/openavb_warnings_tcal.h @@ -0,0 +1,37 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#ifndef OPENAVB_WARNINGS_TCAL_H
+#define OPENAVB_WARNINGS_TCAL_H 1
+
+#define OPENAVB_SUPPRESS_WARNING_UNREACHABLE_CODE()
+
+#endif // OPENAVB_WARNINGS_TCAL_H
+
diff --git a/lib/avtp_pipeline/platform/platTCAL/GNU/rawsock/openavb_rawsock_tcal.h b/lib/avtp_pipeline/platform/platTCAL/GNU/rawsock/openavb_rawsock_tcal.h new file mode 100644 index 00000000..9f1eebfe --- /dev/null +++ b/lib/avtp_pipeline/platform/platTCAL/GNU/rawsock/openavb_rawsock_tcal.h @@ -0,0 +1,59 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#ifndef OPENAVB_RAWSOCK_TCAL_H
+#define OPENAVB_RAWSOCK_TCAL_H 1
+
+
+// Ethernet header
+typedef struct {
+ U8 dhost[ETH_ALEN];
+ U8 shost[ETH_ALEN];
+ uint16_t ethertype;
+}
+__attribute__ ((__packed__)) eth_hdr_t;
+
+// VLAN tag
+typedef struct {
+ uint16_t tpip;
+ uint16_t bits;
+}
+__attribute__ ((__packed__)) vlan_tag_t;
+
+// Ethernet header w/VLAN tag
+typedef struct {
+ U8 dhost[ETH_ALEN];
+ U8 shost[ETH_ALEN];
+ vlan_tag_t vlan;
+ uint16_t ethertype;
+}
+__attribute__ ((__packed__)) eth_vlan_hdr_t;
+
+#endif // OPENAVB_RAWSOCK_TCAL_H
diff --git a/lib/avtp_pipeline/platform/x86_i210/mcr/openavb_mcr_hal.c b/lib/avtp_pipeline/platform/x86_i210/mcr/openavb_mcr_hal.c new file mode 100644 index 00000000..f0b84319 --- /dev/null +++ b/lib/avtp_pipeline/platform/x86_i210/mcr/openavb_mcr_hal.c @@ -0,0 +1,62 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#define AVB_LOG_COMPONENT "MCR"
+#include "openavb_pub.h"
+#include "openavb_log.h"
+
+#include "openavb_mcr_hal.h"
+
+
+bool halInitMCR(U32 packetRate, U32 pushInterval, U32 timestampInterval, U32 recoveryInterval)
+{
+ return TRUE;
+}
+
+bool halCloseMCR(void)
+{
+ return TRUE;
+}
+
+bool halPushMCR(void)
+{
+ return TRUE;
+}
+
+void halAdjustMCRNSec(S32 adjNSec)
+{
+}
+
+void halAdjustMCRGranularityNSec(U32 adjGranularityNSec)
+{
+}
+
+
+
diff --git a/lib/avtp_pipeline/platform/x86_i210/mcr/openavb_mcr_hal.h b/lib/avtp_pipeline/platform/x86_i210/mcr/openavb_mcr_hal.h new file mode 100644 index 00000000..8e377c0a --- /dev/null +++ b/lib/avtp_pipeline/platform/x86_i210/mcr/openavb_mcr_hal.h @@ -0,0 +1,37 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#ifndef OPENAVB_MCR_HAL_H
+#define OPENAVB_MCR_HAL_H
+
+#include "openavb_platform.h"
+#include "openavb_mcr_hal_pub.h"
+
+#endif // OPENAVB_MCR_HAL_H
diff --git a/lib/avtp_pipeline/platform/x86_i210/openavb_ether_hal.c b/lib/avtp_pipeline/platform/x86_i210/openavb_ether_hal.c new file mode 100644 index 00000000..6d531e60 --- /dev/null +++ b/lib/avtp_pipeline/platform/x86_i210/openavb_ether_hal.c @@ -0,0 +1,206 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#include <pci/pci.h>
+#include "igb.h"
+#include "avb.h"
+
+
+#include "openavb_ether_hal.h"
+#include "openavb_osal.h"
+
+#define AVB_LOG_COMPONENT "HAL Ethernet"
+#include "openavb_pub.h"
+#include "openavb_log.h"
+#include "openavb_trace.h"
+
+#define MAX_MTU 1536
+
+static pthread_mutex_t gHALTimeInitMutex = PTHREAD_MUTEX_INITIALIZER;
+#define LOCK() pthread_mutex_lock(&gHALTimeInitMutex)
+#define UNLOCK() pthread_mutex_unlock(&gHALTimeInitMutex)
+
+static bool bInitialized = FALSE;
+static device_t gIgbDev;
+
+static bool x_HalEtherInit(void)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+ int rslt;
+
+ rslt = pci_connect(&gIgbDev);
+ if (rslt) {
+ AVB_LOGF_ERROR("connect failed (%s) - are you running as root?", strerror(errno));
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return FALSE;
+ }
+
+ rslt = igb_init(&gIgbDev);
+ if (rslt) {
+ AVB_LOGF_ERROR("init failed (%s) - is the driver really loaded?\n", strerror(errno));
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return FALSE;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return TRUE;
+}
+
+
+// Initialize HAL layer Ethernet driver
+bool halEthernetInitialize(U8 *macAddr, bool gmacAutoNegotiate)
+{
+ LOCK();
+ if (!bInitialized) {
+ if (x_HalEtherInit())
+ bInitialized = TRUE;
+ }
+ UNLOCK();
+
+ return bInitialized;
+}
+
+bool halEthernetFinalize(void)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+
+ // TODO_OPENAVB : shutdown igb
+
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return TRUE;
+}
+
+bool halEtherAddMulticast(U8 *multicastAddr, bool add)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return TRUE;
+}
+
+U8 *halGetRxBufAVTP(U32 *frameSize)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return NULL;
+}
+
+// Get next Rx packet. NOTE: Expected to be called from a single task (socket task)
+U8 *halGetRxBufAVB(U32 *frameSize, bool *bPtpCBUsed)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return NULL;
+}
+
+U8 *halGetRxBufGEN(U32 *frameSize)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return NULL;
+}
+
+// Send Tx Packet to driver
+bool halSendTxBuffer(U8 *pDataBuf, U32 frameSize, int class)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return FALSE;
+}
+
+// Is the link up. Returns TRUE if it is.
+bool halIsLinkUp(void)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return FALSE;
+}
+
+// Returns the MTU
+U32 halGetMTU(void)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return MAX_MTU;
+}
+
+U8 *halGetMacAddr(void)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return NULL;
+}
+
+bool halGetLocaltime(U64 *localTime64)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+ if (igb_get_wallclock(&gIgbDev, localTime64, NULL ) > 0) {
+ AVB_LOG_ERROR("Failed to get wallclock time");
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return FALSE;
+ }
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return TRUE;
+}
+
+void halTrafficShaperAddStream(int class, uint32_t streamsBytesPerSec)
+{
+
+// TODO_OPENAVB : it appears the igb_set_class_bandwidth() helper function is designed about the concept of a single Class A and single Class B
+// stream. Perhaps it could be used in the short term, however, a final solution may be to talk to the igb driver directly supply the E1000 reg
+// values directly.
+//
+// igb_set_class_bandwidth(dev)
+
+// TODO_OPENAVB
+// if (class == AVB_CLASS_B) {
+// }
+// else if (class == AVB_CLASS_A) {
+// }
+}
+
+void halTrafficShaperRemoveStream(int class, uint32_t streamsBytesPerSec)
+{
+
+// TODO_OPENAVB : it appears the igb_set_class_bandwidth() helper function is designed about the concept of a single Class A and single Class B
+// stream. Perhaps it could be used in the short term, however, a final solution may be to talk to the igb driver directly supply the E1000 reg
+// values directly.
+//
+// igb_set_class_bandwidth(dev)
+
+// TODO_OPENAVB
+// if (class == AVB_CLASS_B) {
+// }
+// else if (class == AVB_CLASS_A) {
+// }
+}
+
+
+
+
diff --git a/lib/avtp_pipeline/platform/x86_i210/openavb_ether_hal.h b/lib/avtp_pipeline/platform/x86_i210/openavb_ether_hal.h new file mode 100644 index 00000000..99e562b3 --- /dev/null +++ b/lib/avtp_pipeline/platform/x86_i210/openavb_ether_hal.h @@ -0,0 +1,75 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#ifndef HAL_ETHER_H
+#define HAL_ETHER_H 1
+
+#include "openavb_platform.h"
+#include "openavb_types_base.h"
+
+// Initialize HAL layer Ethernet driver
+bool halEthernetInitialize(U8 *macAddr, bool gmacAutoNegotiate);
+
+// Finalize HAL layer Ethernet driver
+bool halEthernetFinalize(void);
+
+// Setup for multicast filtering
+bool halEtherAddMulticast(U8 *multicastAddr, bool add);
+
+// Get next AVTP Rx packet
+U8 *halGetRxBufAVTP(U32 *frameSize);
+
+// Get next AVB Rx packet. pPtpCBUsed will be set to true when rx data was send to the RX cb rather than returning the buffer.
+U8 *halGetRxBufAVB(U32 *frameSize, bool *bPtpCBUsed);
+
+// Get next GEN Rx packet
+U8 *halGetRxBufGEN(U32 *frameSize);
+
+// Send Tx Packet to driver
+bool halSendTxBuffer(U8 *pDataBuf, U32 frameSize, int class);
+
+// Is the link up. Returns TRUE if it is.
+bool halIsLinkUp(void);
+
+// Returns pointer to mac address
+U8 *halGetMacAddr(void);
+
+// Return the MTU
+U32 halGetMTU(void);
+
+bool halGetLocaltime(U64 *localTime64);
+
+// Add stream to the credit based shaper
+void halTrafficShaperAddStream(int class, uint32_t streamsBytesPerSec);
+
+// Remove stream from the credit based shaper
+void halTrafficShaperRemoveStream(int class, uint32_t streamsBytesPerSec);
+
+#endif // HAL_ETHER_H
diff --git a/lib/avtp_pipeline/platform/x86_i210/openavb_hal.h b/lib/avtp_pipeline/platform/x86_i210/openavb_hal.h new file mode 100644 index 00000000..4551d453 --- /dev/null +++ b/lib/avtp_pipeline/platform/x86_i210/openavb_hal.h @@ -0,0 +1,38 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#ifndef _OPENAVB_HAL_H
+#define _OPENAVB_HAL_H
+
+// Note this remains for backwards compatabilty with older prots. See openavb_mcr_hall_pub.h for newer APIs
+// halPushMCR() API not defined
+#define HAL_PUSH_MCR(mcrTimeStampPtr) FALSE
+
+#endif // _OPENAVB_HAL_H
diff --git a/lib/avtp_pipeline/platform/x86_i210/openavb_time_hal.c b/lib/avtp_pipeline/platform/x86_i210/openavb_time_hal.c new file mode 100644 index 00000000..d0a44235 --- /dev/null +++ b/lib/avtp_pipeline/platform/x86_i210/openavb_time_hal.c @@ -0,0 +1,61 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#include "openavb_platform.h"
+#include "openavb_ether_hal.h"
+#include "openavb_time_hal.h"
+#include "openavb_trace.h"
+
+#define AVB_LOG_COMPONENT "halTime"
+#include "openavb_pub.h"
+#include "openavb_log.h"
+
+bool halTimeInitialize(void)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TIME);
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return TRUE;
+}
+
+bool halTimeFinalize(void)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TIME);
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return TRUE;
+}
+
+bool halTimeGetLocaltime(U64 *localTime64)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TIME);
+ bool bRslt = halGetLocaltime(localTime64);
+ AVB_TRACE_EXIT(AVB_TRACE_TIME);
+ return bRslt;
+}
+
diff --git a/lib/avtp_pipeline/platform/x86_i210/openavb_time_hal.h b/lib/avtp_pipeline/platform/x86_i210/openavb_time_hal.h new file mode 100644 index 00000000..172c192a --- /dev/null +++ b/lib/avtp_pipeline/platform/x86_i210/openavb_time_hal.h @@ -0,0 +1,38 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#ifndef _OPENAVB_TIME_HAL_H +#define _OPENAVB_TIME_HAL_H +
+bool halTimeInitialize(void);
+bool halTimeFinalize(void);
+bool halTimeGetLocaltime(U64 *localTime64);
+ +#endif // _OPENAVB_TIME_HAL_H diff --git a/lib/avtp_pipeline/qmgr/CMakeLists.txt b/lib/avtp_pipeline/qmgr/CMakeLists.txt new file mode 100644 index 00000000..ab919273 --- /dev/null +++ b/lib/avtp_pipeline/qmgr/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/qmgr/openavb_qmgr.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/qmgr/openavb_qmgr.c b/lib/avtp_pipeline/qmgr/openavb_qmgr.c new file mode 100644 index 00000000..a9617712 --- /dev/null +++ b/lib/avtp_pipeline/qmgr/openavb_qmgr.c @@ -0,0 +1,87 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* + * MODULE : AVB Queue Manager + */ + +#include <openavb_types.h> +#include "openavb_ether_hal.h" +#define AVB_LOG_COMPONENT "QMGR" +#include "openavb_log.h" +#include "openavb_trace.h" + +#include "openavb_qmgr.h" +#include "avb_sched.h" + +static int fqtss_mode; + +bool openavbQmgrInitialize(int mode, int ifindex, char* ifname, unsigned mtu, unsigned link_kbit, unsigned nsr_kbit) +{ + fqtss_mode = mode; + return TRUE; +} + +void openavbQmgrFinalize(void) +{ +} + +U16 openavbQmgrAddStream(SRClassIdx_t nClass, unsigned classRate, unsigned maxIntervalFrames, unsigned maxFrameSize) +{ + AVB_TRACE_ENTRY(AVB_TRACE_QUEUE_MANAGER); + + unsigned long streamBytesPerSec = (maxFrameSize + OPENAVB_AVTP_ETHER_FRAME_OVERHEAD) * maxIntervalFrames * classRate; + int fwmarkClass = AVB_CLASS_NR; + if (nClass == SR_CLASS_A) + fwmarkClass = AVB_CLASS_A; + else + fwmarkClass = AVB_CLASS_B; + int fwmark = TC_AVB_MARK(streamBytesPerSec, fwmarkClass); + + AVB_LOGF_DEBUG("Adding stream; class=%d, rate=%u frames=%u, size=%u, bytes/sec=%lu", nClass, classRate, maxIntervalFrames, maxFrameSize, streamBytesPerSec); + + if (fqtss_mode == FQTSS_MODE_HW_CLASS) { + halTrafficShaperAddStream(TC_AVB_MARK_CLASS(fwmark), streamBytesPerSec); + } + + AVB_TRACE_EXIT(AVB_TRACE_QUEUE_MANAGER); + return fwmark; +} + +void openavbQmgrRemoveStream(U16 fwmark, unsigned classRate, unsigned maxIntervalFrames, unsigned maxFrameSize) +{ + AVB_TRACE_ENTRY(AVB_TRACE_QUEUE_MANAGER); + + if (fqtss_mode == FQTSS_MODE_HW_CLASS) { + halTrafficShaperRemoveStream(TC_AVB_MARK_CLASS(fwmark), TC_AVB_MARK_STREAM(fwmark)); + } + + AVB_TRACE_EXIT(AVB_TRACE_QUEUE_MANAGER); +} diff --git a/lib/avtp_pipeline/qmgr/openavb_qmgr.h b/lib/avtp_pipeline/qmgr/openavb_qmgr.h new file mode 100644 index 00000000..700e6f14 --- /dev/null +++ b/lib/avtp_pipeline/qmgr/openavb_qmgr.h @@ -0,0 +1,104 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* + * MODULE SUMMARY : The AVB Queue Manager sets up Linux network packet + * scheduling, so that transmitted Ethernet frames for an AVB stream + * are sent according to their SRP reservation. + */ + +#ifndef AVB_QMGR_H +#define AVB_QMGR_H 1 + +#include "openavb_types.h" + +#define INVALID_FWMARK (U16)(-1) +#define DEFAULT_FWMARK (U16)(1) + +#define FQTSS_MODE_DISABLED 0 +#define FQTSS_MODE_DROP_ALL 1 +#define FQTSS_MODE_ALL_NONSR 2 +#define FQTSS_MODE_DROP_SR 3 +#define FQTSS_MODE_SW 4 +#define FQTSS_MODE_HW_CLASS 5 + +#ifdef AVB_FEATURE_FQTSS + +bool openavbQmgrInitialize(int mode, + int ifindex, + char* ifname, + unsigned mtu, + unsigned link_kbit, + unsigned nsr_kbit); + +void openavbQmgrFinalize(); + +U16 openavbQmgrAddStream(SRClassIdx_t nClass, + unsigned classRate, + unsigned maxIntervalFrames, + unsigned maxFrameSize); + +void openavbQmgrRemoveStream(U16 fwmark, + unsigned classRate, + unsigned maxIntervalFrames, + unsigned maxFrameSize); + + +//void openavbQmgrRemoveStream(U16 fwmark); + +#else +/* Dummy versions to use if FQTSS is compiled out + */ +inline bool openavbQmgrInitialize(int mode, + int ifindex, + char* ifname, + unsigned mtu, + unsigned link_kbit, + unsigned nsr_kbit) +{ return TRUE; } + +inline void openavbQmgrFinalize() +{} + +inline U16 openavbQmgrAddStream(SRClassIdx_t nClass, + unsigned classRate, + unsigned maxIntervalFrames, + unsigned maxFrameSize) +{ return DEFAULT_FWMARK; } + +inline void openavbQmgrRemoveStream(U16 fwmark, + unsigned classRate, + unsigned maxIntervalFrames, + unsigned maxFrameSize); + +{} +#endif // AVB_FEATURE_FQTSS + +#endif // AVB_QMGR_H diff --git a/lib/avtp_pipeline/rawsock/CMakeLists.txt b/lib/avtp_pipeline/rawsock/CMakeLists.txt new file mode 100644 index 00000000..3e3b4110 --- /dev/null +++ b/lib/avtp_pipeline/rawsock/CMakeLists.txt @@ -0,0 +1 @@ +# No common code diff --git a/lib/avtp_pipeline/rawsock/openavb_rawsock.h b/lib/avtp_pipeline/rawsock/openavb_rawsock.h new file mode 100644 index 00000000..858aced0 --- /dev/null +++ b/lib/avtp_pipeline/rawsock/openavb_rawsock.h @@ -0,0 +1,164 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* + * HEADER SUMMARY : API for raw socket library. + */ + +#ifndef RAWSOCK_H +#define RAWSOCK_H 1 + +#include "openavb_types.h" +#include "openavb_osal.h" + +// Structure to hold information about a network interface +typedef struct { + char name[IFNAMSIZ]; + struct ether_addr mac; + int index; + int mtu; +} if_info_t; + +// Get information about an interface +// ifname - interface name +// info - structure to be filled with info about iface +bool openavbCheckInterface(const char *ifname, if_info_t *info); + +// Structure to hold fields from Ethernet header +// Used to set information to be added to TX frames, +// or to return info parsed from RX frames. +typedef struct { + U8 *shost; // Source MAC address + U8 *dhost; // Destination MAC address + U16 ethertype; // Ethernet type (protocol) + bool vlan; // Include VLAN header? + U8 vlan_pcp; // VLAN Priority Code Point + U16 vlan_vid; // VLAN ID +} hdr_info_t; + + +// Open a raw socket, and setup circular buffer for sending or receiving frames. +// +// Returns rawsock handle which must be passed back to other rawsock library functions. +// +void *openavbRawsockOpen(const char *ifname, // network interface name to bind to + bool rx_mode, // TX mode flag + bool tx_mode, // RX mode flag + U16 ethertype, // Ethernet type (protocol) + U32 frame_size, // maximum size of frame to send/receive + U32 num_frames); // number of frames in the circular buffer + +// Set signal on RX mode +void openavbSetRxSignalMode(void *rawsock, bool rxSignalMode); + +// Close the raw socket and release associated resources. +void openavbRawsockClose(void *rawsock); + +// Get the socket used for this rawsock. +// Client can use that socket for poll/select calls. +int openavbRawsockGetSocket(void *rawsock); + +// Get the Ethernet address of the interface. +// pBuf should point to "char buf[ETH_ALEN]" +bool openavbRawsockGetAddr(void *rawsock, U8 addr[ETH_ALEN]); + +// Flags that can be passed to GetRxFrame instead of actual timeout values. +#define OPENAVB_RAWSOCK_NONBLOCK (0) +#define OPENAVB_RAWSOCK_BLOCK (-1) + +// RX FUNCTIONS +// +// Get a received frame. +// Returns pointer to received frame, or NULL +U8 *openavbRawsockGetRxFrame(void *rawsock, // rawsock handle + U32 usecTimeout, // timeout (microseconds) + // or use OPENAVB_RAWSOCK_BLOCK/NONBLOCK + U32 *offset, // offset of frame in the frame buffer + U32 *len); // returns length of received frame + +// Parse the frame header. Returns length of header, or -1 for failure +int openavbRawsockRxParseHdr(void* rawsock, U8 *pBuffer, hdr_info_t *pInfo); + +// Release the received frame for re-use. +bool openavbRawsockRelRxFrame(void *rawsock, U8 *pFrame); + +// Add (or drop) membership in link-layer multicast group +bool openavbRawsockRxMulticast(void *rawsock, bool add_membership, const U8 buf[ETH_ALEN]); + +// Allows for filtering of AVTP subtypes at the rawsock level for rawsock implementations that aren't able to +// delivery the same packet to multiple sockets. +bool openavbRawsockRxAVTPSubtype(void *rawsock, U8 subtype); + +// TX FUNCTIONS +// +// Setup the header that we'll use on TX Ethernet frames. +// Called once during intialization. +bool openavbRawsockTxSetHdr(void *rawsock, hdr_info_t *pInfo); + +// Copy the pre-set Ethernet header into the frame +bool openavbRawsockTxFillHdr(void *rawsock, + U8 *pBuffer, + U32 *hdrlen); + +// Set the SO_MARK option on the socket +// (used to identify packets for FQTSS in kernel) +bool openavbRawsockTxSetMark(void *rawsock, int prio); + +// Get a buffer to hold a frame for transmission. +// Returns pointer to frame (or NULL). +U8 *openavbRawsockGetTxFrame(void *rawsock, // rawsock handle + bool blocking, // TRUE blocks until frame buffer is available. + U32 *size); // size of the frame buffer + +// Release Tx buffer without sending it +bool openavbRawsockRelTxFrame(void *rawsock, U8 *pBuffer); + +// Submit a frame and mark it "ready to send" +bool openavbRawsockTxFrameReady(void *rawsock, // rawsock handle + U8 *pFrame, // pointer to frame buffer + U32 len); // length of frame to send + +// Send all packets that are marked "ready to send". +// Returns count of bytes in sent frames - or < 0 for error. +int openavbRawsockSend(void *rawsock); + +// Check Tx buffer level in sockets +int openavbRawsockTxBufLevel(void *rawsock); + +// Check Rx buffer level in sockets +int openavbRawsockRxBufLevel(void *rawsock); + +// returns number of TX out of buffer events noticed during streaming +unsigned long openavbRawsockGetTXOutOfBuffers(void *pvRawsock); + +// returns number of TX out of buffer events noticed from the last reporting period +unsigned long openavbRawsockGetTXOutOfBuffersCyclic(void *pvRawsock); + +#endif // RAWSOCK_H diff --git a/lib/avtp_pipeline/sdk/CMakeLists.txt b/lib/avtp_pipeline/sdk/CMakeLists.txt new file mode 100644 index 00000000..128b37c0 --- /dev/null +++ b/lib/avtp_pipeline/sdk/CMakeLists.txt @@ -0,0 +1,70 @@ +# Rules to build the SDK + +############ +# Install rules for the AVTP Interface Module SDK +# Header files +install ( FILES ../include/openavb_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../include/openavb_types_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../include/openavb_intf_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../include/openavb_log_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../include/openavb_trace_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../include/openavb_platform_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../mcr/openavb_mcr_hal_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../mediaq/openavb_mediaq_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../avtp/openavb_avtp_time_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../map_mjpeg/openavb_map_mjpeg_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../map_mpeg2ts/openavb_map_mpeg2ts_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../map_null/openavb_map_null_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../map_pipe/openavb_map_pipe_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../intf_ctrl/openavb_intf_ctrl_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../platform/${OPENAVB_OSAL}/openavb_osal_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) +install ( FILES ../platform/${OPENAVB_OSAL}/openavb_time_osal_pub.h DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) + +# Sample source files +install ( FILES ../intf_echo/openavb_intf_echo.c DESTINATION ${SDK_INSTALL_SDK_INTF_MOD_DIR} ) + +############ +# Install rules for the AVTP Mapping Module SDK +# Header files +install ( FILES ../include/openavb_pub.h DESTINATION ${SDK_INSTALL_SDK_MAP_MOD_DIR} ) +install ( FILES ../include/openavb_types_pub.h DESTINATION ${SDK_INSTALL_SDK_MAP_MOD_DIR} ) +install ( FILES ../include/openavb_map_pub.h DESTINATION ${SDK_INSTALL_SDK_MAP_MOD_DIR} ) +install ( FILES ../include/openavb_log_pub.h DESTINATION ${SDK_INSTALL_SDK_MAP_MOD_DIR} ) +install ( FILES ../include/openavb_trace_pub.h DESTINATION ${SDK_INSTALL_SDK_MAP_MOD_DIR} ) +install ( FILES ../mcr/openavb_mcr_hal_pub.h DESTINATION ${SDK_INSTALL_SDK_MAP_MOD_DIR} ) +install ( FILES ../mediaq/openavb_mediaq_pub.h DESTINATION ${SDK_INSTALL_SDK_MAP_MOD_DIR} ) +install ( FILES ../avtp/openavb_avtp_time_pub.h DESTINATION ${SDK_INSTALL_SDK_MAP_MOD_DIR} ) +install ( FILES ../map_mjpeg/openavb_map_mjpeg_pub.h DESTINATION ${SDK_INSTALL_SDK_MAP_MOD_DIR} ) +install ( FILES ../map_mpeg2ts/openavb_map_mpeg2ts_pub.h DESTINATION ${SDK_INSTALL_SDK_MAP_MOD_DIR} ) +install ( FILES ../map_null/openavb_map_null_pub.h DESTINATION ${SDK_INSTALL_SDK_MAP_MOD_DIR} ) +install ( FILES ../map_pipe/openavb_map_pipe_pub.h DESTINATION ${SDK_INSTALL_SDK_MAP_MOD_DIR} ) + +# Sample source files +install ( FILES ../map_null/openavb_map_null.c DESTINATION ${SDK_INSTALL_SDK_MAP_MOD_DIR} ) + + +############ +# Install rules for the EAVB SDK +# Header files +install ( FILES ../include/openavb_pub.h DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} ) +install ( FILES ../include/openavb_types_pub.h DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} ) +install ( FILES ../include/openavb_log_pub.h DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} ) +install ( FILES ../include/openavb_trace_pub.h DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} ) +install ( FILES ../tl/openavb_tl_pub.h DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} ) +install ( FILES ../mediaq/openavb_mediaq_pub.h DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} ) +install ( FILES ../platform/${OPENAVB_OSAL}/openavb_osal_pub.h DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} ) +install ( FILES ../platform/${OPENAVB_OSAL}/openavb_time_osal_pub.h DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} ) +install ( FILES ../include/openavb_platform_pub.h DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} ) +install ( FILES ../tl/openavb_tl_pub.h DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} ) +install ( FILES ../include/openavb_map_pub.h DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} ) +install ( FILES ../avtp/openavb_avtp_time_pub.h DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} ) +install ( FILES ../include/openavb_intf_pub.h DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} ) + +# CORE_TODO: The avb host example could be moved to the OSAL. +# Sample source files +install ( FILES ../platform/Linux/avb_host/openavb_host.c DESTINATION ${SDK_INSTALL_SDK_EAVB_DIR} ) + +# Add platform specific headers +if ( EXISTS ${AVB_OSAL_DIR}/sdk/CMakeLists.txt ) + add_subdirectory( ${AVB_OSAL_DIR}/sdk ${CMAKE_BINARY_DIR}/${OPENAVB_OSAL}/sdk ) +endif() diff --git a/lib/avtp_pipeline/srp/openavb_srp.h b/lib/avtp_pipeline/srp/openavb_srp.h new file mode 100755 index 00000000..9489a2e5 --- /dev/null +++ b/lib/avtp_pipeline/srp/openavb_srp.h @@ -0,0 +1,320 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : +* +* Implementation of IEEE 802.1Q +* Multiple Stream Reservation Protocol +* (limited intial implementation for end stations) +* +* This file declares the "Private" portion - see also openavb_srp_api.h +*/ + +#ifndef OPENAVB_SRP_H +#define OPENAVB_SRP_H + +#include "openavb_srp_api.h" +//#include "openavb_srp_osal.h" +#include "openavb_poll_osal.h" + +enum { + SRP_POLL_RAW_SOCK_FD = 0, + SRP_POLL_FD_COUNT // Must be the last entry. +}; + +// TBD - How often to [re]send (tx of the Applicant State Machine) - should be configurable +#define OPENAVB_SRP_TX_PERIOD_SEC 10 + +#define openavbSrp_PROTO_VERSION 0x00 // IEEE 802.1Q-2011 Section 35.2.2.3 +#define openavbSrp_ETHERTYPE 0x22EA // there should be a more general define for this someplace with other ether types + +// SR Class Id values are fixed. Priority (PCP) and VLAN Id values +// have defaults specified, but should be configurable - TBD +// Tables referenced here are per IEEE 802.1Q-2011 +// SR Class - SR Class Id - SR Class Priority - SR Class VID +// - Table 35-7 Table 6-6 Table 9-2 +// A 6 3 2 +// B 5 2 2 + +// SR Class IDs are defined here - used only in SRP messages +#define SR_CLASS_A_ID 6 +#define SR_CLASS_B_ID 5 + +// SR Class default Priority (PCP) values per IEEE 802.1Q-2011 Table 6-6 +#define SR_CLASS_A_DEFAULT_PRIORITY 3 +#define SR_CLASS_B_DEFAULT_PRIORITY 2 +// SR Class default VLAN Id values per IEEE 802.1Q-2011 Table 9-2 +#define SR_CLASS_A_DEFAULT_VID 2 +#define SR_CLASS_B_DEFAULT_VID 2 + +// SR Class default delta bandwidth values per IEEE 802.1Q-2011 Section 34.3.1 +#define SR_CLASS_A_DEFAULT_DELTA_BW 75 +#define SR_CLASS_B_DEFAULT_DELTA_BW 0 + +// SR Class Measurement Intervals / Frames Per Second +// - see IEEE 802.1Q-2011 Sections 34.6.1 and 35.2.2.8.4 and Table 35-5 +// Class Class Measurement Interval Frames per Second +// A 125 uSec 1 / .000125 = 8000 +// B 250 uSec 1 / .000250 = 4000 +#define SR_CLASS_A_FPS 8000 +#define SR_CLASS_B_FPS 4000 + + +// Ethernet Frame DA for openavbSrp packets is the +// “Individual LAN Scope group address, Nearest Bridge group address” (per IEEE 802.1Q) +static const U8 openavbSrpDA[ETH_MAC_ADDR_LEN] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x0E }; + +enum openavbSrpAttributeLength { // IEEE 802.1Q-2011 Table 35-2 + openavbSrp_AtLen_Lsnr = 0x08, + openavbSrp_AtLen_TlkrAdv = 0x19, // (= 25) + openavbSrp_AtLen_TlkrFld = 0x22, // (= 34) + openavbSrp_AtLen_Domain = 0x04 +}; + +// openavbSrp Attribute Event (aka "ThreePacked Events") - IEEE 802.1Q-2011 Section 35.2.2.7.1 +typedef enum openavbSrpAtEvt { + openavbSrp_AtEvt_New = 0x00, + openavbSrp_AtEvt_JoinIn = 0x01, + openavbSrp_AtEvt_In = 0x02, + openavbSrp_AtEvt_JoinMt = 0x03, + openavbSrp_AtEvt_Mt = 0x04, + openavbSrp_AtEvt_Leave = 0x05, + openavbSrp_AtEvt_None +} openavbSrpAtEvt_t; + +typedef struct openavbSrpSockInfo { + void* rawTxSock; + void* rawRxSock; + int RxSock; +} openavbSrpSockInfo_t; + +typedef struct avtpCallbacks { + strmAttachCb_t attachCb; + strmRegCb_t registerCb; +} avtpCallbacks_t; + +typedef struct SrClassParameters { + U8 priority; + U16 vid; + U32 operIdleSlope; // currently reserved bandwidth - bits per second + U32 deltaBandwidth; // % + // TBD - deltaBandwidth is supposed to be scaled by 1,000,000 + // (100,000,000 == 100%), but it is NOT in this current implementation + U32 inverseIntervalSec; // 1/classMeasurementInterval (in seconds) +} SrClassParameters_t; + +// Applicant State - IEEE 802.1Q Table 10-3 +// State here are simplified becasue: +// - we are suppporting point-to-point only and +// - initial declaration sends are immediate so there is no need for VP and VN +typedef enum openavbSrpAppState { + openavbSrp_ApSt_VO = 0x00, // no declarations - note that we initialize to 0 + openavbSrp_ApSt_AN = 0x01, // [at least] one new sent - one more pending + openavbSrp_ApSt_AA = 0x02, // [at least] one join sent - one more pending + openavbSrp_ApSt_QA = 0x03, // at least two new or join sent - none pending + openavbSrp_ApSt_LA = 0x04, // one leave sent - one more pending + openavbSrp_ApSt_VN = 0x05, // first new pending +} openavbSrpAppState_t; + +// Registrar State - IEEE 802.1Q Table 10-4 +typedef enum openavbSrpRegState { + openavbSrp_RgSt_MT = 0x00, // Empty - note that we initialize to 0 + openavbSrp_RgSt_LV = 0x01, // Leave + openavbSrp_RgSt_IN = 0x02, // In +} openavbSrpRegState_t; + + +// Element for linked list of declared / registered streams. +// Since both a talker and a listener can exist for the same stream on the same +// end-station, seperate talker and listener lists are kept. +// IMPORTANT NOTE: This implementation assumes that on a given end-station, no +// more than one talker and no more than one listener exist for each stream; if +// this is not the case, individual stream list elements could be corrupted +// because they are not thread-safe; the overall lists are mutex protected. +// +// On talker: +// a list entry will exist only if the talker has a stream to declare +// (or is in the process of withdrawing) +// - declType is what this talker has most recently declared; +// - declSubType is not used; +// - regType / regSubType indicate what the talker has most recently received from +// the listener(s), if anything; +// - avtpHandle, streamId, DA, tSpec, SRClassId, Rank, and Latency +// are as received from AVTP via openavbSrpRegisterStream(); +// - failInfo is populated only if this station has insufficient outbound +// bandwidth for this stream, so must send Talker Failed delcaration; +// - kbpsReserved is the bandwidth currently reserved for the stream. +// +// On Listener: +// a list entry will exist if the listener has either expressed interest in +// receiving the stream (or is in the process of withdrawing interest) or has +// received a talker declaration for the stream, or both. +// - declType is what this listener has most recently declared (via +// openavbSrpAttachStream()), if anything; +// - declSubType is the listener declaration subtype, if any; +// - regType indicates what talker declaration the listener has most recently +// received for the stream, if anything; (openavbSrp_AtTyp_None indicates that +// the listener currently has no talker declaration for the stream); +// - regSubType is not used; +// - avtpHandle is as recieved from AVTP via openavbSrpAttachStream(); +// - strmId is either as recieved from AVTP via openavbSrpAttachStream() or +// as recieved via a talker declaration packet (MSRPDU), whichever occurs first; +// - SRClassIdx is derived from priority received in talker declaration packet; +// - DA, tSpec, latency, and, if applicable, failInfo are +// as received in talker declaration packet +// - rank and kbpsReserved are not applicable on the listener; +// +typedef struct openavbSrpStrm { + struct openavbSrpStrm* prev; + struct openavbSrpStrm* next; + AVBStreamID_t strmId; + void* avtpHandle; + SRClassIdx_t SRClassIdx; + openavbSrpAttribType_t declType; // attribute type this station is declaring for this stream, if any; + // (declType == openavbSrp_AtTyp_None is redundant to appState == openavbSrp_ApSt_VO). + openavbSrpLsnrDeclSubtype_t declSubType; // listener subtype this station is declaring for this stream, if any; + // not used on talker; valid only if declType == openavbSrp_AtTyp_Listener. + openavbSrpAttribType_t regType; // attribute type this station has recieved (registered) for this stream, if any; + openavbSrpLsnrDeclSubtype_t regSubType; // listener subtype this station has recieved (registered) for this stream, if any; + // not used on listener; valid only if regType == openavbSrp_AtTyp_Listener. + U8 DA[ETH_MAC_ADDR_LEN]; + AVBTSpec_t tSpec; + U32 kbpsReserved; + bool rank; + U32 latency; + openavbSrpFailInfo_t failInfo; + openavbSrpAppState_t appState; // current applicant state of the stream on this station + openavbSrpRegState_t regState; +} openavbSrpStrmElem_t; + + +// SRP internal routines +int openavbSrpGetIndexFromId (U8 SRClassId); +void openavbSrpRemoveStrm (openavbSrpStrmElem_t** lstHead, openavbSrpStrmElem_t* pStream); +openavbSrpStrmElem_t* openavbSrpFindOrCreateStrm (openavbSrpStrmElem_t** lstHead, AVBStreamID_t* streamId, + bool allowCreate, bool* created); +void openavbSrpLogBw (void); +void openavbSrpLogAllStreams (void); +void openavbSrpRegState (openavbSrpAttribType_t atrbType); +void openavbSrpCheckAsCapable (void); +void openavbSrpCheckTalkerFailed(void); +void openavbSrpResend (bool tx, openavbSrpAttribType_t atrbType); +void* openavbSrpTxThread (void* notUsed); +void* openavbSrpRcvThread (void* notUsed); +openavbRC openavbSrpCheckStartLsnr (openavbSrpStrmElem_t* stream); +openavbRC openavbSrpDomainSend (void); +openavbRC openavbSrpSend (openavbSrpStrmElem_t* pStream, openavbSrpAtEvt_t atrbEvent); +void openavbSrpReceive (U8* msrpdu, unsigned int pduLen); +void openavbSrpCalcKbps (int SRClassIndex, AVBTSpec_t* tSpec, U32* kbps); +openavbRC openavbSrpCheckAvlBw (int SRClassIndex, U32 kbps); +openavbRC openavbSrpReserve (openavbSrpStrmElem_t* pStream); +void openavbSrpRelease (openavbSrpStrmElem_t* pStream); + +openavbRC openavbSrpOpenPtpSharedMemory (void); +void openavbSrpReleasePtpSharedMemory(void); +bool openavbSrpAsCapable (void); + +int openavbSrpInitializePfd (OPENAVB_POLLFD_TYPE *pfd); + +// First Value content and size depend on the Declaration Type (openavbSrpAttribType_t) +// see IEEE 802.1Q-2011 Section 35.2.2.8.1 +// (left column indicates byte index to start of parameter) +// FirstValue ::= +// 0 StreamID: 8 bytes +// 0 [Src] MAC: 6 bytes (ETH_MAC_ADDR_LEN) +// 6 UniqueID: 2 bytes, U16 +// end of the Listener attribute +// 8 DataFrameParameters: 8 bytes +// 8 DA: 6 bytes (ETH_MAC_ADDR_LEN) +// 14 VID: 2 bytes +// 16 TSpec: 4 bytes +// 16 MaxFrameSize: 2 bytes, U16 +// 18 MaxIntervalFrames: 2 bytes, U16 +// 20 PriorityAndRank: 1 byte +// 20 Priority: 3 bits +// 20 Rank: 1 bit +// 20 Reserved: 4 bits +// 21 AccumulatedLatency: 4 bytes, U32 +// end of the Talker Advertise attribute +// 25 FailureInformation: 9 bytes +// 25 BridgeID: 8 bytes +// 33 FailureCode: 1 byte +// end of the Talker Failed attribute + + +/* description of the limited MSRPDU being supported here + * (currently, only one declaration per packet is supported) + * openavbSrpDU ::= ProtocolVersion, Message + * ProtocolVersion BYTE ::= 0x00 + * Message ::= AttributeType, AttributeLength [, AttributeListLength], AttributeList + * AttributeType BYTE ::= Talker Advertise | Talker Failed | Listener + * Talker Advertise::= 0x01 + * Talker Failed::= 0x02 + * Listener::= 0x03 + * AttributeLength BYTE ::= 0x19 | 0x34 | 0x08 + * AttributeListLength SHORT ::= AttributeLength + 3 | 4 (4 only if AttributeType = Listener) + * AttributeList ::= VectorAttribute {, VectorAttribute}, EndMark + * VectorAttribute ::= VectorHeader, FirstValue, Vector + * VectorHeader SHORT ::= 0x0001 + * FirstValue ::= + * StreamID: 8 bytes + * [Src] MAC: 6 bytes + * UniqueID: 2 bytes, U16 + * end of the Listener attribute + * DataFrameParameters: 8 bytes + * DA: 6 bytes + * VID: 2 bytes + * TSpec: 4 bytes + * MaxFrameSize: 2 bytes, U16 + * MaxIntervalFrames: 2 bytes, U16 + * PriorityAndRank: 1 byte + * Priority: 3 bits + * Rank: 1 bit + * Reserved: 4 bits + * AccumulatedLatency: 4 bytes, U32 + * end of the Talker Advertise attribute + * FailureInformation: 9 bytes + * BridgeID: 8 bytes + * FailureCode: 1 byte + * end of the Talker Failed attribute + * Vector ::= ThreePackedEvent, [FourPackedEvent] + * ThreePackedEvents BYTE ::= New |Lv + * New ::= 0 + * Lv ::= 5 + * FourPackedEvents BYTE ::= Asking Failed | Ready | Ready Failed + * (FourPackedEvents is included only if AttributeType = Listener) + * Asking Failed ::= 1 + * Ready ::= 2 + * Ready Failed ::= 3 + */ + +#endif // OPENAVB_SRP_H + diff --git a/lib/avtp_pipeline/srp/openavb_srp_api.h b/lib/avtp_pipeline/srp/openavb_srp_api.h new file mode 100755 index 00000000..9d99fafa --- /dev/null +++ b/lib/avtp_pipeline/srp/openavb_srp_api.h @@ -0,0 +1,179 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : +* +* Implementation of IEEE 802.1Q +* Multiple Stream Reservation Protocol +* (limited intial implementation for end stations) +* +* This file declares the "Public" API. +* +* THIS IMPLEMENTATION ASSUMES THAT THERE WILL BE NO MORE THAN ONE +* LISTENER THREAD FOR THE SAME STREAM ON A SINGLE END STATION. +* (More than one talker for the same stream makes no sense at all.) +*/ + +#ifndef OPENAVB_SRP_API_H +#define OPENAVB_SRP_API_H + +#include "openavb_types.h" + +// TBD - If queue manager can fail to configure a queue for a granted reservation, SRP +// needs to know about the failure in order to correct reseved bandwidth totals. +// (we are probably toast if queue manager fails when removing a stream) + +// TBD - This implementation handles bandwidth in kilobits per second +// the specs want bits per second (kbps will waste some bandwidth) + +// MSRP Declaration Type - IEEE 802.1Q-2011 Table 35-1 +typedef enum openavbSrpAttribType { + openavbSrp_AtTyp_None = 0x00, // not per spec - SRP internal use only + openavbSrp_AtTyp_TalkerAdvertise = 0x01, + openavbSrp_AtTyp_TalkerFailed = 0x02, + openavbSrp_AtTyp_Listener = 0x03, + openavbSrp_AtTyp_Domain = 0x04, +} openavbSrpAttribType_t; + +// MSRP Listener Declaration Subtype (aka "FourPacked Event") - IEEE 802.1Q-2011 Table 35-3 +typedef enum openavbSrpLsnrDeclSubtype{ + openavbSrp_LDSt_None = 0x00, // not per spec - SRP internal use (same as ignore is OK) + openavbSrp_LDSt_Ignore = 0x00, + openavbSrp_LDSt_Asking_Failed = 0x01, + openavbSrp_LDSt_Ready = 0x02, + openavbSrp_LDSt_Ready_Failed = 0x03, + openavbSrp_LDSt_Stream_Info = 0xFE, // NOT per IEEE spec - for use to inform talker when MAAP allocated. + openavbSrp_LDSt_Interest = 0xFF, // NOT per IEEE spec - for srpAttachStream() only +} openavbSrpLsnrDeclSubtype_t; + +// Stream Reservation Failure Codes - IEEE 802.1Q-2011 Table 35-6 +enum openavbSrpFailureCode { + openavbSrp_FC_NoFail = 0, // 0: No failure, + // 1: Insufficient bandwidth, + // 2: Insufficient Bridge resources, + openavbSrp_FC_NoClassBandwidth = 3, // Insufficient bandwidth for Traffic Class, + // 4: StreamID in use by another Talker, + // 5: Stream destination address already in use, + // 6: Stream pre-empted by higher rank, + // 7: Reported latency has changed, + openavbSrp_FC_NotCapable = 8, // Egress port is not AVBCapable, + // 9: Use a different destination_address, + // 10: Out of MSRP resources, + // 11: Out of MMRP resources, + // 12: Cannot store destination_address, + // 13: Requested priority is not an SR Class priority, + // 14: MaxFrameSize is too large for media, + // 15: maxFanInPorts limit has been reached, + // 16: Changes in FirstValue for a registered StreamID, + // 17: VLAN is blocked on this egress port (Registration Forbidden), + // 18: VLAN tagging is disabled on this egress port (untagged set), + // 19: SR class priority mismatch. +}; + +typedef struct openavbSrpFailInfo{ // IEEE 802.1Q-2011 Section 35.2.2.8.7 + U8 BridgeID[8]; + U8 FailureCode; // openavbSrpFailureCode +} openavbSrpFailInfo_t; + + +// AVTP provided callbacks passed into openavbSrpInitialize: +// +// Callback for SRP to notify AVTP Talker that a Listener Declaration has been +// registered (or de-registered) +// - avtpHandle is provided to SRP by AVTP via openavbSrpRegisterStream() +// and is passed back to AVTP in this call; +// - [listener declaration] subtype is from the listener declaration. +typedef openavbRC (*strmAttachCb_t) (void* avtpHandle, + openavbSrpLsnrDeclSubtype_t subtype); +// Callback for SRP to notify AVTP Listener that a Talker Declaration has been +// registered (or de-registered) +// - avtpHandle is provided to SRP by AVTP via openavbSrpAttachStream() +// and is passed back to AVTP in this call; +// - declType, DA, tSpec and latency are from the Talker Declaration; +// - SRClassId is derived from the priority attribute of the Talker +// Declaration; +// - pFailInfo is valid for only if declType is openavbSrp_AtTyp_TalkerFailed. +typedef openavbRC (*strmRegCb_t) (void* avtpHandle, + openavbSrpAttribType_t declType, + U8 DA[], + AVBTSpec_t* tSpec, + SRClassIdx_t SRClassIdx, + U32 latency, + openavbSrpFailInfo_t* pFailInfo); + +// SRP API + +// Called by AVTP on talker or listener end station to start SRP. +// TxRateKbps is the maximum rate which the interface indicated by ifname +// can transmit, in kilobits per second. Set bypassAsCapableCheck true +// to ignore the IEEE802.1ba section 6.4 requirement the SRP create +// streams only on [IEEE802.1AS / gPTP] asCapable links. +openavbRC openavbSrpInitialize (strmAttachCb_t attachCb, strmRegCb_t registerCb, + char* ifname, U32 TxRateKbps, bool bypassAsCapableCheck); + +// Called by AVTP on talker or listener end station to stop SRP +void openavbSrpShutdown (void); + +// Called by AVTP on talker end station to advertise the indicated stream with +// the supplied attributes. avtpHandle should be provided; it is not used by +// SRP but is returned to AVTP as a parameter in calls to avtpStrmCb.attachCb() +// and avtpStrmCb.detachCb(). +openavbRC openavbSrpRegisterStream (void* avtpHandle, + AVBStreamID_t* streamId, + U8 DA[], + AVBTSpec_t* tSpec, + SRClassIdx_t SRClassIdx, + bool Rank, + U32 Latency); + +// Called by AVTP on talker end station to withdraw the indicated stream +openavbRC openavbSrpDeregisterStream (AVBStreamID_t* streamId); + +// Called by AVTP on listener to either express interest in the indicated stream +// or to send a listener declaration back to the indicated stream's talker. +// subType must be openavbSrp_LDSt_Interest, openavbSrp_LDSt_Ready, or openavbSrp_LDSt_Asking_Failed. +// If subType == openavbSrp_LDSt_Interest, avtpHandle should be provided; it is not used +// by SRP but is returned to AVTP as a parameter in calls to avtpStrmCb.registerCb() +// and avtpStrmCb.deregisterCb(). +openavbRC openavbSrpAttachStream (void* avtpHandle, + AVBStreamID_t* streamId, + openavbSrpLsnrDeclSubtype_t type); + +// Called by AVTP on listener to withdraw both interest in, +// and, if any, listener declaration for, the indicated stream. +openavbRC openavbSrpDetachStream (AVBStreamID_t* streamId); + +// Get the Priority Code Point (PCP), VLAN Id and 1/classMeasurementInterval +// (in seconds) for the indicated SR Class +openavbRC openavbSrpGetClassParams (SRClassIdx_t SRClassIdx, U8* priority, U16* vid, U32* inverseIntervalSec); + +#endif // OPENAVB_SRP_API_H + diff --git a/lib/avtp_pipeline/tl/CMakeLists.txt b/lib/avtp_pipeline/tl/CMakeLists.txt new file mode 100644 index 00000000..7c8a19d7 --- /dev/null +++ b/lib/avtp_pipeline/tl/CMakeLists.txt @@ -0,0 +1,29 @@ +SET (SRC_FILES_TL + ${AVB_SRC_DIR}/tl/openavb_tl.c + ${AVB_OSAL_DIR}/tl/openavb_tl_osal.c + ${AVB_SRC_DIR}/tl/openavb_listener.c + ${AVB_SRC_DIR}/tl/openavb_talker.c + ) + +if(AVB_FEATURE_ENDPOINT) + #Additional Files for Endpoint + MESSAGE ("-- TL with Endpoint") + SET (SRC_FILES_TL_EXTRA + ${AVB_SRC_DIR}/endpoint/openavb_endpoint_client.c + ${AVB_SRC_DIR}/tl/openavb_tl_endpoint.c + ${AVB_SRC_DIR}/tl/openavb_talker_endpoint.c + ${AVB_SRC_DIR}/tl/openavb_listener_endpoint.c + ) +else() + #Additional Files for No Endpoint + MESSAGE ("-- TL without Endpoint") + SET (SRC_FILES_TL_EXTRA + ${AVB_SRC_DIR}/tl/openavb_tl_no_endpoint.c + ${AVB_SRC_DIR}/tl/openavb_talker_no_endpoint.c + ${AVB_SRC_DIR}/tl/openavb_listener_no_endpoint.c + ) +endif() + +SET ( SRC_FILES ${SRC_FILES} ${SRC_FILES_TL} ${SRC_FILES_TL_EXTRA} PARENT_SCOPE) + + diff --git a/lib/avtp_pipeline/tl/NOTES.TXT b/lib/avtp_pipeline/tl/NOTES.TXT new file mode 100644 index 00000000..b9400cad --- /dev/null +++ b/lib/avtp_pipeline/tl/NOTES.TXT @@ -0,0 +1,28 @@ +The TL (talker/listener) modules manages the talker and listener functionality +and the stream associated with them. + +The functionality is exposed via a public API as declared in openavb_tl_pub.h. +These details of this API are described in the EAVB SDK Developer Guide. + +The general flow is as follows. The hosting application dynamically links in the +TL functional. These TL API are implemented in openavb_tl.c. openavbTLInitialize() is +called first to initialize the TL lists. openavbTLOpen() is called after that with +the ini file name. There will be one call to openavbTLOpen for each talker or listener. +openavbTLRun() is used to start the stream for the talker or listener. openavbTLClose() +will stop the stream for the talker or listener. Finally the openavbTLShutdown() is +used to cleanup the TL functionality. + +There are two hosting applications included with our AVB stack primarily as +samples. These are openavb_tl_host and openavb_tl_harness. It is expected that most +solutions will replace these with a customer specific application. + +At a lower level most things happen as a result of the stTLOpen() call. A thread +is created for each talker and listener. So this means every stream has it's own +thread. The main thread entry point is openavbTLThreadFn(). From there the detailed +talker or listener functionality is called with openavbTLRunTalker() or +openavbTLRunListener() respectively. It is in these talker and listener run functions +the talker registers to the endpoint and the listener attaches to the endpoint. +The endpoint will call back into the talker and listener with details of streams +coming and going so that the talker and listener can determine when to send or not +send and when to listen or not listen. + diff --git a/lib/avtp_pipeline/tl/openavb_listener.c b/lib/avtp_pipeline/tl/openavb_listener.c new file mode 100644 index 00000000..ef5817db --- /dev/null +++ b/lib/avtp_pipeline/tl/openavb_listener.c @@ -0,0 +1,424 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Listener implementation +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "openavb_platform.h" +#include "openavb_trace.h" +#include "openavb_tl.h" +#include "openavb_avtp.h" +#include "openavb_listener.h" + +// DEBUG Uncomment to turn on logging for just this module. +//#define AVB_LOG_ON 1 + +#define AVB_LOG_COMPONENT "Listener" +#include "openavb_log.h" + +#include "openavb_debug.h" + +bool listenerStartStream(tl_state_t *pTLState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return FALSE; + } + + assert(!pTLState->bStreaming); + + openavb_tl_cfg_t *pCfg = &pTLState->cfg; + listener_data_t *pListenerData = pTLState->pPvtListenerData; + + openavbRC rc = openavbAvtpRxInit(pTLState->pMediaQ, + &pCfg->map_cb, + &pCfg->intf_cb, + pListenerData->ifname, + &pListenerData->streamID, + pListenerData->destAddr, + pCfg->raw_rx_buffers, + pCfg->rx_signal_mode, + &pListenerData->avtpHandle); + if (IS_OPENAVB_FAILURE(rc)) { + AVB_LOG_ERROR("Failed to create AVTP stream"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return FALSE; + } + + // Setup timers + U64 nowNS; + CLOCK_GETTIME64(OPENAVB_TIMER_CLOCK, &nowNS); + pListenerData->nextReportNS = nowNS + (pCfg->report_seconds * NANOSECONDS_PER_SECOND); + pListenerData->nextSecondNS = nowNS + NANOSECONDS_PER_SECOND; + + // Clear counters + pListenerData->nReportCalls = 0; + pListenerData->nReportFrames = 0; + + // Clear stats + openavbListenerClearStats(pTLState); + + // we're good to go! + pTLState->bStreaming = TRUE; + + AVB_TRACE_EXIT(AVB_TRACE_TL); + return TRUE; +} + +void listenerStopStream(tl_state_t *pTLState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + listener_data_t *pListenerData = pTLState->pPvtListenerData; + if (!pListenerData) { + AVB_LOG_ERROR("Invalid listener data"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + openavbListenerAddStat(pTLState, TL_STAT_RX_CALLS, pListenerData->nReportCalls); + openavbListenerAddStat(pTLState, TL_STAT_RX_FRAMES, pListenerData->nReportFrames); + openavbListenerAddStat(pTLState, TL_STAT_RX_LOST, openavbAvtpLost(pListenerData->avtpHandle)); + openavbListenerAddStat(pTLState, TL_STAT_RX_BYTES, openavbAvtpBytes(pListenerData->avtpHandle)); + + AVB_LOGF_INFO("RX "STREAMID_FORMAT", Totals: calls=%lld, frames=%lld, lost=%lld, bytes=%lld", + STREAMID_ARGS(&pListenerData->streamID), + openavbListenerGetStat(pTLState, TL_STAT_RX_CALLS), + openavbListenerGetStat(pTLState, TL_STAT_RX_FRAMES), + openavbListenerGetStat(pTLState, TL_STAT_RX_LOST), + openavbListenerGetStat(pTLState, TL_STAT_RX_BYTES)); + + if (pTLState->bStreaming) { + openavbAvtpShutdown(pListenerData->avtpHandle); + pTLState->bStreaming = FALSE; + } + + AVB_TRACE_EXIT(AVB_TRACE_TL); +} + +static inline bool listenerDoStream(tl_state_t *pTLState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return FALSE; + } + + openavb_tl_cfg_t *pCfg = &pTLState->cfg; + listener_data_t *pListenerData = pTLState->pPvtListenerData; + bool bRet = FALSE; + + if (pTLState->bStreaming) { + U64 nowNS; + + pListenerData->nReportCalls++; + + // Try to receive a frame + if (IS_OPENAVB_SUCCESS(openavbAvtpRx(pListenerData->avtpHandle))) { + pListenerData->nReportFrames++; + } + + CLOCK_GETTIME64(OPENAVB_TIMER_CLOCK, &nowNS); + + if (pCfg->report_seconds > 0) { + if (nowNS > pListenerData->nextReportNS) { + + U64 lost = openavbAvtpLost(pListenerData->avtpHandle); + U64 bytes = openavbAvtpBytes(pListenerData->avtpHandle); + U32 rxbuf = openavbAvtpRxBufferLevel(pListenerData->avtpHandle); + U32 mqbuf = openavbMediaQCountItems(pTLState->pMediaQ, TRUE); + U32 mqrdy = openavbMediaQCountItems(pTLState->pMediaQ, FALSE); + + AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, FALSE, "RX UID:%d, ", LOG_RT_DATATYPE_U16, &pListenerData->streamID.uniqueID); + AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "calls=%ld, ", LOG_RT_DATATYPE_U32, &pListenerData->nReportCalls); + AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "frames=%ld, ", LOG_RT_DATATYPE_U32, &pListenerData->nReportFrames); + AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "lost=%lld, ", LOG_RT_DATATYPE_U64, &lost); + AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "bytes=%lld, ", LOG_RT_DATATYPE_U64, &bytes); + AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "rxbuf=%d, ", LOG_RT_DATATYPE_U32, &rxbuf); + AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "mqbuf=%d, ", LOG_RT_DATATYPE_U32, &mqbuf); + AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, LOG_RT_END, "mqrdy=%d", LOG_RT_DATATYPE_U32, &mqrdy); + + openavbListenerAddStat(pTLState, TL_STAT_RX_CALLS, pListenerData->nReportCalls); + openavbListenerAddStat(pTLState, TL_STAT_RX_FRAMES, pListenerData->nReportFrames); + openavbListenerAddStat(pTLState, TL_STAT_RX_LOST, lost); + openavbListenerAddStat(pTLState, TL_STAT_RX_BYTES, bytes); + + pListenerData->nReportCalls = 0; + pListenerData->nReportFrames = 0; + pListenerData->nextReportNS += (pCfg->report_seconds * NANOSECONDS_PER_SECOND); + } + } + + if (nowNS > pListenerData->nextSecondNS) { + pListenerData->nextSecondNS += NANOSECONDS_PER_SECOND; + bRet = TRUE; + } + } + else { + SLEEP(1); + bRet = TRUE; + } + + AVB_TRACE_EXIT(AVB_TRACE_TL); + return bRet; +} + +// Called from openavbTLThreadFn() which is started from openavbTLRun() +void openavbTLRunListener(tl_state_t *pTLState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + openavb_tl_cfg_t *pCfg = &pTLState->cfg; + + pTLState->pPvtListenerData = calloc(1, sizeof(listener_data_t)); + if (!pTLState->pPvtListenerData) { + AVB_LOG_WARNING("Failed to allocate listener data."); + return; + } + + AVBStreamID_t streamID; + memcpy(streamID.addr, pCfg->stream_addr.mac, ETH_ALEN); + streamID.uniqueID = pCfg->stream_uid; + + AVB_LOGF_INFO("Attach "STREAMID_FORMAT, STREAMID_ARGS(&streamID)); + + // Create Stats Mutex + { + MUTEX_ATTR_HANDLE(mta); + MUTEX_ATTR_INIT(mta); + MUTEX_ATTR_SET_TYPE(mta, MUTEX_ATTR_TYPE_DEFAULT); + MUTEX_ATTR_SET_NAME(mta, "TLStatsMutex"); + MUTEX_CREATE_ERR(); + MUTEX_CREATE(pTLState->statsMutex, mta); + MUTEX_LOG_ERR("Could not create/initialize 'TLStatsMutex' mutex"); + } + + // Tell endpoint to listen for our stream. + // If there is a talker, we'll get callback (above.) + pTLState->bConnected = openavbTLRunListenerInit(pTLState->endpointHandle, &streamID); + + if (pTLState->bConnected) { + bool bServiceIPC; + + // Do until we are stopped or loose connection to endpoint + while (pTLState->bRunning && pTLState->bConnected) { + + // Listen for an RX frame (or just sleep if not streaming) + bServiceIPC = listenerDoStream(pTLState); + + if (bServiceIPC) { + // Look for messages from endpoint. Don't block (timeout=0) + if (!openavbEptClntService(pTLState->endpointHandle, 0)) { + AVB_LOGF_WARNING("Lost connection to endpoint "STREAMID_FORMAT, STREAMID_ARGS(&streamID)); + pTLState->bConnected = FALSE; + pTLState->endpointHandle = 0; + } + } + } + + // Stop streaming + listenerStopStream(pTLState); + + { + MUTEX_CREATE_ERR(); + MUTEX_DESTROY(pTLState->statsMutex); // Destroy Stats Mutex + MUTEX_LOG_ERR("Error destroying mutex"); + } + + // withdraw our listener attach + if (pTLState->bConnected) + openavbEptClntStopStream(pTLState->endpointHandle, &streamID); + } + else { + AVB_LOGF_WARNING("Failed to connect to endpoint "STREAMID_FORMAT, STREAMID_ARGS(&streamID)); + } + + if (pTLState->pPvtListenerData) { + free(pTLState->pPvtListenerData); + pTLState->pPvtListenerData = NULL; + } + + AVB_TRACE_EXIT(AVB_TRACE_TL); +} + +void openavbTLPauseListener(tl_state_t *pTLState, bool bPause) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + listener_data_t *pListenerData = pTLState->pPvtListenerData; + if (!pListenerData) { + AVB_LOG_ERROR("Invalid private listener data"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + openavbAvtpPause(pListenerData->avtpHandle, bPause); + + AVB_TRACE_EXIT(AVB_TRACE_TL); +} + +void openavbListenerClearStats(tl_state_t *pTLState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + listener_data_t *pListenerData = pTLState->pPvtListenerData; + if (!pListenerData) { + AVB_LOG_ERROR("Invalid private listener data"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + LOCK_STATS(); + memset(&pListenerData->stats, 0, sizeof(pListenerData->stats)); + UNLOCK_STATS(); + + AVB_TRACE_EXIT(AVB_TRACE_TL); +} + +void openavbListenerAddStat(tl_state_t *pTLState, tl_stat_t stat, U64 val) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + listener_data_t *pListenerData = pTLState->pPvtListenerData; + if (!pListenerData) { + AVB_LOG_ERROR("Invalid private listener data"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + LOCK_STATS(); + switch (stat) { + case TL_STAT_TX_CALLS: + case TL_STAT_TX_FRAMES: + case TL_STAT_TX_LATE: + case TL_STAT_TX_BYTES: + break; + case TL_STAT_RX_CALLS: + pListenerData->stats.totalCalls += val; + break; + case TL_STAT_RX_FRAMES: + pListenerData->stats.totalFrames += val; + break; + case TL_STAT_RX_LOST: + pListenerData->stats.totalLost += val; + break; + case TL_STAT_RX_BYTES: + pListenerData->stats.totalBytes += val; + break; + } + UNLOCK_STATS(); + + AVB_TRACE_EXIT(AVB_TRACE_TL); +} + +U64 openavbListenerGetStat(tl_state_t *pTLState, tl_stat_t stat) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + U64 val = 0; + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return 0; + } + + listener_data_t *pListenerData = pTLState->pPvtListenerData; + if (!pListenerData) { + AVB_LOG_ERROR("Invalid private listener data"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return 0; + } + + LOCK_STATS(); + switch (stat) { + case TL_STAT_TX_CALLS: + case TL_STAT_TX_FRAMES: + case TL_STAT_TX_LATE: + case TL_STAT_TX_BYTES: + break; + case TL_STAT_RX_CALLS: + val = pListenerData->stats.totalCalls; + break; + case TL_STAT_RX_FRAMES: + val = pListenerData->stats.totalFrames; + break; + case TL_STAT_RX_LOST: + val = pListenerData->stats.totalLost; + break; + case TL_STAT_RX_BYTES: + val = pListenerData->stats.totalBytes; + break; + } + UNLOCK_STATS(); + + AVB_TRACE_EXIT(AVB_TRACE_TL); + return val; +} + + diff --git a/lib/avtp_pipeline/tl/openavb_listener.h b/lib/avtp_pipeline/tl/openavb_listener.h new file mode 100644 index 00000000..89d2f6fe --- /dev/null +++ b/lib/avtp_pipeline/tl/openavb_listener.h @@ -0,0 +1,73 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Listener +*/ + +#ifndef OPENAVB_TL_LISTENER_H +#define OPENAVB_TL_LISTENER_H 1 + +#include "openavb_tl.h" + +typedef struct { + U64 totalCalls; + U64 totalFrames; + U64 totalLost; + U64 totalBytes; +} listener_stats_t; + +typedef struct { + // Data from callback + char ifname[IFNAMSIZ]; + AVBStreamID_t streamID; + U8 destAddr[ETH_ALEN]; + AVBTSpec_t tSpec; + long nFramesRx; + + // State info for streaming + void *avtpHandle; + unsigned long nReportFrames; + unsigned long nReportCalls; + U64 nextReportNS; + U64 nextSecondNS; + listener_stats_t stats; +} listener_data_t; + +void openavbTLRunListener(tl_state_t *pTLState); +void openavbTLPauseListener(tl_state_t *pTLState, bool bPause); +void openavbListenerClearStats(tl_state_t *pTLState); +void openavbListenerAddStat(tl_state_t *pTLState, tl_stat_t stat, U64 val); +U64 openavbListenerGetStat(tl_state_t *pTLState, tl_stat_t stat); +bool openavbTLRunListenerInit(int h, AVBStreamID_t *streamID); +bool listenerStartStream(tl_state_t *pTLState); +void listenerStopStream(tl_state_t *pTLState); + +#endif // OPENAVB_TL_LISTENER_H diff --git a/lib/avtp_pipeline/tl/openavb_listener_endpoint.c b/lib/avtp_pipeline/tl/openavb_listener_endpoint.c new file mode 100644 index 00000000..e1521e17 --- /dev/null +++ b/lib/avtp_pipeline/tl/openavb_listener_endpoint.c @@ -0,0 +1,139 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Listener implementation +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "openavb_platform.h" +#include "openavb_trace.h" +#include "openavb_tl.h" +#include "openavb_endpoint.h" +#include "openavb_avtp.h" +#include "openavb_listener.h" + +// DEBUG Uncomment to turn on logging for just this module. +//#define AVB_LOG_ON 1 + +#define AVB_LOG_COMPONENT "Listener" +#include "openavb_log.h" + +/* Listener callback comes from endpoint, to indicate when talkers + * come and go. We may need to start or stop the listener thread. + */ +void openavbEptClntNotifyLstnrOfSrpCb(int endpointHandle, + AVBStreamID_t *streamID, + char *ifname, + U8 destAddr[], + openavbSrpAttribType_t tlkrDecl, + AVBTSpec_t *tSpec, + U8 srClassID, + U32 latency, + openavbSrpFailInfo_t *failInfo) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + static const U8 emptyMAC[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + tl_state_t *pTLState = TLHandleListGet(endpointHandle); + openavb_tl_cfg_t *pCfg = &pTLState->cfg; + listener_data_t *pListenerData = pTLState->pPvtListenerData; + + if (!pTLState) { + AVB_LOG_WARNING("Unable to get listener from endpoint handle."); + return; + } + + AVB_LOGF_DEBUG("%s streaming=%d, tlkrDecl=%d", __FUNCTION__, pTLState->bStreaming, tlkrDecl); + + if (!pTLState->bStreaming + && tlkrDecl == openavbSrp_AtTyp_TalkerAdvertise) { + // if(x_cfg.noSrp) this is sort of a recursive call into openavbEptClntAttachStream() + // but we are OK due to the intervening IPC. + bool rc = openavbEptClntAttachStream(pTLState->endpointHandle, streamID, openavbSrp_LDSt_Ready); + if (rc) { + // Save data provided by endpoint/SRP + strncpy(pListenerData->ifname, ifname, IFNAMSIZ); + memcpy(&pListenerData->streamID, streamID, sizeof(AVBStreamID_t)); + if (memcmp(destAddr, emptyMAC, ETH_ALEN) != 0) { + memcpy(&pListenerData->destAddr, destAddr, ETH_ALEN); + memcpy(&pListenerData->tSpec, tSpec, sizeof(AVBTSpec_t)); + } + else { + // manual stream configuration required to be obtained from config file; + // see comments at call to strmRegCb() in openavbEptClntAttachStream() in openavb_endpoint.c + AVB_LOG_INFO("Endpoint Configuration requires manual stream configuration on listener"); + if ((!pCfg->dest_addr.mac) || memcmp(&(pCfg->dest_addr.mac->ether_addr_octet[0]), &(emptyMAC[0]), ETH_ALEN) == 0) { + AVB_LOG_ERROR(" Configuration Error - dest_addr required in listener config file"); + } + else { + memcpy(&pListenerData->destAddr, &pCfg->dest_addr.mac->ether_addr_octet, ETH_ALEN); + AVB_LOGF_INFO(" Listener configured dest_addr is %02x:%02x:%02x:%02x:%02x:%02x", + pListenerData->destAddr[0], pListenerData->destAddr[1], + pListenerData->destAddr[2], pListenerData->destAddr[3], + pListenerData->destAddr[4], pListenerData->destAddr[5]); + } + if ((!pCfg->max_interval_frames) || (!pCfg->max_frame_size)) { + AVB_LOG_ERROR(" Configuration Error - both max_interval_frames and max_frame_size required in listener config file"); + } + else { + pListenerData->tSpec.maxIntervalFrames = pCfg->max_interval_frames; + pListenerData->tSpec.maxFrameSize = pCfg->max_frame_size; + AVB_LOGF_INFO(" Listener configured max_interval_frames = %u, max_frame_size = %u", + pListenerData->tSpec.maxIntervalFrames, pListenerData->tSpec.maxFrameSize); + } + } + + // We should start streaming + AVB_LOGF_INFO("Starting stream: "STREAMID_FORMAT, STREAMID_ARGS(streamID)); + listenerStartStream(pTLState); + } + else { + AVB_LOG_DEBUG("Failed to attach listener stream"); + } + } + else if (pTLState->bStreaming + && tlkrDecl != openavbSrp_AtTyp_TalkerAdvertise) { + AVB_LOGF_INFO("Stopping stream: "STREAMID_FORMAT, STREAMID_ARGS(streamID)); + listenerStopStream(pTLState); + + // We're still interested in the stream + openavbEptClntAttachStream(pTLState->endpointHandle, streamID, openavbSrp_LDSt_Interest); + } + + AVB_TRACE_EXIT(AVB_TRACE_TL); +} + +bool openavbTLRunListenerInit(int h, AVBStreamID_t *streamID) +{ + return(openavbEptClntAttachStream(h, streamID, openavbSrp_LDSt_Interest)); +} diff --git a/lib/avtp_pipeline/tl/openavb_listener_no_endpoint.c b/lib/avtp_pipeline/tl/openavb_listener_no_endpoint.c new file mode 100644 index 00000000..90502d77 --- /dev/null +++ b/lib/avtp_pipeline/tl/openavb_listener_no_endpoint.c @@ -0,0 +1,80 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+/*
+* MODULE SUMMARY : Listener implementation
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "openavb_platform.h"
+#include "openavb_tl.h"
+#include "openavb_avtp.h"
+#include "openavb_listener.h"
+#include "openavb_endpoint.h"
+
+#define AVB_LOG_COMPONENT "Listener"
+#include "openavb_log.h"
+#include "openavb_trace.h"
+
+bool openavbTLRunListenerInit(int hnd, AVBStreamID_t *streamID)
+{
+ tl_state_t *pTLState = TLHandleListGet(hnd);
+ openavb_tl_cfg_t *pCfg = &pTLState->cfg;
+ listener_data_t *pListenerData = pTLState->pPvtListenerData;
+
+ strncpy(pListenerData->ifname, pCfg->ifname, IFNAMSIZ);
+ memcpy(&pListenerData->streamID.addr, &pCfg->stream_addr.mac->ether_addr_octet, ETH_ALEN);
+ pListenerData->streamID.uniqueID = pCfg->stream_uid;
+ memcpy(&pListenerData->destAddr, &pCfg->dest_addr.mac->ether_addr_octet, ETH_ALEN);
+ pListenerData->tSpec.maxIntervalFrames = pCfg->max_interval_frames;
+ pListenerData->tSpec.maxFrameSize = pCfg->max_frame_size;
+
+ AVB_LOGF_INFO("Dest Addr: "ETH_FORMAT, ETH_OCTETS(pListenerData->destAddr));
+ AVB_LOGF_INFO("Starting stream: "STREAMID_FORMAT, STREAMID_ARGS(streamID));
+ listenerStartStream(pTLState);
+
+ return TRUE;
+}
+
+
+void openavbEptClntNotifyLstnrOfSrpCb(int endpointHandle,
+ AVBStreamID_t *streamID,
+ char *ifname,
+ U8 destAddr[],
+ openavbSrpAttribType_t tlkrDecl,
+ AVBTSpec_t *tSpec,
+ U8 srClassID,
+ U32 latency,
+ openavbSrpFailInfo_t *failInfo)
+{
+}
+
diff --git a/lib/avtp_pipeline/tl/openavb_talker.c b/lib/avtp_pipeline/tl/openavb_talker.c new file mode 100644 index 00000000..c142f559 --- /dev/null +++ b/lib/avtp_pipeline/tl/openavb_talker.c @@ -0,0 +1,495 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Talker implementation +*/ + +#include <stdlib.h> +#include "openavb_platform.h" +#include "openavb_trace.h" +#include "openavb_tl.h" +#include "openavb_avtp.h" +#include "openavb_talker.h" +// #include "openavb_time.h" + +// DEBUG Uncomment to turn on logging for just this module. +//#define AVB_LOG_ON 1 + +#define AVB_LOG_COMPONENT "Talker" +#include "openavb_log.h" + +#include "openavb_debug.h" + + + +bool talkerStartStream(tl_state_t *pTLState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return FALSE; + } + + openavb_tl_cfg_t *pCfg = &pTLState->cfg; + talker_data_t *pTalkerData = pTLState->pPvtTalkerData; + + assert(!pTLState->bStreaming); + + pTalkerData->wakeFrames = pCfg->max_interval_frames * pCfg->batch_factor; + + // Set a max_transmit_deficit_usec default + if (pCfg->max_transmit_deficit_usec == 0) + pCfg->max_transmit_deficit_usec = 50000; + + openavbRC rc = openavbAvtpTxInit(pTLState->pMediaQ, + &pCfg->map_cb, + &pCfg->intf_cb, + pTalkerData->ifname, + &pTalkerData->streamID, + pTalkerData->destAddr, + pCfg->max_transit_usec, + pTalkerData->fwmark, + pTalkerData->vlanID, + pTalkerData->vlanPCP, + pTalkerData->wakeFrames * pCfg->raw_tx_buffers, + &pTalkerData->avtpHandle); + if (IS_OPENAVB_FAILURE(rc)) { + AVB_LOG_ERROR("Failed to create AVTP stream"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return FALSE; + } + + avtp_stream_t *pStream = (avtp_stream_t *)(pTalkerData->avtpHandle); + + if (!pStream->pMapCB->map_transmit_interval_cb(pTLState->pMediaQ)) { + pTalkerData->wakeRate = pTalkerData->classRate / pCfg->batch_factor; + } + else { + // Override the class observation interval with the one provided by the mapping module. + pTalkerData->wakeRate = pStream->pMapCB->map_transmit_interval_cb(pTLState->pMediaQ) / pCfg->batch_factor; + } + pTalkerData->sleepUsec = MICROSECONDS_PER_SECOND / pTalkerData->wakeRate; + pTalkerData->intervalNS = NANOSECONDS_PER_SECOND / pTalkerData->wakeRate; + + U32 SRKbps = ((unsigned long)pTalkerData->classRate * (unsigned long)pCfg->max_interval_frames * (unsigned long)pStream->frameLen * 8L) / 1000; + U32 DataKbps = ((unsigned long)pTalkerData->wakeRate * (unsigned long)pCfg->max_interval_frames * (unsigned long)pStream->frameLen * 8L) / 1000; + + AVB_LOGF_INFO(STREAMID_FORMAT", sr-rate=%lu, data-rate=%lu, frames=%lu, size=%lu, batch=%ld, sleep=%lldus, sr-Kbps=%d, data-Kbps=%d", + STREAMID_ARGS(&pTalkerData->streamID), (unsigned long)(pTalkerData->classRate), (unsigned long)(pTalkerData->wakeRate), + pTalkerData->tSpec.maxIntervalFrames, pTalkerData->tSpec.maxFrameSize, + pCfg->batch_factor, pTalkerData->intervalNS / 1000, SRKbps, DataKbps); + + + // number of intervals per report + pTalkerData->wakesPerReport = pCfg->report_seconds * NANOSECONDS_PER_SECOND / pTalkerData->intervalNS; + // counts of intervals and frames between reports + pTalkerData->cntFrames = 0; + pTalkerData->cntWakes = 0; + + // setup the initial times + U64 nowNS; + CLOCK_GETTIME64(OPENAVB_TIMER_CLOCK, &nowNS); + + // Align clock : allows for some performance gain + nowNS = ((nowNS + (pTalkerData->intervalNS)) / pTalkerData->intervalNS) * pTalkerData->intervalNS; + + pTalkerData->nextReportNS = nowNS + (pCfg->report_seconds * NANOSECONDS_PER_SECOND); + pTalkerData->nextSecondNS = nowNS + NANOSECONDS_PER_SECOND; + pTalkerData->nextCycleNS = nowNS + pTalkerData->intervalNS; + + // Clear stats + openavbTalkerClearStats(pTLState); + + // we're good to go! + pTLState->bStreaming = TRUE; + + AVB_TRACE_EXIT(AVB_TRACE_TL); + return TRUE; +} + +void talkerStopStream(tl_state_t *pTLState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + talker_data_t *pTalkerData = pTLState->pPvtTalkerData; + if (!pTalkerData) { + AVB_LOG_ERROR("Invalid listener data"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + void *rawsock = NULL; + if (pTalkerData->avtpHandle) { + rawsock = ((avtp_stream_t*)pTalkerData->avtpHandle)->rawsock; + } + + openavbTalkerAddStat(pTLState, TL_STAT_TX_CALLS, pTalkerData->cntWakes); + openavbTalkerAddStat(pTLState, TL_STAT_TX_FRAMES, pTalkerData->cntFrames); +// openavbTalkerAddStat(pTLState, TL_STAT_TX_LATE, 0); // Can't calulate at this time + openavbTalkerAddStat(pTLState, TL_STAT_TX_BYTES, openavbAvtpBytes(pTalkerData->avtpHandle)); + + AVB_LOGF_INFO("TX "STREAMID_FORMAT", Totals: calls=%lld, frames=%lld, late=%lld, bytes=%lld, TXOutOfBuffs=%ld", + STREAMID_ARGS(&pTalkerData->streamID), + openavbTalkerGetStat(pTLState, TL_STAT_TX_CALLS), + openavbTalkerGetStat(pTLState, TL_STAT_TX_FRAMES), + openavbTalkerGetStat(pTLState, TL_STAT_TX_LATE), + openavbTalkerGetStat(pTLState, TL_STAT_TX_BYTES), + openavbRawsockGetTXOutOfBuffers(rawsock) + ); + + if (pTLState->bStreaming) { + openavbAvtpShutdown(pTalkerData->avtpHandle); + pTLState->bStreaming = FALSE; + } + + AVB_TRACE_EXIT(AVB_TRACE_TL); +} + +static inline bool talkerDoStream(tl_state_t *pTLState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return FALSE; + } + + openavb_tl_cfg_t *pCfg = &pTLState->cfg; + talker_data_t *pTalkerData = pTLState->pPvtTalkerData; + bool bRet = FALSE; + + if (pTLState->bStreaming) { + U64 nowNS; + + if (!pCfg->tx_blocking_in_intf) { + + // sleep until the next interval + SLEEP_UNTIL_NSEC(pTalkerData->nextCycleNS); + + //AVB_DBG_INTERVAL(8000, TRUE); + + // send the frames for this interval + int i; + for (i = pTalkerData->wakeFrames; i > 0; i--) { + if (IS_OPENAVB_SUCCESS(openavbAvtpTx(pTalkerData->avtpHandle, i == 1, pCfg->tx_blocking_in_intf))) + pTalkerData->cntFrames++; + else break; + } + } + else { + // Interface module block option + if (IS_OPENAVB_SUCCESS(openavbAvtpTx(pTalkerData->avtpHandle, TRUE, pCfg->tx_blocking_in_intf))) + pTalkerData->cntFrames++; + } + + if (pTalkerData->cntWakes++ % pTalkerData->wakeRate == 0) { + // time to service the endpoint IPC + bRet = TRUE; + } + + CLOCK_GETTIME64(OPENAVB_TIMER_CLOCK, &nowNS); + + if (pCfg->report_seconds > 0) { + if (nowNS > pTalkerData->nextReportNS) { + + S32 late = pTalkerData->wakesPerReport - pTalkerData->cntWakes; + U64 bytes = openavbAvtpBytes(pTalkerData->avtpHandle); + if (late < 0) late = 0; + U32 txbuf = openavbAvtpTxBufferLevel(pTalkerData->avtpHandle); + U32 mqbuf = openavbMediaQCountItems(pTLState->pMediaQ, TRUE); + + AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, FALSE, "TX UID:%d, ", LOG_RT_DATATYPE_U16, &pTalkerData->streamID.uniqueID); + AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "calls=%ld, ", LOG_RT_DATATYPE_U32, &pTalkerData->cntWakes); + AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "frames=%ld, ", LOG_RT_DATATYPE_U32, &pTalkerData->cntFrames); + AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "late=%d, ", LOG_RT_DATATYPE_U32, &late); + AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "bytes=%lld, ", LOG_RT_DATATYPE_U64, &bytes); + AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "txbuf=%d, ", LOG_RT_DATATYPE_U32, &txbuf); + AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, LOG_RT_END, "mqbuf=%d, ", LOG_RT_DATATYPE_U32, &mqbuf); + + openavbTalkerAddStat(pTLState, TL_STAT_TX_CALLS, pTalkerData->cntWakes); + openavbTalkerAddStat(pTLState, TL_STAT_TX_FRAMES, pTalkerData->cntFrames); + openavbTalkerAddStat(pTLState, TL_STAT_TX_LATE, late); + openavbTalkerAddStat(pTLState, TL_STAT_TX_BYTES, bytes); + + pTalkerData->cntFrames = 0; + pTalkerData->cntWakes = 0; + pTalkerData->nextReportNS = nowNS + (pCfg->report_seconds * NANOSECONDS_PER_SECOND); + } + } + + if (nowNS > pTalkerData->nextSecondNS) { + pTalkerData->nextSecondNS += NANOSECONDS_PER_SECOND; + bRet = TRUE; + } + + if (!pCfg->tx_blocking_in_intf) { + pTalkerData->nextCycleNS += pTalkerData->intervalNS; + + if ((pTalkerData->nextCycleNS + (pCfg->max_transmit_deficit_usec * 1000)) < nowNS) { + // Hit max deficit time. Something must be wrong. Reset the cycle timer. + // Align clock : allows for some performance gain + nowNS = ((nowNS + (pTalkerData->intervalNS)) / pTalkerData->intervalNS) * pTalkerData->intervalNS; + pTalkerData->nextCycleNS = nowNS + pTalkerData->intervalNS; + } + } + } + else { + SLEEP(1); + // time to service the endpoint IPC + bRet = TRUE; + } + + AVB_TRACE_EXIT(AVB_TRACE_TL); + return bRet; +} + + +// Called from openavbTLThreadFn() which is started from openavbTLRun() +void openavbTLRunTalker(tl_state_t *pTLState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + pTLState->pPvtTalkerData = calloc(1, sizeof(talker_data_t)); + if (!pTLState->pPvtTalkerData) { + AVB_LOG_WARNING("Failed to allocate talker data."); + return; + } + + // Create Stats Mutex + { + MUTEX_ATTR_HANDLE(mta); + MUTEX_ATTR_INIT(mta); + MUTEX_ATTR_SET_TYPE(mta, MUTEX_ATTR_TYPE_DEFAULT); + MUTEX_ATTR_SET_NAME(mta, "TLStatsMutex"); + MUTEX_CREATE_ERR(); + MUTEX_CREATE(pTLState->statsMutex, mta); + MUTEX_LOG_ERR("Could not create/initialize 'TLStatsMutex' mutex"); + } + + /* If using endpoint register talker, + else register with tpsec */ + pTLState->bConnected = openavbTLRunTalkerInit(pTLState); + + if (pTLState->bConnected) { + bool bServiceIPC; + + // Do until we are stopped or loose connection to endpoint + while (pTLState->bRunning && pTLState->bConnected) { + + // Talk (or just sleep if not streaming.) + bServiceIPC = talkerDoStream(pTLState); + + // TalkerDoStream() returns TRUE once per second, + // so that we can service our IPC at that low rate. + if (bServiceIPC) { + // Look for messages from endpoint. Don't block (timeout=0) + if (!openavbEptClntService(pTLState->endpointHandle, 0)) { + AVB_LOGF_WARNING("Lost connection to endpoint, will retry "STREAMID_FORMAT, STREAMID_ARGS(&(((talker_data_t *)pTLState->pPvtTalkerData)->streamID))); + pTLState->bConnected = FALSE; + pTLState->endpointHandle = 0; + } + } + } + + // Stop streaming + talkerStopStream(pTLState); + + { + MUTEX_CREATE_ERR(); + MUTEX_DESTROY(pTLState->statsMutex); // Destroy Stats Mutex + MUTEX_LOG_ERR("Error destroying mutex"); + } + + // withdraw our talker registration + if (pTLState->bConnected) + openavbEptClntStopStream(pTLState->endpointHandle, &(((talker_data_t *)pTLState->pPvtTalkerData)->streamID)); + + openavbTLRunTalkerFinish(pTLState); + } + else { + AVB_LOGF_WARNING("Failed to connect to endpoint"STREAMID_FORMAT, STREAMID_ARGS(&(((talker_data_t *)pTLState->pPvtTalkerData)->streamID))); + } + + if (pTLState->pPvtTalkerData) { + free(pTLState->pPvtTalkerData); + pTLState->pPvtTalkerData = NULL; + } + + AVB_TRACE_EXIT(AVB_TRACE_TL); +} + +void openavbTLPauseTalker(tl_state_t *pTLState, bool bPause) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + talker_data_t *pTalkerData = pTLState->pPvtTalkerData; + if (!pTalkerData) { + AVB_LOG_ERROR("Invalid private talker data"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + openavbAvtpPause(pTalkerData->avtpHandle, bPause); + + AVB_TRACE_EXIT(AVB_TRACE_TL); +} + +void openavbTalkerClearStats(tl_state_t *pTLState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + talker_data_t *pTalkerData = pTLState->pPvtTalkerData; + if (!pTalkerData) { + AVB_LOG_ERROR("Invalid private talker data"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + LOCK_STATS(); + memset(&pTalkerData->stats, 0, sizeof(pTalkerData->stats)); + UNLOCK_STATS(); + + AVB_TRACE_EXIT(AVB_TRACE_TL); +} + +void openavbTalkerAddStat(tl_state_t *pTLState, tl_stat_t stat, U64 val) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + talker_data_t *pTalkerData = pTLState->pPvtTalkerData; + if (!pTalkerData) { + AVB_LOG_ERROR("Invalid private talker data"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return; + } + + LOCK_STATS(); + switch (stat) { + case TL_STAT_TX_CALLS: + pTalkerData->stats.totalCalls += val; + break; + case TL_STAT_TX_FRAMES: + pTalkerData->stats.totalFrames += val; + break; + case TL_STAT_TX_LATE: + pTalkerData->stats.totalLate += val; + break; + case TL_STAT_TX_BYTES: + pTalkerData->stats.totalBytes += val; + break; + case TL_STAT_RX_CALLS: + case TL_STAT_RX_FRAMES: + case TL_STAT_RX_LOST: + case TL_STAT_RX_BYTES: + break; + } + UNLOCK_STATS(); + + AVB_TRACE_EXIT(AVB_TRACE_TL); +} + +U64 openavbTalkerGetStat(tl_state_t *pTLState, tl_stat_t stat) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + U64 val = 0; + + if (!pTLState) { + AVB_LOG_ERROR("Invalid TLState"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return 0; + } + + talker_data_t *pTalkerData = pTLState->pPvtTalkerData; + if (!pTalkerData) { + AVB_LOG_ERROR("Invalid private talker data"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return 0; + } + + LOCK_STATS(); + switch (stat) { + case TL_STAT_TX_CALLS: + val = pTalkerData->stats.totalCalls; + break; + case TL_STAT_TX_FRAMES: + val = pTalkerData->stats.totalFrames; + break; + case TL_STAT_TX_LATE: + val = pTalkerData->stats.totalLate; + break; + case TL_STAT_TX_BYTES: + val = pTalkerData->stats.totalBytes; + break; + case TL_STAT_RX_CALLS: + case TL_STAT_RX_FRAMES: + case TL_STAT_RX_LOST: + case TL_STAT_RX_BYTES: + break; + } + UNLOCK_STATS(); + + AVB_TRACE_EXIT(AVB_TRACE_TL); + return val; +} + diff --git a/lib/avtp_pipeline/tl/openavb_talker.h b/lib/avtp_pipeline/tl/openavb_talker.h new file mode 100644 index 00000000..253377ac --- /dev/null +++ b/lib/avtp_pipeline/tl/openavb_talker.h @@ -0,0 +1,77 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Talker Listener process lifecycle interface +*/ + +#ifndef OPENAVB_TL_TALKER_H +#define OPENAVB_TL_TALKER_H 1 + +#include "openavb_tl.h" + +typedef struct { + // Data from callback + char ifname[IFNAMSIZ]; + AVBStreamID_t streamID; + U8 destAddr[ETH_ALEN]; + AVBTSpec_t tSpec; + U32 classRate; + U32 fwmark; + U16 vlanID; + U8 vlanPCP; + + // State info for streaming + void *avtpHandle; + unsigned long sleepUsec; + unsigned long wakeRate; + unsigned long wakeFrames; + unsigned long wakesPerReport; + unsigned long cntWakes; + unsigned long cntFrames; + U64 nextCycleNS; + U64 intervalNS; + U64 nextReportNS; + U64 nextSecondNS; + talker_stats_t stats; +} talker_data_t; + + +void openavbTLRunTalker(tl_state_t *pTLState); +void openavbTLPauseTalker(tl_state_t *pTLState, bool bPause); +void openavbTalkerClearStats(tl_state_t *pTLState); +void openavbTalkerAddStat(tl_state_t *pTLState, tl_stat_t stat, U64 val); +U64 openavbTalkerGetStat(tl_state_t *pTLState, tl_stat_t stat); +bool talkerStartStream(tl_state_t *pTLState); +void talkerStopStream(tl_state_t *pTLState); +bool openavbTLRunTalkerInit(tl_state_t *pTLState); +void openavbTLRunTalkerFinish(tl_state_t *pTLState); + +#endif // OPENAVB_TL_TALKER_H diff --git a/lib/avtp_pipeline/tl/openavb_talker_endpoint.c b/lib/avtp_pipeline/tl/openavb_talker_endpoint.c new file mode 100644 index 00000000..b9dd7d34 --- /dev/null +++ b/lib/avtp_pipeline/tl/openavb_talker_endpoint.c @@ -0,0 +1,187 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Talker implementation for use with endpoint +*/ + +#include <stdlib.h> +#include <string.h> +#include "openavb_platform.h" +#include "openavb_trace.h" +#include "openavb_tl.h" +#include "openavb_endpoint.h" +#include "openavb_avtp.h" +#include "openavb_talker.h" +#include "openavb_time.h" + +// DEBUG Uncomment to turn on logging for just this module. +//#define AVB_LOG_ON 1 + +#define AVB_LOG_COMPONENT "Talker" +#include "openavb_log.h" + +/* Talker callback comes from endpoint, to indicate when listeners + * come and go. We may need to start or stop the talker thread. + */ +void openavbEptClntNotifyTlkrOfSrpCb(int endpointHandle, + AVBStreamID_t *streamID, + char *ifname, + U8 destAddr[], + openavbSrpLsnrDeclSubtype_t lsnrDecl, + U32 classRate, + U16 vlanID, + U8 priority, + U16 fwmark) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + tl_state_t *pTLState = TLHandleListGet(endpointHandle); + talker_data_t *pTalkerData = pTLState->pPvtTalkerData; + + if (!pTLState) { + AVB_LOG_WARNING("Unable to get talker from endpoint handle."); + return; + } + + AVB_LOGF_DEBUG("%s streaming=%d, lsnrDecl=%d", __FUNCTION__, pTLState->bStreaming, lsnrDecl); + + if (!pTLState->bStreaming) { + if (lsnrDecl == openavbSrp_LDSt_Ready + || lsnrDecl == openavbSrp_LDSt_Ready_Failed) { + + // Save the data provided by endpoint/SRP + strncpy(pTalkerData->ifname, ifname, IFNAMSIZ); + memcpy(&pTalkerData->streamID, streamID, sizeof(AVBStreamID_t)); + memcpy(&pTalkerData->destAddr, destAddr, ETH_ALEN); + pTalkerData->classRate = classRate; + pTalkerData->vlanID = vlanID; + pTalkerData->vlanPCP = priority; + pTalkerData->fwmark = fwmark; + + // We should start streaming + AVB_LOGF_INFO("Starting stream: "STREAMID_FORMAT, STREAMID_ARGS(streamID)); + talkerStartStream(pTLState); + } + else if (lsnrDecl == openavbSrp_LDSt_Stream_Info) { + // Stream information is available does NOT mean listener is ready. Stream not started yet. + strncpy(pTalkerData->ifname, ifname, IFNAMSIZ); + memcpy(&pTalkerData->streamID, streamID, sizeof(AVBStreamID_t)); + memcpy(&pTalkerData->destAddr, destAddr, ETH_ALEN); + pTalkerData->classRate = classRate; + pTalkerData->vlanID = vlanID; + pTalkerData->vlanPCP = priority; + pTalkerData->fwmark = fwmark; + } + } + else { + if (lsnrDecl != openavbSrp_LDSt_Ready + && lsnrDecl != openavbSrp_LDSt_Ready_Failed) { + // Nobody is listening any more + AVB_LOGF_INFO("Stopping stream: "STREAMID_FORMAT, STREAMID_ARGS(streamID)); + talkerStopStream(pTLState); + } + } + + + AVB_TRACE_EXIT(AVB_TRACE_TL); +} + +bool openavbTLRunTalkerInit(tl_state_t *pTLState) +{ + openavb_tl_cfg_t *pCfg = &pTLState->cfg; + talker_data_t *pTalkerData = pTLState->pPvtTalkerData; + + AVBStreamID_t streamID; + memset(&streamID, 0, sizeof(AVBStreamID_t)); + if (pCfg->stream_addr.mac) + memcpy(streamID.addr, pCfg->stream_addr.mac, ETH_ALEN); + streamID.uniqueID = pCfg->stream_uid; + + unsigned int maxBitrate = 0; + if (pCfg->intf_cb.intf_get_src_bitrate_cb != NULL) { + maxBitrate = pCfg->intf_cb.intf_get_src_bitrate_cb(pTLState->pMediaQ); + } + if (maxBitrate > 0) { + if (pCfg->map_cb.map_set_src_bitrate_cb != NULL) { + pCfg->map_cb.map_set_src_bitrate_cb(pTLState->pMediaQ, maxBitrate); + } + + if (pCfg->map_cb.map_get_max_interval_frames_cb != NULL) { + unsigned int map_intv_frames = pCfg->map_cb.map_get_max_interval_frames_cb(pTLState->pMediaQ, pTLState->cfg.sr_class); + pCfg->max_interval_frames = map_intv_frames > 0 ? map_intv_frames : pCfg->max_interval_frames; + } + } + pTalkerData->tSpec.maxIntervalFrames = pCfg->max_interval_frames; + + // The TSpec frame size is the L2 payload - i.e. no Ethernet headers, VLAN, FCS, etc... + pTalkerData->tSpec.maxFrameSize = pCfg->map_cb.map_max_data_size_cb(pTLState->pMediaQ); + + { + // TODO_OPENAVB : Consider this a placeholder. But it maybe the correct thing to do. + if_info_t ifinfo; + openavbCheckInterface(pTalkerData->ifname, &ifinfo); + + pTalkerData->fwmark = openavbQmgrAddStream((SRClassIdx_t)pCfg->sr_class, + pTalkerData->wakeRate, + pTalkerData->tSpec.maxIntervalFrames, + pTalkerData->tSpec.maxFrameSize); + + if (pTalkerData->fwmark == INVALID_FWMARK) + return FALSE; + } + + + AVB_LOGF_INFO("Register "STREAMID_FORMAT": class: %c frame size: %d frame interval: %d", STREAMID_ARGS(&streamID), AVB_CLASS_LABEL(pCfg->sr_class), pTalkerData->tSpec.maxFrameSize, pTalkerData->tSpec.maxIntervalFrames); + + // Tell endpoint to register our stream. + // SRP will send out talker declarations on the LAN. + // If there are listeners, we'll get callback (above.) + return (openavbEptClntRegisterStream(pTLState->endpointHandle, + &streamID, + pCfg->dest_addr.mac->ether_addr_octet, + &pTalkerData->tSpec, + pCfg->sr_class, + pCfg->sr_rank, + pCfg->internal_latency)); +} + +void openavbTLRunTalkerFinish(tl_state_t *pTLState) +{ + { + // TODO_OPENAVB : Consider this a placeholder. But it maybe the correct thing to do. + talker_data_t *pTalkerData = pTLState->pPvtTalkerData; + + openavbQmgrRemoveStream(pTalkerData->fwmark, + pTalkerData->wakeRate, + pTalkerData->tSpec.maxIntervalFrames, + pTalkerData->tSpec.maxFrameSize); + } +} diff --git a/lib/avtp_pipeline/tl/openavb_talker_no_endpoint.c b/lib/avtp_pipeline/tl/openavb_talker_no_endpoint.c new file mode 100644 index 00000000..a6ad68d7 --- /dev/null +++ b/lib/avtp_pipeline/tl/openavb_talker_no_endpoint.c @@ -0,0 +1,156 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+/*
+* MODULE SUMMARY : Talker implementation for use without endpoint
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "openavb_tl.h"
+#include "openavb_avtp.h"
+#include "openavb_talker.h"
+#include "openavb_qmgr.h"
+#include "openavb_endpoint.h"
+
+#define AVB_LOG_COMPONENT "Talker"
+#include "openavb_log.h"
+#include "openavb_trace.h"
+
+// SR Class default Priority (PCP) values per IEEE 802.1Q-2011 Table 6-6
+#define SR_CLASS_A_DEFAULT_PRIORITY 3
+#define SR_CLASS_B_DEFAULT_PRIORITY 2
+
+// SR Class default VLAN Id values per IEEE 802.1Q-2011 Table 9-2
+#define SR_CLASS_A_DEFAULT_VID 2
+#define SR_CLASS_B_DEFAULT_VID 2
+
+// Returns TRUE, to say we're connected and registers tspec with FQTSS tspec should be initialized
+bool openavbTLRunTalkerInit(tl_state_t *pTLState)
+{
+ openavb_tl_cfg_t *pCfg = &pTLState->cfg;
+ talker_data_t *pTalkerData = pTLState->pPvtTalkerData;
+ //avtp_stream_t *pStream = (avtp_stream_t *)(pTalkerData->avtpHandle);
+
+ strncpy(pTalkerData->ifname, pCfg->ifname, IFNAMSIZ);
+
+ // CORE_TODO: It would be good to have some parts of endpoint moved into non-endpoint general code to handle some the stream
+ // configuration values.
+ // strncpy(pTalkerData->ifname, pCfg->ifname, IFNAMSIZ);
+ if (pCfg->stream_addr.mac) {
+ memcpy(pTalkerData->streamID.addr, pCfg->stream_addr.mac, ETH_ALEN);
+ }else {
+ AVB_LOG_WARNING("Stream Address Not Set");
+ }
+
+ pTalkerData->streamID.uniqueID = pCfg->stream_uid;
+ if (pCfg->sr_class == SR_CLASS_A) {
+ pTalkerData->classRate = 8000;
+ pTalkerData->vlanID = SR_CLASS_A_DEFAULT_VID;
+ pTalkerData->vlanPCP = SR_CLASS_A_DEFAULT_PRIORITY;
+ }
+ else if (pCfg->sr_class == SR_CLASS_B) {
+ pTalkerData->classRate = 4000;
+ pTalkerData->vlanID = SR_CLASS_B_DEFAULT_VID;
+ pTalkerData->vlanPCP = SR_CLASS_B_DEFAULT_PRIORITY;
+ }
+ memcpy(&pTalkerData->destAddr, &pCfg->dest_addr.mac->ether_addr_octet, ETH_ALEN);
+
+ unsigned int maxBitrate = 0;
+ if (pCfg->intf_cb.intf_get_src_bitrate_cb != NULL) {
+ maxBitrate = pCfg->intf_cb.intf_get_src_bitrate_cb(pTLState->pMediaQ);
+ }
+ if (maxBitrate > 0) {
+ if (pCfg->map_cb.map_set_src_bitrate_cb != NULL) {
+ pCfg->map_cb.map_set_src_bitrate_cb(pTLState->pMediaQ, maxBitrate);
+ }
+
+ if (pCfg->map_cb.map_get_max_interval_frames_cb != NULL) {
+ unsigned int map_intv_frames = pCfg->map_cb.map_get_max_interval_frames_cb(pTLState->pMediaQ, pTLState->cfg.sr_class);
+ pCfg->max_interval_frames = map_intv_frames > 0 ? map_intv_frames : pCfg->max_interval_frames;
+ }
+ }
+ pTalkerData->tSpec.maxIntervalFrames = pCfg->max_interval_frames;
+ pTalkerData->tSpec.maxFrameSize = pCfg->map_cb.map_max_data_size_cb(pTLState->pMediaQ);
+
+ // TODO_COREAVB : This wakeRate should also be set in the endpoint case and removed from the tasker.c start stream
+ if (!pCfg->map_cb.map_transmit_interval_cb(pTLState->pMediaQ)) {
+ pTalkerData->wakeRate = pTalkerData->classRate / pCfg->batch_factor;
+ }
+ else {
+ // Override the class observation interval with the one provided by the mapping module.
+ pTalkerData->wakeRate = pCfg->map_cb.map_transmit_interval_cb(pTLState->pMediaQ) / pCfg->batch_factor;
+ }
+
+ if_info_t ifinfo;
+ openavbCheckInterface(pTalkerData->ifname, &ifinfo);
+
+ pTalkerData->fwmark = openavbQmgrAddStream((SRClassIdx_t)pCfg->sr_class,
+ pTalkerData->wakeRate,
+ pTalkerData->tSpec.maxIntervalFrames,
+ pTalkerData->tSpec.maxFrameSize);
+
+ if (pTalkerData->fwmark == INVALID_FWMARK)
+ return FALSE;
+
+ AVB_LOGF_INFO("Dest Addr: "ETH_FORMAT, ETH_OCTETS(pTalkerData->destAddr));
+ AVB_LOGF_INFO("Starting stream: "STREAMID_FORMAT, STREAMID_ARGS(&pTalkerData->streamID));
+ talkerStartStream(pTLState);
+
+ return TRUE;
+}
+
+void openavbTLRunTalkerFinish(tl_state_t *pTLState)
+{
+ {
+ // TODO_OPENAVB : Consider this a placeholder. But it maybe the correct thing to do.
+ talker_data_t *pTalkerData = pTLState->pPvtTalkerData;
+
+ openavbQmgrRemoveStream(pTalkerData->fwmark,
+ pTalkerData->wakeRate,
+ pTalkerData->tSpec.maxIntervalFrames,
+ pTalkerData->tSpec.maxFrameSize);
+ }
+}
+
+void openavbEptClntNotifyTlkrOfSrpCb(
+int endpointHandle,
+AVBStreamID_t *streamID,
+char *ifname,
+U8 destAddr[],
+openavbSrpLsnrDeclSubtype_t lsnrDecl,
+U32 classRate,
+U16 vlanID,
+U8 priority,
+U16 fwmark)
+{
+}
+
diff --git a/lib/avtp_pipeline/tl/openavb_tl.c b/lib/avtp_pipeline/tl/openavb_tl.c new file mode 100755 index 00000000..68267785 --- /dev/null +++ b/lib/avtp_pipeline/tl/openavb_tl.c @@ -0,0 +1,733 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+/*
+* MODULE SUMMARY : Common implementation for the talker and listener
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "openavb_tl.h"
+#include "openavb_trace.h"
+#include "openavb_mediaq.h"
+#include "openavb_talker.h"
+#include "openavb_listener.h"
+// #include "openavb_avtp.h"
+#include "openavb_platform.h"
+
+#define AVB_LOG_COMPONENT "Talker / Listener"
+#include "openavb_pub.h"
+#include "openavb_log.h"
+
+U32 gMaxTL;
+tl_handle_t *gTLHandleList;
+
+// We are accessed from multiple threads, so need a mutex
+MUTEX_HANDLE(gTLStateMutex);
+
+#define MATCH(A, B)(strcasecmp((A), (B)) == 0)
+//#define MATCH_LEFT(A, B, C)(strncasecmp((A), (B), (C)) == 0)
+#define MATCH_LEFT(A, B, C) (memcmp((A), (B), (C)) == 0)
+
+THREAD_TYPE(listenerThread);
+THREAD_TYPE(talkerThread);
+
+void* openavbTLThreadFn(void *pv);
+#define THREAD_CREATE_TALKER() THREAD_CREATE(talkerThread, pTLState->TLThread, NULL, openavbTLThreadFn, pTLState)
+#define THREAD_CREATE_LISTENER() THREAD_CREATE(listenerThread, pTLState->TLThread, NULL, openavbTLThreadFn, pTLState)
+
+void timespec_add_usec(struct timespec *t, unsigned long us)
+{
+ t->tv_nsec += us * NANOSECONDS_PER_USEC;
+ t->tv_sec += t->tv_nsec / NANOSECONDS_PER_SECOND;
+ t->tv_nsec = t->tv_nsec % NANOSECONDS_PER_SECOND;
+}
+
+void timespec_sub_usec(struct timespec *t, unsigned long us)
+{
+ t->tv_nsec -= us * NANOSECONDS_PER_USEC;
+ t->tv_sec += t->tv_nsec / NANOSECONDS_PER_SECOND;
+ t->tv_nsec = t->tv_nsec % NANOSECONDS_PER_SECOND;
+ if (t->tv_nsec < 0) {
+ t->tv_sec--;
+ t->tv_nsec = NANOSECONDS_PER_SECOND + t->tv_nsec;
+ }
+}
+
+unsigned long timespec_usec_diff(struct timespec *t1, struct timespec *t2)
+{
+ return (t1->tv_sec - t2->tv_sec) * MICROSECONDS_PER_SECOND
+ + (t1->tv_nsec - t2->tv_nsec) / NANOSECONDS_PER_USEC;
+}
+
+int timespec_cmp(struct timespec *a, struct timespec *b)
+{
+ if (a->tv_sec > b->tv_sec)
+ return 1;
+ else if (a->tv_sec < b->tv_sec)
+ return -1;
+ else {
+ if (a->tv_nsec > b->tv_nsec)
+ return 1;
+ else if (a->tv_nsec < b->tv_nsec)
+ return -1;
+ }
+ return 0;
+}
+
+static bool TLHandleListAdd(tl_handle_t handle)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ if (!handle || !gTLHandleList) {
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return FALSE;
+ }
+
+ TL_LOCK();
+ int i1;
+ for (i1 = 0; i1 < gMaxTL; i1++) {
+ if (!gTLHandleList[i1]) {
+ gTLHandleList[i1] = handle;
+ TL_UNLOCK();
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return TRUE;
+ }
+ }
+ TL_UNLOCK();
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return FALSE;
+}
+
+bool TLHandleListRemove(tl_handle_t handle)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ if (!handle || !gTLHandleList) {
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return FALSE;
+ }
+
+ TL_LOCK();
+ int i1;
+ for (i1 = 0; i1 < gMaxTL; i1++) {
+ if (gTLHandleList[i1] == handle) {
+ gTLHandleList[i1] = NULL;
+ TL_UNLOCK();
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return TRUE;
+ }
+ }
+ TL_UNLOCK();
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return FALSE;
+}
+
+static bool checkIntfCallbacks(openavb_tl_cfg_t *pCfg)
+{
+ bool validCfg = TRUE;
+
+ if (!pCfg->intf_cb.intf_cfg_cb) {
+ AVB_LOG_WARNING("INI file doesn't specify inferface callback for '_cfg'.");
+ // validCfg = FALSE;
+ }
+ if ((pCfg->role == AVB_ROLE_TALKER) && !pCfg->intf_cb.intf_tx_init_cb) {
+ AVB_LOG_WARNING("INI file doesn't specify inferface callback for '_tx_init'.");
+ // validCfg = FALSE;
+ }
+ if ((pCfg->role == AVB_ROLE_TALKER) && !pCfg->intf_cb.intf_tx_cb) {
+ AVB_LOG_ERROR("INI file doesn't specify inferface callback for '_tx'.");
+ validCfg = FALSE;
+ }
+ if ((pCfg->role == AVB_ROLE_LISTENER) && !pCfg->intf_cb.intf_rx_init_cb) {
+ AVB_LOG_WARNING("INI file doesn't specify inferface callback for '_rx_init'.");
+ // validCfg = FALSE;
+ }
+ if ((pCfg->role == AVB_ROLE_LISTENER) && !pCfg->intf_cb.intf_rx_cb) {
+ AVB_LOG_ERROR("INI file doesn't specify inferface callback for '_rx'.");
+ validCfg = FALSE;
+ }
+ if (!pCfg->intf_cb.intf_end_cb) {
+ AVB_LOG_WARNING("INI file doesn't specify inferface callback for '_end'.");
+ // validCfg = FALSE;
+ }
+ if (!pCfg->intf_cb.intf_gen_init_cb) {
+ AVB_LOG_WARNING("INI file doesn't specify inferface callback for '_gen_init'.");
+ // validCfg = FALSE;
+ }
+ if (!pCfg->intf_cb.intf_gen_end_cb) {
+ AVB_LOG_WARNING("INI file doesn't specify inferface callback for '_gen_end'.");
+ // validCfg = FALSE;
+ }
+ if (!pCfg->intf_cb.intf_avdecc_init_cb) {
+ // Optional callback
+ // CORE_TODO: AVDECC not formally supported yet.
+ // AVB_LOG_WARNING("INI file doesn't specify inferface callback for '_avdecc_init'.");
+ // validCfg = FALSE;
+ }
+
+ return validCfg;
+}
+
+static bool checkMapCallbacks(openavb_tl_cfg_t *pCfg)
+{
+ bool validCfg = TRUE;
+
+ if (!pCfg->map_cb.map_cfg_cb) {
+ AVB_LOG_WARNING("INI doesn't specify mapping callback for '_cfg'.");
+ // validCfg = FALSE;
+ }
+ if (!pCfg->map_cb.map_subtype_cb) {
+ AVB_LOG_WARNING("INI doesn't specify mapping callback for '_subtype'.");
+ // validCfg = FALSE;
+ }
+ if (!pCfg->map_cb.map_avtp_version_cb) {
+ AVB_LOG_WARNING("INI doesn't specify mapping callback for '_avtp_version'.");
+ // validCfg = FALSE;
+ }
+ if ((pCfg->role == AVB_ROLE_TALKER) && !pCfg->map_cb.map_tx_init_cb) {
+ AVB_LOG_WARNING("INI doesn't specify mapping callback for '_tx_init'.");
+ // validCfg = FALSE;
+ }
+ if ((pCfg->role == AVB_ROLE_TALKER) && !pCfg->map_cb.map_tx_cb) {
+ AVB_LOG_ERROR("INI doesn't specify mapping callback for '_tx'.");
+ validCfg = FALSE;
+ }
+ if ((pCfg->role == AVB_ROLE_LISTENER) && !pCfg->map_cb.map_rx_init_cb) {
+ AVB_LOG_WARNING("INI doesn't specify mapping callback for '_rx_init'.");
+ // validCfg = FALSE;
+ }
+ if ((pCfg->role == AVB_ROLE_LISTENER) && !pCfg->map_cb.map_rx_cb) {
+ AVB_LOG_ERROR("INI doesn't specify mapping callback for '_rx'.");
+ validCfg = FALSE;
+ }
+ if (!pCfg->map_cb.map_end_cb) {
+ AVB_LOG_WARNING("INI doesn't specify mapping callback for '_end'.");
+ // validCfg = FALSE;
+ }
+ if (!pCfg->map_cb.map_gen_init_cb) {
+ AVB_LOG_WARNING("INI doesn't specify mapping callback for '_gen_init'.");
+ // validCfg = FALSE;
+ }
+ if (!pCfg->map_cb.map_gen_end_cb) {
+ AVB_LOG_WARNING("INI doesn't specify mapping callback for '_gen_end'.");
+ // validCfg = FALSE;
+ }
+ if (!pCfg->map_cb.map_avdecc_init_cb) {
+ // Optional callback
+ // CORE_TODO: AVDECC not formally supported yet.
+ // AVB_LOG_WARNING("INI doesn't specify mapping callback for '_avdecc_init'.");
+ // validCfg = FALSE;
+ }
+
+ return validCfg;
+}
+
+void openavbTLUnconfigure(tl_state_t *pTLState)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ // CORE_TODO: Disable this functionality until updated to properly handle distinction between values point to static const char
+ // and dynamically allocated strings.
+#if 0
+ openavb_tl_cfg_t *pCfg = &pTLState->cfg;
+
+ int i1;
+ for (i1 = 0; i1 < pCfg->nLibCfgItems; i1++) {
+ if (pCfg->libCfgNames[i1])
+ free(pCfg->libCfgNames[i1]);
+ if (pNVCfg->libCfgValues[i1])
+ free(pNVCfg->libCfgValues[i1]);
+ pCfg->libCfgNames[i1] = pNVCfg->libCfgValues[i1] = NULL;
+ }
+ pCfg->nLibCfgItems = 0;
+#endif
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+}
+
+
+/* Public APIs
+ */
+// General initizlization of the talker and listener library. Must be called prior to using any other TL APIs.
+EXTERN_DLL_EXPORT bool openavbTLInitialize(U32 maxTL)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+ LOG_EAVB_CORE_VERSION();
+
+ // CORE_TODO : This can be used to AVB stack init functionality once such a thing exists in the reference implementation
+ AVB_TIME_INIT();
+
+ gMaxTL = maxTL;
+
+ {
+ MUTEX_ATTR_HANDLE(mta);
+ MUTEX_ATTR_INIT(mta);
+ MUTEX_ATTR_SET_TYPE(mta, MUTEX_ATTR_TYPE_DEFAULT);
+ MUTEX_ATTR_SET_NAME(mta, "gTLStateMutex");
+ MUTEX_CREATE_ERR();
+ MUTEX_CREATE(gTLStateMutex, mta);
+ MUTEX_LOG_ERR("Error creating mutex");
+ }
+
+ gTLHandleList = calloc(1, sizeof(tl_handle_t) * gMaxTL);
+ if (gTLHandleList) {
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return TRUE;
+ }
+ else {
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return FALSE;
+ }
+}
+
+// General cleanup of the talker and listener library. Should be called after all Talker and Listeners are closed.
+EXTERN_DLL_EXPORT bool openavbTLCleanup()
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+ if (gTLHandleList) {
+ free(gTLHandleList);
+ gTLHandleList = NULL;
+ }
+ else {
+ return FALSE;
+ }
+
+ {
+ MUTEX_CREATE_ERR();
+ MUTEX_DESTROY(gTLStateMutex);
+ MUTEX_LOG_ERR("Error destroying mutex");
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return TRUE;
+}
+
+EXTERN_DLL_EXPORT bool openavbGetVersion(U8 *major, U8 *minor, U8 *revision)
+{
+ if (!major || !minor || !revision) {
+ return FALSE;
+ }
+
+ *major = AVB_CORE_VER_MAJOR;
+ *minor = AVB_CORE_VER_MINOR;
+ *revision = AVB_CORE_VER_REVISION;
+ return TRUE;
+}
+
+EXTERN_DLL_EXPORT tl_handle_t openavbTLOpen(void)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ tl_state_t *pTLState = calloc(1, sizeof(tl_state_t));
+
+ if (!pTLState) {
+ AVB_LOG_ERROR("Unable to allocate talker listener state data.");
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return NULL;
+ }
+
+ if (!TLHandleListAdd(pTLState)) {
+ AVB_LOG_ERROR("To many talker listeners open.");
+ free(pTLState);
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return NULL;
+ }
+
+ return pTLState;
+}
+
+EXTERN_DLL_EXPORT void openavbTLInitCfg(openavb_tl_cfg_t *pCfg)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ memset(pCfg, 0, sizeof(openavb_tl_cfg_t));
+
+ // Set default values.
+ pCfg->role = AVB_ROLE_UNDEFINED;
+ //pCfg->map_cb;
+ //pCfg->intf_cb;
+ //pCfg->dest_addr;
+ //pCfg->stream_addr;
+ pCfg->stream_uid = -1;
+ pCfg->max_interval_frames = 1;
+ pCfg->max_frame_size = 1500;
+ pCfg->max_transit_usec = 50000;
+ pCfg->max_transmit_deficit_usec = 50000;
+ pCfg->internal_latency = 0;
+ pCfg->max_stale = MICROSECONDS_PER_SECOND;
+ pCfg->batch_factor = 1;
+ pCfg->report_seconds = 0;
+ pCfg->start_paused = FALSE;
+ pCfg->sr_class = SR_CLASS_B;
+ pCfg->sr_rank = SR_RANK_REGULAR;
+ pCfg->raw_tx_buffers = 8;
+ pCfg->raw_rx_buffers = 100;
+ pCfg->tx_blocking_in_intf = 0;
+ pCfg->rx_signal_mode = 1;
+ pCfg->pMapInitFn = NULL;
+ pCfg->pIntfInitFn = NULL;
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+}
+
+EXTERN_DLL_EXPORT bool openavbTLConfigure(tl_handle_t handle, openavb_tl_cfg_t *pCfgIn, openavb_tl_cfg_name_value_t *pNVCfg)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ tl_state_t *pTLState = (tl_state_t *)handle;
+
+ if (!pTLState) {
+ AVB_LOG_ERROR("Invalid handle.");
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return FALSE;
+ }
+
+ // Create the mediaQ
+ pTLState->pMediaQ = openavbMediaQCreate();
+ if (!pTLState->pMediaQ) {
+ AVB_LOG_ERROR("Unable to create media queue");
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return FALSE;
+ }
+
+ // CORE_TODO: It's not safe to simply copy the openavb_tl_cfg_t since there are embedded pointers in the cfg_mac_t member.
+ // Those pointers need to be updated after a copy. Longer term the cfg_mac_t should be changed to not contain the mac
+ // member to remedy this issue and avoid further bugs.
+ memcpy(&pTLState->cfg, pCfgIn, sizeof(openavb_tl_cfg_t));
+ pTLState->cfg.dest_addr.mac = &pTLState->cfg.dest_addr.buffer;
+ pTLState->cfg.stream_addr.mac = &pTLState->cfg.stream_addr.buffer;
+
+ openavb_tl_cfg_t *pCfg = &pTLState->cfg;
+
+ if (!((pCfg->role == AVB_ROLE_TALKER) || (pCfg->role == AVB_ROLE_LISTENER))) {
+ AVB_LOG_ERROR("Talker - Listener Config Error: invalid role");
+ return FALSE;
+ }
+
+ if ((pCfg->role == AVB_ROLE_TALKER) && (pCfg->max_interval_frames == 0)) {
+ AVB_LOG_ERROR("Talker - Listener Config Error: talker role requires 'max_interval_frames'");
+ return FALSE;
+ }
+
+ openavbMediaQSetMaxStaleTail(pTLState->pMediaQ, pCfg->max_stale);
+
+ if (!openavbTLOpenLinkLibsOsal(pTLState)) {
+ AVB_LOG_ERROR("Failed to open mapping / interface library");
+ return FALSE;
+ }
+
+ if (pCfg->pMapInitFn && pCfg->pMapInitFn(pTLState->pMediaQ, &pCfg->map_cb, pCfg->max_transit_usec)) {
+ checkMapCallbacks(&pTLState->cfg);
+ }
+ else {
+ AVB_LOG_ERROR("Mapping initialize function error.");
+ return FALSE;
+ }
+
+ if (pCfg->pIntfInitFn && pCfg->pIntfInitFn(pTLState->pMediaQ, &pCfg->intf_cb)) {
+ checkIntfCallbacks(&pTLState->cfg);
+ }
+ else {
+ AVB_LOG_ERROR("Interface initialize function error.");
+ return FALSE;
+ }
+
+ // Submit configuration values to mapping and interface modules
+ int i;
+ for (i = 0; i < pNVCfg->nLibCfgItems; i++) {
+ if (MATCH_LEFT(pNVCfg->libCfgNames[i], "intf_nv_", 8)) {
+ if (pCfg->intf_cb.intf_cfg_cb) {
+ pCfg->intf_cb.intf_cfg_cb(pTLState->pMediaQ, pNVCfg->libCfgNames[i], pNVCfg->libCfgValues[i]);
+ }
+ else {
+ AVB_LOGF_ERROR("No interface module cfg function; ignoring %s", pNVCfg->libCfgNames[i]);
+ }
+ }
+ else if (MATCH_LEFT(pNVCfg->libCfgNames[i], "map_nv_", 7)) {
+ if (pCfg->map_cb.map_cfg_cb) {
+ pCfg->map_cb.map_cfg_cb(pTLState->pMediaQ, pNVCfg->libCfgNames[i], pNVCfg->libCfgValues[i]);
+ }
+ else {
+ AVB_LOGF_ERROR("No mapping module cfg function; ignoring %s", pNVCfg->libCfgNames[i]);
+ }
+ }
+ else {
+ assert(0);
+ }
+ } // for loop ends
+
+ pTLState->cfg.map_cb.map_gen_init_cb(pTLState->pMediaQ);
+ pTLState->cfg.intf_cb.intf_gen_init_cb(pTLState->pMediaQ);
+
+ return TRUE;
+}
+
+EXTERN_DLL_EXPORT bool openavbTLRun(tl_handle_t handle)
+{
+ bool retVal = FALSE;
+
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ tl_state_t *pTLState = (tl_state_t *)handle;
+
+ do {
+ if (!pTLState) {
+ AVB_LOG_ERROR("Invalid handle.");
+ break;
+ }
+
+ pTLState->bRunning = TRUE;
+ if (pTLState->cfg.role == AVB_ROLE_TALKER) {
+ THREAD_CREATE_TALKER();
+ }
+ else if (pTLState->cfg.role == AVB_ROLE_LISTENER) {
+ THREAD_CREATE_LISTENER();
+ }
+
+ retVal = TRUE;
+
+ } while (0);
+
+
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return retVal;
+}
+
+extern DLL_EXPORT bool openavbTLStop(tl_handle_t handle)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ tl_state_t *pTLState = (tl_state_t *)handle;
+
+ if (!pTLState) {
+ AVB_LOG_ERROR("Invalid handle.");
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return FALSE;
+ }
+
+ if (pTLState->bRunning) {
+ // don't set bStreaming to false here, that's needed to track
+ // that the streaming thread is running, so we can shut it down.
+ //pTLState->bStreaming = FALSE;
+ pTLState->bRunning = FALSE;
+
+ THREAD_JOIN(pTLState->TLThread, NULL);
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return TRUE;
+}
+
+EXTERN_DLL_EXPORT bool openavbTLClose(tl_handle_t handle)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ tl_state_t *pTLState = (tl_state_t *)handle;
+
+ if (!pTLState) {
+ AVB_LOG_ERROR("Invalid handle.");
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return FALSE;
+ }
+
+ if (pTLState->bRunning == TRUE) {
+ // In case openavbTLStop wasn't called stop is now.
+ openavbTLStop(handle);
+ }
+
+ pTLState->cfg.intf_cb.intf_gen_end_cb(pTLState->pMediaQ);
+ pTLState->cfg.map_cb.map_gen_end_cb(pTLState->pMediaQ);
+
+ TLHandleListRemove(handle);
+
+ openavbTLUnconfigure(pTLState);
+ openavbTLCloseLinkLibsOsal(pTLState);
+
+ if (pTLState->pMediaQ) {
+ openavbMediaQDelete(pTLState->pMediaQ);
+ pTLState->pMediaQ = NULL;
+ }
+
+ // Free TLState
+ free(pTLState);
+ pTLState = NULL;
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return TRUE;
+}
+
+EXTERN_DLL_EXPORT void* openavbTLGetIntfHostCBList(tl_handle_t handle)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ tl_state_t *pTLState = (tl_state_t *)handle;
+
+ if (!pTLState) {
+ AVB_LOG_ERROR("Invalid handle.");
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return FALSE;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return pTLState->cfg.intf_cb.intf_host_cb_list;
+}
+
+EXTERN_DLL_EXPORT void* openavbTLGetIntfHandle(tl_handle_t handle)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ tl_state_t *pTLState = (tl_state_t *)handle;
+
+ if (!pTLState) {
+ AVB_LOG_ERROR("Invalid handle.");
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return FALSE;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return pTLState->pMediaQ;
+}
+
+EXTERN_DLL_EXPORT bool openavbTLIsRunning(tl_handle_t handle)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ tl_state_t *pTLState = (tl_state_t *)handle;
+
+ if (!pTLState) {
+ AVB_LOG_ERROR("Invalid handle.");
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return FALSE;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return pTLState->bRunning;
+}
+
+EXTERN_DLL_EXPORT bool openavbTLIsConnected(tl_handle_t handle)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ tl_state_t *pTLState = (tl_state_t *)handle;
+
+ if (!pTLState) {
+ AVB_LOG_ERROR("Invalid handle.");
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return FALSE;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return pTLState->bConnected;
+}
+
+EXTERN_DLL_EXPORT bool openavbTLIsStreaming(tl_handle_t handle)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ tl_state_t *pTLState = (tl_state_t *)handle;
+
+ if (!pTLState) {
+ AVB_LOG_ERROR("Invalid handle.");
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return FALSE;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return pTLState->bStreaming;
+}
+
+EXTERN_DLL_EXPORT avb_role_t openavbTLGetRole(tl_handle_t handle)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ tl_state_t *pTLState = (tl_state_t *)handle;
+
+ if (!pTLState) {
+ AVB_LOG_ERROR("Invalid handle");
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return AVB_ROLE_UNDEFINED;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return pTLState->cfg.role;
+}
+
+
+EXTERN_DLL_EXPORT U64 openavbTLStat(tl_handle_t handle, tl_stat_t stat)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+ U64 val = 0;
+
+ tl_state_t *pTLState = (tl_state_t *)handle;
+
+ if (!pTLState) {
+ AVB_LOG_ERROR("Invalid handle");
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return 0;
+ }
+
+ if (pTLState->cfg.role == AVB_ROLE_TALKER) {
+ val = openavbTalkerGetStat(pTLState, stat);
+ }
+ else if (pTLState->cfg.role == AVB_ROLE_LISTENER) {
+ val = openavbListenerGetStat(pTLState, stat);
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return val;
+}
+
+EXTERN_DLL_EXPORT void openavbTLPauseStream(tl_handle_t handle, bool bPause)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ tl_state_t *pTLState = (tl_state_t *)handle;
+
+ if (!pTLState) {
+ AVB_LOG_ERROR("Invalid handle.");
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return;
+ }
+
+ if (pTLState->cfg.role == AVB_ROLE_TALKER) {
+ openavbTLPauseTalker(pTLState, bPause);
+ }
+ else if (pTLState->cfg.role == AVB_ROLE_LISTENER) {
+ openavbTLPauseListener(pTLState, bPause);
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+}
+
+
+
diff --git a/lib/avtp_pipeline/tl/openavb_tl.h b/lib/avtp_pipeline/tl/openavb_tl.h new file mode 100755 index 00000000..8cbcdd5f --- /dev/null +++ b/lib/avtp_pipeline/tl/openavb_tl.h @@ -0,0 +1,164 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Talker Listener header +*/ + +#ifndef OPENAVB_TL_H +#define OPENAVB_TL_H 1 + +#include "openavb_types.h" +#include "openavb_osal.h" +#include "openavb_mediaq_pub.h" +#include "openavb_tl_pub.h" + +typedef enum OPENAVB_TL_AVB_VER_STATE +{ + OPENAVB_TL_AVB_VER_UNKNOWN = 0, + OPENAVB_TL_AVB_VER_INVALID, + OPENAVB_TL_AVB_VER_VALID, +} openavbTLAVBVerState_t; + +typedef struct { + U64 totalCalls; + U64 totalFrames; + U64 totalLate; + U64 totalBytes; +} talker_stats_t; + +THREAD_TYPE(TLThread); + +typedef struct { + // Running flag. (assumed atomic) + bool bRunning; + + // Connected to endpoint flag. (assumed atomic) + bool bConnected; + + // Streaming data flag. (assumed atomic) + bool bStreaming; + + // The status of the version check to make sure endpoint and TL are running the same version. + openavbTLAVBVerState_t AVBVerState; + + // Configuration settings. (Values are set once from a single thread no lock needed). + openavb_tl_cfg_t cfg; + + // Handle to the endpoint. (Set once from a single thread no lock needed.) + int endpointHandle; + + // Media queue struct. + media_q_t *pMediaQ; + + // Private talker data. + void *pPvtTalkerData; + + // Private listener data. + void *pPvtListenerData; + + // Thread for talker or listener + THREAD_DEFINITON(TLThread); + + // Per stream Stats Mutex + MUTEX_HANDLE(statsMutex); + + LINK_LIB(mapLib); + + LINK_LIB(intfLib); +} tl_state_t; + +// Clock that we use for all timers in TL +//#define TIMER_CLOCK CLOCK_MONOTONIC + +//////////////// +// TL state mutex +//////////////// +extern MUTEX_HANDLE(gTLStateMutex); +#define TL_LOCK() { MUTEX_CREATE_ERR(); MUTEX_LOCK(gTLStateMutex); MUTEX_LOG_ERR("Mutex lock failure"); } +#define TL_UNLOCK() { MUTEX_CREATE_ERR(); MUTEX_UNLOCK(gTLStateMutex); MUTEX_LOG_ERR("Mutex unlock failure"); } + +//////////////// +// TL stats mutex +//////////////// +#define LOCK_STATS() { MUTEX_CREATE_ERR(); MUTEX_LOCK(pTLState->statsMutex); MUTEX_LOG_ERR("Mutex lock failure"); } +#define UNLOCK_STATS() { MUTEX_CREATE_ERR(); MUTEX_UNLOCK(pTLState->statsMutex); MUTEX_LOG_ERR("Mutex unlock failure"); } + +//////////////// +// timespec support functions +//////////////// +void timespec_add_usec(struct timespec *t, unsigned long us); +void timespec_sub_usec(struct timespec *t, unsigned long us); +unsigned long timespec_usec_diff(struct timespec *t1, struct timespec *t2); +int timespec_cmp(struct timespec *a, struct timespec *b); + +//////////////// +// TL Handle List functions. The TL handle list is created in the implemntation of openavbTLInitialize. +//////////////// +// Get a tl_handle_t from the TLHandleList given an endpointHandle. +extern U32 gMaxTL; +extern tl_handle_t *gTLHandleList; +tl_handle_t TLHandleListGet(int endpointHandle); + +bool TLHandleListRemove(tl_handle_t handle); +void openavbTLUnconfigure(tl_state_t *pTLState); + +// Remove a tl_handle_t from the TL handle list. +bool TLHandleRemove(tl_handle_t handle); + + +//////////////// +// AVDECC Integration functions. +//////////////// +// Run a single talker or listener. At this point data can be sent or recieved. Used in place of the public openavbTLRun +bool openavbTLAVDECCRunListener(tl_handle_t handle, U16 configIdx, U16 descriptorType, U16 descriptorIdx, void *pVoidListenerStreamInfo); +bool openavbTLAVDECCRunTalker(tl_handle_t handle, U16 configIdx, U16 descriptorType, U16 descriptorIdx, void *pVoidTalkerStreamInfo); + +// Stop a single talker or listener. At this point data will not be sent or recieved. Used in place of the public openavbTLStop. +bool openavbTLAVDECCStopListener(tl_handle_t handle, U16 configIdx, void *pVoidListenerStreamInfo); +bool openavbTLAVDECCStopTalker(tl_handle_t handle, U16 configIdx, void *pVoidTalkerStreamInfo); + +// Get talker stream details. Structure members in TalkerStrreamInfo will be filled. +bool openavbTLAVDECCGetTalkerStreamInfo(tl_handle_t handle, U16 configIdx, void *pVoidTalkerStreamInfo); + + +//////////////// +// OSAL implementation functions +//////////////// +bool openavbTLThreadFnOsal(tl_state_t *pTLState); +bool openavbTLOpenLinkLibsOsal(tl_state_t *pTLState); +bool openavbTLCloseLinkLibsOsal(tl_state_t *pTLState); + +/* These were in openavb_endpoint.h, but was moved here + * for implementations that do not have endpoint */ +bool openavbEptClntService(int h, int timeout); +bool openavbEptClntStopStream(int h, AVBStreamID_t *streamID); + +#endif // OPENAVB_TL_H diff --git a/lib/avtp_pipeline/tl/openavb_tl_endpoint.c b/lib/avtp_pipeline/tl/openavb_tl_endpoint.c new file mode 100644 index 00000000..4c36db34 --- /dev/null +++ b/lib/avtp_pipeline/tl/openavb_tl_endpoint.c @@ -0,0 +1,277 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Common implementation for the talker and listener +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "openavb_tl.h" +#include "openavb_acmp.h" +#include "openavb_trace.h" +#include "openavb_mediaq.h" +#include "openavb_talker.h" +#include "openavb_listener.h" +#include "openavb_endpoint.h" +#include "openavb_avtp.h" +#include "openavb_platform.h" +#include "openavb_endpoint_osal.h" + +#define AVB_LOG_COMPONENT "Talker / Listener" +#include "openavb_pub.h" +#include "openavb_log.h" + +void openavbEptClntCheckVerMatchesSrvr(int endpointHandle, U32 AVBVersion) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + tl_state_t *pTLState = TLHandleListGet(endpointHandle); + + if (AVBVersion == AVB_CORE_VER_FULL) { + pTLState->AVBVerState = OPENAVB_TL_AVB_VER_VALID; + } + else { + pTLState->AVBVerState = OPENAVB_TL_AVB_VER_INVALID; + } + + AVB_TRACE_EXIT(AVB_TRACE_TL); +} + +/* Talker Listener thread function that talks primarily with the endpoint + */ +void* openavbTLThreadFn(void *pv) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + tl_state_t *pTLState = (tl_state_t *)pv; + + while (pTLState->bRunning) { + AVB_TRACE_LINE(AVB_TRACE_TL_DETAIL); + + int endpointHandle = openavbEptClntOpenSrvrConnection(pTLState); + + if (endpointHandle == AVB_ENDPOINT_HANDLE_INVALID) { + // error connecting to endpoint, already logged + } + else { + pTLState->endpointHandle = endpointHandle; + + // Validate the AVB version for TL and Endpoint are the same before continuing + pTLState->AVBVerState = OPENAVB_TL_AVB_VER_UNKNOWN; + pTLState->bConnected = openavbEptClntRequestVersionFromServer(pTLState->endpointHandle); + while (pTLState->bRunning && pTLState->bConnected && pTLState->AVBVerState == OPENAVB_TL_AVB_VER_UNKNOWN) { + // Check for endpoint version message. Timeout in 50 msec. + if (!openavbEptClntService(pTLState->endpointHandle, 50)) { + AVB_LOG_WARNING("Lost connection to endpoint, will retry"); + pTLState->bConnected = FALSE; + pTLState->endpointHandle = 0; + } + } + if (pTLState->AVBVerState == OPENAVB_TL_AVB_VER_INVALID) { + AVB_LOG_ERROR("AVB core version is different than Endpoint AVB core version. Streams will not be started. Will reconnect to the endpoint and check again."); + } + + if (pTLState->bConnected && pTLState->AVBVerState == OPENAVB_TL_AVB_VER_VALID) { + if (pTLState->cfg.role == AVB_ROLE_TALKER) { + openavbTLRunTalker(pTLState); + } + else { + openavbTLRunListener(pTLState); + } + } + + // Close the endpoint connection. unless connection already gone in which case the socket could already be reused. + if (pTLState->bConnected) { + openavbEptClntCloseSrvrConnection(endpointHandle); + pTLState->bConnected = FALSE; + pTLState->endpointHandle = 0; + } + } + + if (pTLState->bRunning) { + SLEEP(1); + } + } + + THREAD_JOINABLE(pTLState->TLThread); + + AVB_TRACE_EXIT(AVB_TRACE_TL); + return NULL; +} + +// This is currently only for used for Endpoint +tl_handle_t TLHandleListGet(int endpointHandle) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!endpointHandle || !gTLHandleList) { + AVB_TRACE_EXIT(AVB_TRACE_TL); + return NULL; + } + + TL_LOCK(); + int i1; + for (i1 = 0; i1 < gMaxTL; i1++) { + if (gTLHandleList[i1]) { + tl_state_t *pTLState = (tl_state_t *)gTLHandleList[i1]; + if (pTLState->endpointHandle == endpointHandle) { + TL_UNLOCK(); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return pTLState; + } + } + } + TL_UNLOCK(); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return NULL; +} + +bool openavbTLAVDECCRunListener(tl_handle_t handle, U16 configIdx, U16 descriptorType, U16 descriptorIdx, void *pVoidListenerStreamInfo) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!handle) { + AVB_LOG_ERROR("Invalid handle."); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return FALSE; + } + + if (!pVoidListenerStreamInfo) { + AVB_LOG_ERROR("Invalid argument"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return FALSE; + } + + openavb_acmp_ListenerStreamInfo_t *pListenerStreamInfo = pVoidListenerStreamInfo; + tl_state_t *pTLState = (tl_state_t *)handle; + + memcpy(pTLState->cfg.dest_addr.mac->ether_addr_octet, pListenerStreamInfo->stream_dest_mac, ETH_ALEN); + memcpy(pTLState->cfg.stream_addr.mac->ether_addr_octet, pListenerStreamInfo->stream_id, ETH_ALEN); + U8 *pStreamUID = pListenerStreamInfo->stream_id + 6; +// pTLState->cfg.stream_uid = ntohs(*(U16 *)(pStreamUID)); + U16 align16; + memcpy(&align16, (U16 *)(pStreamUID), sizeof(U16)); + pTLState->cfg.stream_uid = ntohs(align16); + + if (pTLState->cfg.intf_cb.intf_avdecc_init_cb) { + pTLState->cfg.intf_cb.intf_avdecc_init_cb(pTLState->pMediaQ, configIdx, descriptorType, descriptorIdx); + } + if (pTLState->cfg.map_cb.map_avdecc_init_cb) { + pTLState->cfg.map_cb.map_avdecc_init_cb(pTLState->pMediaQ, configIdx, descriptorType, descriptorIdx); + } + + openavbTLRun(handle); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return TRUE; +} + +bool openavbTLAVDECCRunTalker(tl_handle_t handle, U16 configIdx, U16 descriptorType, U16 descriptorIdx, void *pVoidTalkerStreamInfo) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!handle) { + AVB_LOG_ERROR("Invalid handle."); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return FALSE; + } + + tl_state_t *pTLState = (tl_state_t *)handle; + + if (pTLState->cfg.intf_cb.intf_avdecc_init_cb) { + pTLState->cfg.intf_cb.intf_avdecc_init_cb(pTLState->pMediaQ, configIdx, descriptorType, descriptorIdx); + } + if (pTLState->cfg.map_cb.map_avdecc_init_cb) { + pTLState->cfg.map_cb.map_avdecc_init_cb(pTLState->pMediaQ, configIdx, descriptorType, descriptorIdx); + } + + if (!openavbTLIsRunning(handle)) { + openavbTLRun(handle); + } + + AVB_TRACE_EXIT(AVB_TRACE_TL); + return TRUE; +} + +bool openavbTLAVDECCStopListener(tl_handle_t handle, U16 configIdx, void *pVoidListenerStreamInfo) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + openavbTLStop(handle); + + AVB_TRACE_EXIT(AVB_TRACE_TL); + return TRUE; +} + +bool openavbTLAVDECCStopTalker(tl_handle_t handle, U16 configIdx, void *pVoidTalkerStreamInfo) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + openavbTLStop(handle); + + AVB_TRACE_EXIT(AVB_TRACE_TL); + return TRUE; +} + +// CORE_TODO: Consider having this functionality live in openavb_talker.c and then talker_data_t can again be private to openavb_talker.c +bool openavbTLAVDECCGetTalkerStreamInfo(tl_handle_t handle, U16 configIdx, void *pVoidTalkerStreamInfo) +{ + AVB_TRACE_ENTRY(AVB_TRACE_TL); + + if (!handle) { + AVB_LOG_ERROR("Invalid handle."); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return FALSE; + } + + if (!pVoidTalkerStreamInfo) { + AVB_LOG_ERROR("Invalid argument"); + AVB_TRACE_EXIT(AVB_TRACE_TL); + return FALSE; + } + + openavb_acmp_TalkerStreamInfo_t *pTalkerStreamInfo = pVoidTalkerStreamInfo; + tl_state_t *pTLState = (tl_state_t *)handle; + + talker_data_t *pTalkerData = pTLState->pPvtTalkerData; + + // Get the destination mac address. + memcpy(pTalkerStreamInfo->stream_dest_mac, pTalkerData->destAddr, ETH_ALEN); + + // Get the stream ID + memcpy(pTalkerStreamInfo->stream_id, pTalkerData->streamID.addr, ETH_ALEN); + U8 *pStreamUID = pTalkerStreamInfo->stream_id + 6; + *(U16 *)(pStreamUID) = htons(pTLState->cfg.stream_uid); + + AVB_TRACE_EXIT(AVB_TRACE_TL); + return TRUE; +} + diff --git a/lib/avtp_pipeline/tl/openavb_tl_no_endpoint.c b/lib/avtp_pipeline/tl/openavb_tl_no_endpoint.c new file mode 100644 index 00000000..1dce6284 --- /dev/null +++ b/lib/avtp_pipeline/tl/openavb_tl_no_endpoint.c @@ -0,0 +1,134 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+/*
+* MODULE SUMMARY : Common implementation for the talker and listener
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "openavb_tl.h"
+#include "openavb_trace.h"
+//#include "openavb_mediaq.h"
+#include "openavb_talker.h"
+#include "openavb_listener.h"
+#include "openavb_avtp.h"
+#include "openavb_endpoint.h"
+
+#include "openavb_platform.h"
+
+#define AVB_LOG_COMPONENT "Talker / Listener"
+#include "openavb_pub.h"
+#include "openavb_log.h"
+
+void openavbEptClntCheckVerMatchesSrvr(int endpointHandle, U32 AVBVersion)
+{
+}
+
+
+// Talker Listener thread function
+void* openavbTLThreadFn(void *pv)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ tl_state_t *pTLState = (tl_state_t *)pv;
+
+ openavbTLThreadFnOsal(pTLState);
+
+ TL_LOCK();
+ // Assign a unique endpoint handle
+ static int gEndpointHandle = 1;
+ pTLState->endpointHandle = gEndpointHandle++;
+ TL_UNLOCK();
+
+ while (pTLState->bRunning) {
+ AVB_TRACE_LINE(AVB_TRACE_TL_DETAIL);
+
+ if (pTLState->cfg.role == AVB_ROLE_TALKER) {
+ openavbTLRunTalker(pTLState);
+ }
+ else {
+ openavbTLRunListener(pTLState);
+ }
+
+ // Close the endpoint connection. unless connection already gone in which case the socket could already be reused.
+ if (pTLState->bConnected) {
+ pTLState->bConnected = FALSE;
+ pTLState->endpointHandle = 0;
+ }
+
+ if (pTLState->bRunning) {
+ SLEEP(1);
+ }
+ }
+
+ THREAD_JOINABLE(pTLState->TLThread);
+
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return NULL;
+}
+
+tl_handle_t TLHandleListGet(int endpointHandle)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_TL);
+
+ if (!endpointHandle || !gTLHandleList) {
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return NULL;
+ }
+
+ TL_LOCK();
+ int i1;
+ for (i1 = 0; i1 < gMaxTL; i1++) {
+ if (gTLHandleList[i1]) {
+ tl_state_t *pTLState = (tl_state_t *)gTLHandleList[i1];
+ if (pTLState->endpointHandle == endpointHandle) {
+ TL_UNLOCK();
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return pTLState;
+ }
+ }
+ }
+ TL_UNLOCK();
+ AVB_TRACE_EXIT(AVB_TRACE_TL);
+ return NULL;
+}
+
+bool openavbEptClntStopStream(int h, AVBStreamID_t *streamID)
+{
+ return TRUE;
+}
+
+bool openavbEptClntService(int h, int timeout)
+{
+ return TRUE;
+}
+
diff --git a/lib/avtp_pipeline/tl/openavb_tl_pub.h b/lib/avtp_pipeline/tl/openavb_tl_pub.h new file mode 100755 index 00000000..cde88223 --- /dev/null +++ b/lib/avtp_pipeline/tl/openavb_tl_pub.h @@ -0,0 +1,339 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* HEADER SUMMARY : Talker Listener Public Interface +*/ + +#ifndef OPENAVB_TL_PUB_H +#define OPENAVB_TL_PUB_H 1 + +#include "openavb_types_pub.h" +#include "openavb_mediaq_pub.h" +#include "openavb_map_pub.h" +#include "openavb_intf_pub.h" +#include "openavb_avtp_time_pub.h" + +/** \file + * Talker Listener Public Interface. + */ + +/// Handle to a single talker or listener. +typedef void *tl_handle_t; + +/// Types of statistics gathered +typedef enum { + /// Number of TX calls + TL_STAT_TX_CALLS, + /// Number of TX frames + TL_STAT_TX_FRAMES, + /// NUmber of late TX frames + TL_STAT_TX_LATE, + /// Number of bytes send + TL_STAT_TX_BYTES, + /// Number of RX calls + TL_STAT_RX_CALLS, + /// Number of RX frames + TL_STAT_RX_FRAMES, + /// Number of RX frames lost + TL_STAT_RX_LOST, + /// Number of bytes received + TL_STAT_RX_BYTES, +} tl_stat_t; + +/// Maximum number of configuration parameters inside INI file a host can have +#define MAX_LIB_CFG_ITEMS 64 + +#define IFNAMSIZE 16 + +/// Structure containing configuration of the host +typedef struct { + /// Role of the host + avb_role_t role; + /// Structure with callbacks to mapping + openavb_map_cb_t map_cb; + /// Structure with callbacks to inteface + openavb_intf_cb_t intf_cb; + /// MAC address of destination - multicast (talker only if SRP is enabled) + cfg_mac_t dest_addr; + /// MAC address of the source + cfg_mac_t stream_addr; + /// Stream UID (has to be unique) + S32 stream_uid; + /// Maximum number of packets sent during one interval (talker only) + U32 max_interval_frames; + /// Maximum size of the frame + U32 max_frame_size; + /// Setting maximum transit time, on talker value is added to PTP Walltime, + /// on listener value is validated timestamp range + U32 max_transit_usec; + /// Maximum transmit deficit in usec - should be set to expected buffer size + /// on the listener side (talker only) + U32 max_transmit_deficit_usec; + /// Specify manual an internal latency (talker only) + U32 internal_latency; + /// Number of microseconds after which late MediaQItem will be purged as too + /// old (listener only) + U32 max_stale; + /// Number of intervals to handle at once (talker only) + U32 batch_factor; + /// Statistics reporting frequency + U32 report_seconds; + /// Start paused + bool start_paused; + /// Class in which host will operatea ::SRClassIdx_t (talker only) + U8 sr_class; + /// Rank of the stream #SR_RANK_REGULAR or #SR_RANK_EMERGENCY (talker only) + U8 sr_rank; + /// Number of raw TX buffers that should be used (talker only) + U32 raw_tx_buffers; + /// Number of raw rx buffers (listener only) + U32 raw_rx_buffers; + /// Is the interface module blocking in the TX CB. + bool tx_blocking_in_intf; + /// Network interface name. Not used on all platforms. + char ifname[IFNAMSIZE]; + /// When set incoming packets will trigger a signal to the stream task to wakeup. + bool rx_signal_mode; + + /// Initialization function in mapper + openavb_map_initialize_fn_t pMapInitFn; + /// Initialization function in interface + openavb_intf_initialize_fn_t pIntfInitFn; +} openavb_tl_cfg_t; + +typedef struct openavb_tl_cfg_name_value_t { + /// Configuration parameters Names for interface and mapping modules + char *libCfgNames[MAX_LIB_CFG_ITEMS]; + /// Configuration parameters Values for interface and mapping modules + char *libCfgValues[MAX_LIB_CFG_ITEMS]; + /// Number of configuration parameters defined + U32 nLibCfgItems; +} openavb_tl_cfg_name_value_t; + + +/** Initialize the talker listener library. + * + * This function must be called first before any other in the openavb_tl API. + * Any AVTP wide initialization occurs at this point + * + * \param maxTL The maximum number of talkers and listeners that will be started + * \return TRUE on success or FALSE on failure + * + * \warning Must be called prior to using any other TL APIs + */ +bool openavbTLInitialize(U32 maxTL); + +/** Final cleanup of the talker listener library. + * + * This function must be called last after all talkers and listeners have been closed + * + * \return TRUE on success or FALSE on failure + * + * \warning Should be called after all Talker and Listeners are closed + */ +bool openavbTLCleanup(void); + +/** Get the version of the AVB stack. + * + * Fills the major, minor and revision parameters with the values version + * information of the AVB stack + * + * \param major The major part of the version number + * \param minor The minor part of the version number + * \param revision The revision part of the version number + * \return TRUE on success or FALSE on failure + */ +bool openavbGetVersion(U8 *major, U8 *minor, U8 *revision); + +/** Open a talker or listener. + * + * This will create a talker / listener + * + * \return handle of the talker/listener. NULL if the talker or listener could not be loaded + */ +tl_handle_t openavbTLOpen(void); + +/** Initialize the configuration to default values. + * Initializes configuration file to default values + * + * \param pCfg Pointer to configuration structure + */ +void openavbTLInitCfg(openavb_tl_cfg_t *pCfg); + +/** Configure the talker / listener. + * + * Configures talker/listener with configuration values from configuration + * structure and name value pairs + * + * \param handle Handle of talker/listener + * \param pCfg Pointer to configuration structure + * \param pNVCfg Pointer to name value pair configuration + * structure + * \return TRUE on success or FALSE on failure + */ +bool openavbTLConfigure(tl_handle_t handle, openavb_tl_cfg_t *pCfg, openavb_tl_cfg_name_value_t *pNVCfg); + +/** Run the talker or listener. + * + * The talker or listener indicated by handle that was previously loaded with + * the openavbTLOpen() function will be run. The stream will be opened at this time. + * Two threads created, one for endpoint IPC and one for stream handling. + * + * \param handle The handle return from openavbTLOpen() + * \return TRUE on success or FALSE on failure + */ +bool openavbTLRun(tl_handle_t handle); + +/** Stop a single talker or listener. + * + * Stop a single talker or listener. At this point data will not be sent or recieved + * + * \param handle The handle return from openavbTLOpen() + * \return TRUE on success or FALSE on failure + */ +bool openavbTLStop(tl_handle_t handle); + +/** Pause or resume as stream. + * + * A paused stream will do everything except will toss both tx and rx packets + * + * \param handle The handle return from openavbTLOpen() + * \param bPause TRUE to pause, FALSE to resume + * \return TRUE on success or FALSE on failure + */ +void openavbTLPauseStream(tl_handle_t handle, bool bPause); + +/** Close the talker or listener. + * + * The talker or listener indicated by handle that was previously loaded with + * the openavbTLOpen() function will be closed. The stream will be shutdown at this + * time and the threads created for this talker or listener will be killed + * + * \param handle The handle return from openavbTLOpen() + * \return TRUE on success or FALSE on failure + */ +bool openavbTLClose(tl_handle_t handle); + +/** Get a pointer to a list of interfaces module callbacks. + * + * In cases where a host application needs to call directly into an interface + * module it is preferable to do so with the APIs supplied in this SDK. This + * will allow passing back into the interface module a handle to its data. This + * handle is the value returned from openavbTLGetIntfHandle() + * + * \param handle The handle return from openavbTLOpen() + * \return A void * is returned. This is a pointer that can be resolved when + * combined with a public API defined by the specific interface module + */ +void* openavbTLGetIntfHostCBList(tl_handle_t handle); + +/** Get a handle to the interface module data from this talker or listener. + * + * Returns a handle to the interface module data. This handle will be used in + * call backs into the interface module from the host application and allows the + * interface module to associate the call back with the correct talker / + * listener (stream) + * + * \param handle The handle return from openavbTLOpen() \return Handle as a void * + * to the interface module data. This returned value is only intended to be + * passed back to the interface module in call backs from the host application. + */ +void* openavbTLGetIntfHandle(tl_handle_t handle); + +/** Check if a talker or listener is running. + * + * Checks if the talker or listener indicated by handle is running. The running + * status will be true after calling openavbTLRun() + * + * \param handle The handle return from openavbTLOpen() + * \return TRUE if running FALSE if not running + */ +bool openavbTLIsRunning(tl_handle_t handle); + +/** Checks if a talker or listener is connected to the endpoint. + * + * Checks if the talker or listener indicated by handle is connected to the + * endpoint process + * + * \param handle The handle return from openavbTLOpen() + * \return TRUE if connected FALSE if not connected + */ +bool openavbTLIsConnected(tl_handle_t handle); + +/** Checks if a talker or listener has an open stream. + * + * Checks if the talker or listener indicated by handle has an open stream + * + * \param handle The handle return from openavbTLOpen() + * \return TRUE if streaming FALSE if not streaming + */ +bool openavbTLIsStreaming(tl_handle_t handle); + +/** Return the role of the current stream handle. + * + * Returns if the current open stream is a talker or listener + * + * \param handle The handle return from openavbTLOpen() + * \return The current role + */ +avb_role_t openavbTLGetRole(tl_handle_t handle); + +/** Allows pulling current stat counters for a running stream. + * + * The various stat counters for a stream can be retrieved with this function + * + * \param handle The handle return from openavbTLOpen() + * \param stat Which stat to retrieve + * \return the requested counter + */ +U64 openavbTLStat(tl_handle_t handle, tl_stat_t stat); + +/** Read an ini file. + * + * Parses an input configuration file tp populate configuration structures, and + * name value pairs. Only used in Operating Systems that have a file system + * + * \param handle Pointer to handle of talker/listener + * \param fileName Pointer to configuration file name + * \param pCfg Pointer to configuration structure + * \param pNVCfg Pointer to name value pair configuration + * structure + * \return TRUE on success or FALSE on failure + * + * \warning Not available on all platforms + */ +bool openavbTLReadIniFileOsal(tl_handle_t TLhandle, const char *fileName, openavb_tl_cfg_t *pCfg, openavb_tl_cfg_name_value_t *pNVCfg); + + +/** \example openavb_host.c + * Talker / Listener example host application. + */ +#endif // OPENAVB_TL_PUB_H diff --git a/lib/avtp_pipeline/util/CMakeLists.txt b/lib/avtp_pipeline/util/CMakeLists.txt new file mode 100644 index 00000000..3e87f341 --- /dev/null +++ b/lib/avtp_pipeline/util/CMakeLists.txt @@ -0,0 +1,14 @@ +SET (SRC_FILES ${SRC_FILES} + ${AVB_SRC_DIR}/util/openavb_result_codes.c + ${AVB_SRC_DIR}/util/openavb_list.c + ${AVB_SRC_DIR}/util/openavb_array.c + ${AVB_SRC_DIR}/util/openavb_debug.c + ${AVB_SRC_DIR}/util/openavb_plugin.c + ${AVB_SRC_DIR}/util/openavb_log.c + ${AVB_SRC_DIR}/util/openavb_queue.c + ${AVB_SRC_DIR}/util/openavb_time.c + ${AVB_SRC_DIR}/util/openavb_timestamp.c + ${AVB_SRC_DIR}/util/openavb_printbuf.c + PARENT_SCOPE +) + diff --git a/lib/avtp_pipeline/util/openavb_array.c b/lib/avtp_pipeline/util/openavb_array.c new file mode 100644 index 00000000..426212ec --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_array.c @@ -0,0 +1,443 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Implementation for a basic dynamic array abstraction. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> + +#include "openavb_debug.h" +#include "openavb_array.h" + +OPENAVB_CODE_MODULE_PRI + +struct openavb_array_elem { + void *data; + bool managed; + S32 idx; +}; + +struct openavb_array { + U32 elemSize; + S32 size; + S32 count; + S32 iterIdx; + openavb_array_elem_t elemArray; +}; + +openavb_array_t openavbArrayNewArray(U32 size) +{ + openavb_array_t retArray = calloc(1, sizeof(struct openavb_array)); + if (retArray) { + retArray->elemSize = size; + retArray->size = 0; + retArray->count = 0; + retArray->iterIdx = 0; + } + + return retArray; +} + +bool openavbArraySetInitSize(openavb_array_t array, U32 size) +{ + if (array) { + if (!array->size) { + array->elemArray = calloc(size, sizeof(struct openavb_array_elem)); + if (array->elemArray) { + array->size = size; + + // Initialize each elem + S32 i1; + for (i1 = 0; i1 < array->size; i1++) { + array->elemArray[i1].data = NULL; + array->elemArray[i1].managed = FALSE; + array->elemArray[i1].idx = i1; + } + return TRUE; + } + } + } + return FALSE; +} + +void openavbArrayDeleteArray(openavb_array_t array) +{ + if (array && array->elemArray) { + openavb_array_elem_t elem; + elem = openavbArrayIterFirst(array); + while (elem) { + openavbArrayDelete(array, elem); + elem = openavbArrayIterNext(array); + } + free(array->elemArray); + array->elemArray = NULL; + free(array); + } +} + +openavb_array_elem_t openavbArrayAdd(openavb_array_t array, void *data) +{ + openavb_array_elem_t retElem = NULL; + + if (array && data) { + if (array->elemArray && (array->count < array->size)) { + // Find the empty slot + S32 i1; + for (i1 = 0; i1 < array->size; i1++) { + if (!array->elemArray[i1].data) { + retElem = &array->elemArray[i1]; + break; + } + } + } + else { + // Need to make room for new element + openavb_array_elem_t newElemArray = realloc(array->elemArray, (array->size + 1) * sizeof(struct openavb_array_elem)); + if (newElemArray) { + array->elemArray = newElemArray; + retElem = &array->elemArray[array->size]; + retElem->data = NULL; + retElem->managed = FALSE; + retElem->idx = array->size; + array->size++; + } + } + + if (retElem) { + retElem->data = data; + array->count++; + } + } + + return retElem; +} + +openavb_array_elem_t openavbArrayNew(openavb_array_t array) +{ + openavb_array_elem_t retElem = NULL; + + if (array) { + void *data = calloc(1, array->elemSize); + if (data) { + retElem = openavbArrayAdd(array, data); + if (retElem) { + retElem->managed = TRUE; + } else { + free(data); + } + } + } + + return retElem; +} + +bool openavbArrayMultiNew(openavb_array_t array, S32 count) +{ + int i1; + for (i1 = 0; i1 < count; i1++) { + if (!openavbArrayNew(array)) + return FALSE; + } + return TRUE; +} + +void openavbArrayDelete(openavb_array_t array, openavb_array_elem_t elem) +{ + if (array && elem) { + if (elem->managed) { + free(elem->data); + } + elem->data = NULL; + array->count--; + } +} + +openavb_array_elem_t openavbArrayIdx(openavb_array_t array, S32 idx) +{ + openavb_array_elem_t retElem = NULL; + + if (array && array->elemArray && idx >= 0 && idx < array->size) { + if (array->elemArray[idx].data) { + retElem = &array->elemArray[idx]; + } + } + return retElem; +} + +openavb_array_elem_t openavbArrayIterFirst(openavb_array_t array) +{ + openavb_array_elem_t retElem = NULL; + + if (array && array->elemArray) { + S32 i1; + for (i1 = 0; i1 < array->size; i1++) { + if (array->elemArray[i1].data) { + retElem = &array->elemArray[i1]; + array->iterIdx = i1; + break; + } + } + } + return retElem; +} + +openavb_array_elem_t openavbArrayIterLast(openavb_array_t array) +{ + openavb_array_elem_t retElem = NULL; + + if (array && array->elemArray) { + S32 i1; + for (i1 = array->size - 1; i1 >= 0; i1--) { + if (array->elemArray[i1].data) { + retElem = &array->elemArray[i1]; + array->iterIdx = i1; + break; + } + } + } + return retElem; +} + +openavb_array_elem_t openavbArrayIterNext(openavb_array_t array) +{ + openavb_array_elem_t retElem = NULL; + + if (array && array->elemArray) { + S32 i1; + for (i1 = array->iterIdx + 1; i1 < array->size; i1++) { + if (array->elemArray[i1].data) { + retElem = &array->elemArray[i1]; + array->iterIdx = i1; + break; + } + } + } + return retElem; +} + +openavb_array_elem_t openavbArrayIterPrev(openavb_array_t array) +{ + openavb_array_elem_t retElem = NULL; + + if (array && array->elemArray) { + S32 i1; + for (i1 = array->iterIdx - 1; i1 >= 0; i1--) { + if (array->elemArray[i1].data) { + retElem = &array->elemArray[i1]; + array->iterIdx = i1; + break; + } + } + } + return retElem; +} + +openavb_array_elem_t openavbArrayIterFirstAlt(openavb_array_t array, U32 *iter) +{ + openavb_array_elem_t retElem = NULL; + + if (array && array->elemArray) { + S32 i1; + for (i1 = 0; i1 < array->size; i1++) { + if (array->elemArray[i1].data) { + retElem = &array->elemArray[i1]; + *iter = i1; + break; + } + } + } + return retElem; +} + +openavb_array_elem_t openavbArrayIterLastAlt(openavb_array_t array, U32 *iter) +{ + openavb_array_elem_t retElem = NULL; + + if (array && array->elemArray) { + S32 i1; + for (i1 = array->size - 1; i1 >= 0; i1--) { + if (array->elemArray[i1].data) { + retElem = &array->elemArray[i1]; + *iter = i1; + break; + } + } + } + return retElem; +} + +openavb_array_elem_t openavbArrayIterNextAlt(openavb_array_t array, U32 *iter) +{ + openavb_array_elem_t retElem = NULL; + + if (array && array->elemArray) { + S32 i1; + for (i1 = *iter + 1; i1 < array->size; i1++) { + if (array->elemArray[i1].data) { + retElem = &array->elemArray[i1]; + *iter = i1; + break; + } + } + } + return retElem; +} + +openavb_array_elem_t openavbArrayIterPrevAlt(openavb_array_t array, U32 *iter) +{ + openavb_array_elem_t retElem = NULL; + + if (array && array->elemArray) { + S32 i1; + for (i1 = *iter - 1; i1 >= 0; i1--) { + if (array->elemArray[i1].data) { + retElem = &array->elemArray[i1]; + *iter = i1; + break; + } + } + } + return retElem; +} + +void *openavbArrayData(openavb_array_elem_t elem) +{ + if (elem) { + return elem->data; + } + return NULL; +} + +void *openavbArrayDataIdx(openavb_array_t array, S32 idx) +{ + if (array) { + openavb_array_elem_t elem = openavbArrayIdx(array, idx); + + if (elem) { + return elem->data; + } + } + return NULL; +} + +void *openavbArrayDataNew(openavb_array_t array) +{ + if (array) { + openavb_array_elem_t elem = openavbArrayNew(array); + + if (elem) { + return elem->data; + } + } + return NULL; +} + +S32 openavbArrayFindData(openavb_array_t array, void *data) +{ + S32 retIdx = -1; + if (array && data) { + S32 i1; + for (i1 = 0; i1 < array->size; i1++) { + if (array->elemArray[i1].data == data) { + retIdx = i1; + break; + } + } + } + return retIdx; +} + + +S32 openavbArraySize(openavb_array_t array) +{ + if (array) { + return array->size; + } + return 0; +} + +S32 openavbArrayCount(openavb_array_t array) +{ + if (array) { + return array->count; + } + return 0; +} + +S32 openavbArrayGetIdx(openavb_array_elem_t elem) +{ + if (elem) { + return elem->idx; + } + return -1; +} + +bool openavbArrayIsManaged(openavb_array_elem_t elem) +{ + if (elem) { + if (elem->managed) { + return TRUE; + } + } + return FALSE; +} + +void openavbArrayDump(openavb_array_t array) +{ + printf("TEST DUMP: Array contents\n"); + if (array) { + printf("array: %p\n", array); + printf("elemSize: %u\n", (unsigned int)(array->elemSize)); + printf("size: %d\n", (int)(array->size)); + printf("count: %d\n", (int)(array->count)); + printf("iterIdx: %d\n", (int)(array->iterIdx)); + printf("elemArray %p\n", array->elemArray); + + if (array->elemArray) { + S32 i1; + for (i1 = 0; i1 < array->size; i1++) { + printf("elemArray[%02d] %p\n", (int)i1, &array->elemArray[i1]); + printf("elemArray[%02d].data %p\n", (int)i1, array->elemArray[i1].data); + printf("elemArray[%02d].managed %d\n", (int)i1, array->elemArray[i1].managed); + printf("elemArray[%02d].idx %d\n", (int)i1, (int)(array->elemArray[i1].idx)); + openavbDebugDumpBuf(array->elemArray[i1].data, array->elemSize); + } + } + + return; + } + printf("Invalid array pointer\n"); +} + + diff --git a/lib/avtp_pipeline/util/openavb_array.h b/lib/avtp_pipeline/util/openavb_array.h new file mode 100644 index 00000000..90fb3b7d --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_array.h @@ -0,0 +1,122 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Interface for a basic dynamic array abstraction. +* +* - Array dynamically grows as needed. +* - Elements can be removed. This leaves an empty slot. +* - Elements can not be moved once placed in the array. +*/ + +#ifndef OPENAVB_ARRAY_H +#define OPENAVB_ARRAY_H 1 + +#include "openavb_types.h" + +typedef struct openavb_array_elem * openavb_array_elem_t; +typedef struct openavb_array * openavb_array_t; + +// Create an array. Returns NULL on failure. +openavb_array_t openavbArrayNewArray(U32 elemSize); + +// Sets the initial number of array slots. Can only be used before adding elements to the array. +bool openavbArraySetInitSize(openavb_array_t array, U32 size); + +// Delete an array. +void openavbArrayDeleteArray(openavb_array_t array); + +// Add a data element to the array. Returns NULL on failure. +openavb_array_elem_t openavbArrayAdd(openavb_array_t array, void *data); + +// Allocate and manage data element and add to the array. Returns NULL on failure. +openavb_array_elem_t openavbArrayNew(openavb_array_t array); + +// Allocate and manage multiple data element and add to the array. Returns FALSE on failure. +bool openavbArrayMultiNew(openavb_array_t array, S32 count); + +// Remove (delete) element. Will result in an empty slot. +void openavbArrayDelete(openavb_array_t array, openavb_array_elem_t elem); + +// Gets the element at index. Returns FALSE on error. +openavb_array_elem_t openavbArrayIdx(openavb_array_t array, S32 idx); + +// Gets the first element. Returns FALSE on error or empty array. +openavb_array_elem_t openavbArrayIterFirst(openavb_array_t array); + +// Gets the last element. Returns FALSE on error or empty array. +openavb_array_elem_t openavbArrayIterLast(openavb_array_t array); + +// Gets the next element. Returns FALSE on error or last element. +openavb_array_elem_t openavbArrayIterNext(openavb_array_t array); + +// Gets the previous element. Returns FALSE on error or first element. +openavb_array_elem_t openavbArrayIterPrev(openavb_array_t array); + +// Gets the first element. Returns FALSE on error or empty array. Alternate version with iter passed in for possible multi-threaded use. +openavb_array_elem_t openavbArrayIterFirstAlt(openavb_array_t array, U32 *iter); + +// Gets the last element. Returns FALSE on error or empty array. Alternate version with iter passed in for possible multi-threaded use. +openavb_array_elem_t openavbArrayIterLastAlt(openavb_array_t array, U32 *iter); + +// Gets the next element. Returns FALSE on error or last element. Alternate version with iter passed in for possible multi-threaded use. +openavb_array_elem_t openavbArrayIterNextAlt(openavb_array_t array, U32 *iter); + +// Gets the previous element. Returns FALSE on error or first element. Alternate version with iter passed in for possible multi-threaded use. +openavb_array_elem_t openavbArrayIterPrevAlt(openavb_array_t array, U32 *iter); + +// Get data of the element. Returns NULL on failure. +void *openavbArrayData(openavb_array_elem_t elem); + +// Get data of the element at the Idx. Returns NULL on failure. +void *openavbArrayDataIdx(openavb_array_t array, S32 idx); + +// Get data of a new element. Returns NULL on failure. +void *openavbArrayDataNew(openavb_array_t array); + +// Find the index of the element data. Returns -1 if not found. +S32 openavbArrayFindData(openavb_array_t array, void *data); + +// Get the size of the array. Allocated element slots +S32 openavbArraySize(openavb_array_t array); + +// Get the count of the array. Valid elements in the array. +S32 openavbArrayCount(openavb_array_t array); + +// Returns the index of the array element. -1 on error. +S32 openavbArrayGetIdx(openavb_array_elem_t elem); + +// Returns TRUE is node data element memory is being managed by the list. +bool openavbArrayIsManaged(openavb_array_elem_t elem); + +// Dump array details to console +void openavbArrayDump(openavb_array_t array); + +#endif // OPENAVB_ARRAY_H diff --git a/lib/avtp_pipeline/util/openavb_debug.c b/lib/avtp_pipeline/util/openavb_debug.c new file mode 100644 index 00000000..c652d279 --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_debug.c @@ -0,0 +1,90 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Implementation of debugging aid functions. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_debug.h" + +#define AVB_LOG_COMPONENT "Debug" +#include "openavb_log_pub.h" + +void openavbDebugDumpBuf(U8 *pBuf, U32 len) +{ + printf("Dump buffer %p: ", pBuf); + if (pBuf) { + U32 i1; + for (i1 = 0; i1 < len; i1++) { + printf("%02X ", *pBuf); + pBuf++; + } + } + printf("\n"); +} + +void openavbDebugInterval(U32 interval, bool log, U32 *maxInterval, U32 *minInterval, U32 *cntInterval, U32 *accInterval, U64 *prevNS) +{ + U32 deltaUS; + U64 nowNS; + + if (*prevNS > 0) { + CLOCK_GETTIME64(OPENAVB_CLOCK_MONOTONIC, &nowNS); + deltaUS = (nowNS - *prevNS) / 1000; + (*cntInterval)++; + if (deltaUS > *maxInterval) + *maxInterval = deltaUS; + if (deltaUS < *minInterval) + *minInterval = deltaUS; + *accInterval += deltaUS; + + if (*cntInterval % interval == 0) { + if (log) { + AVB_LOGF_INFO("Gap(us):%u Gap Min(us):%u Gap Max(us):%u Gap Avg(us):%u", + (unsigned int)deltaUS, + (unsigned int)*minInterval, + (unsigned int)*maxInterval, + (unsigned int)(*accInterval / *cntInterval)); + } + *cntInterval = 0; + *maxInterval = 0; + *minInterval = (U32)-1; + *accInterval = 0; + } + } + + CLOCK_GETTIME64(OPENAVB_CLOCK_MONOTONIC, prevNS); +} diff --git a/lib/avtp_pipeline/util/openavb_debug.h b/lib/avtp_pipeline/util/openavb_debug.h new file mode 100644 index 00000000..646183ab --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_debug.h @@ -0,0 +1,57 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Implementation of debugging aid functions. +*/ + +#ifndef OPENAVB_DEBUG_H +#define OPENAVB_DEBUG_H 1 + +#include "openavb_types.h" + +#define DBG_VARx(x, y) x ## y +#define DBG_VAR(x, y) DBG_VARx(x, y) + +// Dump the buffer to the console +void openavbDebugDumpBuf(U8 *pBuf, U32 len); + +// Interval Testing +#define AVB_DBG_INTERVAL(interval, log) \ + static U32 DBG_VAR(maxInterval,__LINE__) = 0; \ + static U32 DBG_VAR(minInterval,__LINE__) = (U32)-1; \ + static U32 DBG_VAR(cntInterval,__LINE__) = 0; \ + static U32 DBG_VAR(accInterval,__LINE__) = 0; \ + static U64 DBG_VAR(prevNS,__LINE__) = 0; \ + openavbDebugInterval(interval, log, &DBG_VAR(maxInterval,__LINE__), &DBG_VAR(minInterval,__LINE__), &DBG_VAR(cntInterval,__LINE__), &DBG_VAR(accInterval,__LINE__), &DBG_VAR(prevNS,__LINE__)) +void openavbDebugInterval(U32 interval, bool log, U32 *maxInterval, U32 *minInterval, U32 *cntInterval, U32 *accInterval, U64 *prevNS); + + +#endif // OPENAVB_DEBUG_H diff --git a/lib/avtp_pipeline/util/openavb_list.c b/lib/avtp_pipeline/util/openavb_list.c new file mode 100644 index 00000000..82b8b3dd --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_list.c @@ -0,0 +1,290 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Implementation for a basic doubly link list abstraction +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "openavb_debug.h" +#include "openavb_list.h" + +OPENAVB_CODE_MODULE_PRI + +struct openavb_list_node { + void *data; + bool managed; + openavb_list_node_t prev; + openavb_list_node_t next; +}; + +struct openavb_list { + openavb_list_node_t head; + openavb_list_node_t tail; + openavb_list_node_t iter; +}; + +openavb_list_t openavbListNewList() +{ + return calloc(1, sizeof(struct openavb_list)); +} + +void openavbListDeleteList(openavb_list_t list) +{ + if (list) { + openavb_list_node_t node; + while ((node = openavbListFirst(list))) { + openavbListDelete(list, node); + } + free(list); + } +} + +openavb_list_node_t openavbListAdd(openavb_list_t list, void *data) +{ + openavb_list_node_t retNode = NULL; + if (list) { + retNode = calloc(1, sizeof(struct openavb_list_node)); + if (retNode) { + retNode->data = data; + retNode->prev = NULL; + if (!list->head) { + list->head = retNode; + list->tail = retNode; + } + else { + retNode->prev = list->tail; + list->tail->next = retNode; + list->tail = retNode; + } + } + } + return retNode; +} + +openavb_list_node_t openavbListNew(openavb_list_t list, U32 size) +{ + openavb_list_node_t retNode = NULL; + if (list && size) { + void *data = calloc(1, size); + if (data) { + retNode = openavbListAdd(list, data); + if (retNode) { + retNode->managed = TRUE; + } else { + free(data); + } + } + } + + return retNode; +} + +openavb_list_node_t openavbListDelete(openavb_list_t list, openavb_list_node_t node) +{ + openavb_list_node_t retNode = NULL; + if (list && node) { + // Update head and tail if needed + if (node == list->head) { + list->head = node->next; + } + if (node == list->tail) { + list->tail = node->prev; + } + + // Unchain ourselves + if (node->prev) { + node->prev->next = node->next; + } + if (node->next) { + node->next->prev = node->prev; + retNode = node->next; + } + + // Free managed memory + if (node->managed && node->data) { + free(node->data); + node->data = NULL; + } + free(node); + } + return retNode; +} + +openavb_list_node_t openavbListNext(openavb_list_t list, openavb_list_node_t node) +{ + openavb_list_node_t retNode = NULL; + if (list && node) { + retNode = node->next; + } + return retNode; +} + +openavb_list_node_t openavbListPrev(openavb_list_t list, openavb_list_node_t node) +{ + openavb_list_node_t retNode = NULL; + if (list && node) { + retNode = node->prev; + } + return retNode; +} + +openavb_list_node_t openavbListFirst(openavb_list_t list) +{ + openavb_list_node_t retNode = NULL; + if (list) { + return list->head; + } + return retNode; +} + +openavb_list_node_t openavbListLast(openavb_list_t list) +{ + openavb_list_node_t retNode = NULL; + if (list) { + return list->tail; + } + return retNode; +} + +openavb_list_node_t openavbListIterFirst(openavb_list_t list) +{ + openavb_list_node_t retNode = NULL; + if (list) { + list->iter = list->head; + retNode = list->iter; + } + return retNode; +} + + +openavb_list_node_t openavbListIterLast(openavb_list_t list) +{ + openavb_list_node_t retNode = NULL; + if (list) { + list->iter = list->tail; + retNode = list->iter; + } + return retNode; +} + +openavb_list_node_t openavbListIterNext(openavb_list_t list) +{ + openavb_list_node_t retNode = NULL; + if (list && list->iter) { + list->iter = list->iter->next; + retNode = list->iter; + } + return retNode; +} + + +openavb_list_node_t openavbListIterPrev(openavb_list_t list) +{ + openavb_list_node_t retNode = NULL; + if (list && list->iter) { + list->iter = list->iter->prev; + retNode = list->iter; + } + return retNode; +} + +void *openavbListData(openavb_list_node_t node) +{ + if (node) { + return node->data; + } + return NULL; +} + +bool openavbListIsFirst(openavb_list_t list, openavb_list_node_t node) +{ + if (list && node) { + if (list->head == node) { + return TRUE; + } + } + return FALSE; +} + +bool openavbListIsLast(openavb_list_t list, openavb_list_node_t node) +{ + if (list && node) { + if (list->tail == node) { + return TRUE; + } + } + return FALSE; +} + +bool openavbListIsManaged(openavb_list_node_t node) +{ + if (node) { + if (node->managed) { + return TRUE; + } + } + return FALSE; +} + +void openavbListDump(openavb_list_t list, U32 dataSize) +{ + U32 count = 0; + + printf("TEST DUMP: List contents\n"); + if (list) { + + printf("listHead %p\n", list->head); + printf("listTail %p\n", list->tail); + printf("listIter %p\n", list->iter); + + openavb_list_node_t node = openavbListFirst(list); + while (node) { + count++; + printf("listNode Count %i\n", (int)count); + printf("listNodeData %p\n", node->data); + printf("listNodeManaged %i\n", node->managed); + printf("listNodePrev %p\n", node->prev); + printf("listNodeNext %p\n", node->next); + openavbDebugDumpBuf(node->data, dataSize); + node = openavbListNext(list, node); + } + + return; + } + printf("Invalid list pointer\n"); +} + + + + diff --git a/lib/avtp_pipeline/util/openavb_list.h b/lib/avtp_pipeline/util/openavb_list.h new file mode 100644 index 00000000..f184e631 --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_list.h @@ -0,0 +1,99 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Interface for a basic doubly link list abstraction +* - Nodes can be added to the end of the list. +* - Nodes can be removed at any location in the list. +*/ + +#ifndef OPENAVB_LIST_H +#define OPENAVB_LIST_H 1 + +#include "openavb_types.h" + +typedef struct openavb_list_node * openavb_list_node_t; +typedef struct openavb_list * openavb_list_t; + +// Create a link list. Returns NULL on failure +openavb_list_t openavbListNewList(void); + +// Delete a link list. +void openavbListDeleteList(openavb_list_t list); + +// Add a data element as a node to a link list. Returns NULL on failure. +openavb_list_node_t openavbListAdd(openavb_list_t list, void *data); + +// Allocate and manage data element and add as a node to a link list. Returns NULL on failure. +openavb_list_node_t openavbListNew(openavb_list_t list, U32 size); + +// Remove (delete) node. Returns NULL on failure otherwise next node. +openavb_list_node_t openavbListDelete(openavb_list_t list, openavb_list_node_t node); + +// Gets the next node. Returns FALSE on error or if already at the tail. +openavb_list_node_t openavbListNext(openavb_list_t list, openavb_list_node_t node); + +// Gets the previous node. Returns FALSE on error or if already at the head. +openavb_list_node_t openavbListPrev(openavb_list_t list, openavb_list_node_t node); + +// Gets the first (head) node. Returns FALSE on error or empty list. +openavb_list_node_t openavbListFirst(openavb_list_t list); + +// Gets the lastt (tail) node. Returns FALSE on error or empty list. +openavb_list_node_t openavbListLast(openavb_list_t list); + +// Gets the first node and preps for iteration. Returns FALSE on error or empty list. +openavb_list_node_t openavbListIterFirst(openavb_list_t list); + +// Gets the last node and preps for iteration. Returns FALSE on error or empty list. +openavb_list_node_t openavbListIterLast(openavb_list_t list); + +// Gets the next node in the iteration. Returns FALSE on error or end of list. +openavb_list_node_t openavbListIterNext(openavb_list_t list); + +// Gets the prev node in the iteration. Returns FALSE on error or beginning of list. +openavb_list_node_t openavbListIterPrev(openavb_list_t list); + +// Get data element. Returns NULL on failure. +void *openavbListData(openavb_list_node_t node); + +// Returns TRUE is node is the head. +bool openavbListIsFirst(openavb_list_t list, openavb_list_node_t node); + +// Returns TRUE is node is the tail. +bool openavbListIsLast(openavb_list_t list, openavb_list_node_t node); + +// Returns TRUE is node data element memory is being managed by the list. +bool openavbListIsManaged(openavb_list_node_t node); + +// Dump the contents of the list to the console. Debugging purposes only. +void openavbListDump(openavb_list_t list, U32 dataSize); + +#endif // OPENAVB_LIST_H diff --git a/lib/avtp_pipeline/util/openavb_log.c b/lib/avtp_pipeline/util/openavb_log.c new file mode 100644 index 00000000..a5fe4381 --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_log.c @@ -0,0 +1,386 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#include "openavb_types_pub.h"
+#include "openavb_platform_pub.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include "openavb_queue.h"
+#include "openavb_tcal_pub.h"
+
+#include "openavb_log.h"
+
+typedef struct {
+ U8 msg[LOG_QUEUE_MSG_SIZE];
+ bool bRT; // TRUE = Details are in RT queue
+} log_queue_item_t;
+
+typedef struct {
+ char *pFormat;
+ log_rt_datatype_t dataType;
+ union {
+ struct timespec nowTS;
+ U16 unsignedShortVar;
+ S16 signedShortVar;
+ U32 unsignedLongVar;
+ S32 signedLongVar;
+ U64 unsignedLongLongVar;
+ S64 signedLongLongVar;
+ float floatVar;
+ } data;
+ bool bEnd;
+} log_rt_queue_item_t;
+
+static openavb_queue_t logQueue;
+static openavb_queue_t logRTQueue;
+
+static char msg[LOG_MSG_LEN] = "";
+static char time_msg[LOG_TIME_LEN] = "";
+static char timestamp_msg[LOG_TIMESTAMP_LEN] = "";
+static char file_msg[LOG_FILE_LEN] = "";
+static char proc_msg[LOG_PROC_LEN] = "";
+static char thread_msg[LOG_THREAD_LEN] = "";
+static char full_msg[LOG_FULL_MSG_LEN] = "";
+
+static char rt_msg[LOG_RT_MSG_LEN] = "";
+
+static bool loggingThreadRunning = true;
+extern void *loggingThreadFn(void *pv);
+THREAD_TYPE(loggingThread);
+THREAD_DEFINITON(loggingThread);
+
+static MUTEX_HANDLE_ALT(gLogMutex);
+#define LOG_LOCK() MUTEX_LOCK_ALT(gLogMutex)
+#define LOG_UNLOCK() MUTEX_UNLOCK_ALT(gLogMutex)
+
+void avbLogRTRender(log_queue_item_t *pLogItem)
+{
+ if (logRTQueue) {
+ pLogItem->msg[0] = 0x00;
+ bool bMore = TRUE;
+ while (bMore) {
+ openavb_queue_elem_t elem = openavbQueueTailLock(logRTQueue);
+ if (elem) {
+ log_rt_queue_item_t *pLogRTItem = (log_rt_queue_item_t *)openavbQueueData(elem);
+
+ switch (pLogRTItem->dataType) {
+ case LOG_RT_DATATYPE_CONST_STR:
+ strcat((char *)pLogItem->msg, pLogRTItem->pFormat);
+ break;
+ case LOG_RT_DATATYPE_NOW_TS:
+ sprintf(rt_msg, "[%lu:%10lu] ", pLogRTItem->data.nowTS.tv_sec, pLogRTItem->data.nowTS.tv_nsec);
+ strcat((char *)pLogItem->msg, rt_msg);
+ break;
+ case LOG_RT_DATATYPE_U16:
+ sprintf(rt_msg, pLogRTItem->pFormat, pLogRTItem->data.unsignedShortVar);
+ strcat((char *)pLogItem->msg, rt_msg);
+ break;
+ case LOG_RT_DATATYPE_S16:
+ sprintf(rt_msg, pLogRTItem->pFormat, pLogRTItem->data.signedShortVar);
+ strcat((char *)pLogItem->msg, rt_msg);
+ break;
+ case LOG_RT_DATATYPE_U32:
+ sprintf(rt_msg, pLogRTItem->pFormat, pLogRTItem->data.unsignedLongVar);
+ strcat((char *)pLogItem->msg, rt_msg);
+ break;
+ case LOG_RT_DATATYPE_S32:
+ sprintf(rt_msg, pLogRTItem->pFormat, pLogRTItem->data.signedLongVar);
+ strcat((char *)pLogItem->msg, rt_msg);
+ break;
+ case LOG_RT_DATATYPE_U64:
+ sprintf(rt_msg, pLogRTItem->pFormat, pLogRTItem->data.unsignedLongLongVar);
+ strcat((char *)pLogItem->msg, rt_msg);
+ break;
+ case LOG_RT_DATATYPE_S64:
+ sprintf(rt_msg, pLogRTItem->pFormat, pLogRTItem->data.signedLongLongVar);
+ strcat((char *)pLogItem->msg, rt_msg);
+ break;
+ case LOG_RT_DATATYPE_FLOAT:
+ sprintf(rt_msg, pLogRTItem->pFormat, pLogRTItem->data.floatVar);
+ strcat((char *)pLogItem->msg, rt_msg);
+ break;
+ default:
+ break;
+ }
+
+ if (pLogRTItem->bEnd) {
+ if (OPENAVB_TCAL_LOG_EXTRA_NEWLINE)
+ strcat((char *)pLogItem->msg, "\n");
+ bMore = FALSE;
+ }
+ openavbQueueTailPull(logRTQueue);
+ }
+
+ }
+ }
+}
+
+
+U32 avbLogGetMsg(U8 *pBuf, U32 bufSize)
+{
+ U32 dataLen = 0;
+ if (logQueue) {
+ openavb_queue_elem_t elem = openavbQueueTailLock(logQueue);
+ if (elem) {
+ log_queue_item_t *pLogItem = (log_queue_item_t *)openavbQueueData(elem);
+
+ if (pLogItem->bRT)
+ avbLogRTRender(pLogItem);
+
+ dataLen = strlen((const char *)pLogItem->msg);
+ if (dataLen <= bufSize)
+ memcpy(pBuf, (U8 *)pLogItem->msg, dataLen);
+ else
+ memcpy(pBuf, (U8 *)pLogItem->msg, bufSize);
+ openavbQueueTailPull(logQueue);
+ return dataLen;
+ }
+ }
+ return dataLen;
+}
+
+void *loggingThreadFn(void *pv)
+{
+ while (loggingThreadRunning) {
+ SLEEP_MSEC(LOG_QUEUE_SLEEP_MSEC);
+
+ bool more = TRUE;
+
+ while (more) {
+ more = FALSE;
+ openavb_queue_elem_t elem = openavbQueueTailLock(logQueue);
+ if (elem) {
+ log_queue_item_t *pLogItem = (log_queue_item_t *)openavbQueueData(elem);
+
+ if (pLogItem->bRT)
+ avbLogRTRender(pLogItem);
+
+ fputs((const char *)pLogItem->msg, AVB_LOG_OUTPUT_FD);
+ openavbQueueTailPull(logQueue);
+ more = TRUE;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+extern void DLL_EXPORT avbLogInit(void)
+{
+ MUTEX_CREATE_ALT(gLogMutex);
+
+ logQueue = openavbQueueNewQueue(sizeof(log_queue_item_t), LOG_QUEUE_MSG_CNT);
+ if (!logQueue) {
+ printf("Failed to initialize logging facility\n");
+ }
+
+ logRTQueue = openavbQueueNewQueue(sizeof(log_rt_queue_item_t), LOG_RT_QUEUE_CNT);
+ if (!logRTQueue) {
+ printf("Failed to initialize logging RT facility\n");
+ }
+
+ // Start the logging task
+ if (OPENAVB_LOG_FROM_THREAD) {
+ bool errResult;
+ THREAD_CREATE(loggingThread, loggingThread, NULL, loggingThreadFn, NULL);
+ THREAD_CHECK_ERROR(loggingThread, "Thread / task creation failed", errResult);
+ if (errResult); // Already reported
+ }
+}
+
+extern void DLL_EXPORT avbLogExit()
+{
+ loggingThreadRunning = false;
+ THREAD_JOIN(loggingThread, NULL);
+}
+
+extern void DLL_EXPORT avbLogFn(
+ int level,
+ const char *tag,
+ const char *company,
+ const char *component,
+ const char *path,
+ int line,
+ const char *fmt,
+ ...)
+{
+ if (level <= AVB_LOG_LEVEL) {
+ va_list args;
+ va_start(args, fmt);
+
+ LOG_LOCK();
+
+ vsprintf(msg, fmt, args);
+
+ if (OPENAVB_LOG_FILE_INFO && path) {
+ char* file = strrchr(path, '/');
+ if (!file)
+ file = strrchr(path, '\\');
+ if (file)
+ file += 1;
+ else
+ file = (char*)path;
+ sprintf(file_msg, " %s:%d", file, line);
+ }
+ if (OPENAVB_LOG_PROC_INFO) {
+ sprintf(proc_msg, " P:%5.5d", GET_PID());
+ }
+ if (OPENAVB_LOG_THREAD_INFO) {
+ sprintf(thread_msg, " T:%lu", THREAD_SELF());
+ }
+ if (OPENAVB_LOG_TIME_INFO) {
+ time_t tNow = time(NULL);
+ struct tm tmNow;
+ localtime_r(&tNow, &tmNow);
+
+ sprintf(time_msg, "%2.2d:%2.2d:%2.2d", tmNow.tm_hour, tmNow.tm_min, tmNow.tm_sec);
+ }
+ if (OPENAVB_LOG_TIMESTAMP_INFO) {
+ struct timespec nowTS;
+ CLOCK_GETTIME(OPENAVB_CLOCK_REALTIME, &nowTS);
+
+ sprintf(timestamp_msg, "%lu:%10lu ", nowTS.tv_sec, nowTS.tv_nsec);
+ }
+
+ // using sprintf and puts allows using static buffers rather than heap.
+ if (OPENAVB_TCAL_LOG_EXTRA_NEWLINE)
+ /* S32 full_msg_len = */ sprintf(full_msg, "[%s%s%s%s %s %s%s] %s: %s\n", time_msg, timestamp_msg, proc_msg, thread_msg, company, component, file_msg, tag, msg);
+ else
+ /* S32 full_msg_len = */ sprintf(full_msg, "[%s%s%s%s %s %s%s] %s: %s", time_msg, timestamp_msg, proc_msg, thread_msg, company, component, file_msg, tag, msg);
+
+ if (!OPENAVB_LOG_FROM_THREAD && !OPENAVB_LOG_PULL_MODE) {
+ fputs(full_msg, AVB_LOG_OUTPUT_FD);
+ }
+ else {
+ if (logQueue) {
+ openavb_queue_elem_t elem = openavbQueueHeadLock(logQueue);
+ if (elem) {
+ log_queue_item_t *pLogItem = (log_queue_item_t *)openavbQueueData(elem);
+ pLogItem->bRT = FALSE;
+ strncpy((char *)pLogItem->msg, full_msg, LOG_QUEUE_MSG_LEN);
+ openavbQueueHeadPush(logQueue);
+ }
+ }
+ }
+
+ va_end(args);
+
+ LOG_UNLOCK();
+ }
+}
+
+extern void DLL_EXPORT avbLogRT(int level, bool bBegin, bool bItem, bool bEnd, char *pFormat, log_rt_datatype_t dataType, void *pVar)
+{
+ if (level <= AVB_LOG_LEVEL) {
+ if (logRTQueue) {
+ if (bBegin) {
+ LOG_LOCK();
+
+ openavb_queue_elem_t elem = openavbQueueHeadLock(logRTQueue);
+ if (elem) {
+ log_rt_queue_item_t *pLogRTItem = (log_rt_queue_item_t *)openavbQueueData(elem);
+ pLogRTItem->bEnd = FALSE;
+ pLogRTItem->pFormat = NULL;
+ pLogRTItem->dataType = LOG_RT_DATATYPE_NOW_TS;
+ CLOCK_GETTIME(OPENAVB_CLOCK_REALTIME, &pLogRTItem->data.nowTS);
+ openavbQueueHeadPush(logRTQueue);
+ }
+ }
+
+ if (bItem) {
+ openavb_queue_elem_t elem = openavbQueueHeadLock(logRTQueue);
+ if (elem) {
+ log_rt_queue_item_t *pLogRTItem = (log_rt_queue_item_t *)openavbQueueData(elem);
+ if (bEnd)
+ pLogRTItem->bEnd = TRUE;
+ else
+ pLogRTItem->bEnd = FALSE;
+ pLogRTItem->pFormat = pFormat;
+ pLogRTItem->dataType = dataType;
+
+ switch (pLogRTItem->dataType) {
+ case LOG_RT_DATATYPE_CONST_STR:
+ break;
+ case LOG_RT_DATATYPE_U16:
+ pLogRTItem->data.unsignedLongVar = *(U16 *)pVar;
+ break;
+ case LOG_RT_DATATYPE_S16:
+ pLogRTItem->data.signedLongVar = *(S16 *)pVar;
+ break;
+ case LOG_RT_DATATYPE_U32:
+ pLogRTItem->data.unsignedLongVar = *(U32 *)pVar;
+ break;
+ case LOG_RT_DATATYPE_S32:
+ pLogRTItem->data.signedLongVar = *(S32 *)pVar;
+ break;
+ case LOG_RT_DATATYPE_U64:
+ pLogRTItem->data.unsignedLongLongVar = *(U64 *)pVar;
+ break;
+ case LOG_RT_DATATYPE_S64:
+ pLogRTItem->data.signedLongLongVar = *(S64 *)pVar;
+ break;
+ case LOG_RT_DATATYPE_FLOAT:
+ pLogRTItem->data.floatVar = *(float *)pVar;
+ break;
+ default:
+ break;
+ }
+ openavbQueueHeadPush(logRTQueue);
+ }
+ }
+
+ if (!bItem && bEnd) {
+ openavb_queue_elem_t elem = openavbQueueHeadLock(logRTQueue);
+ if (elem) {
+ log_rt_queue_item_t *pLogRTItem = (log_rt_queue_item_t *)openavbQueueData(elem);
+ pLogRTItem->bEnd = TRUE;
+ pLogRTItem->pFormat = NULL;
+ pLogRTItem->dataType = LOG_RT_DATATYPE_NONE;
+ openavbQueueHeadPush(logRTQueue);
+ }
+ }
+
+ if (bEnd) {
+ if (logQueue) {
+ openavb_queue_elem_t elem = openavbQueueHeadLock(logQueue);
+ if (elem) {
+ log_queue_item_t *pLogItem = (log_queue_item_t *)openavbQueueData(elem);
+ pLogItem->bRT = TRUE;
+ openavbQueueHeadPush(logQueue);
+ }
+ }
+
+ LOG_UNLOCK();
+ }
+ }
+ }
+}
+
diff --git a/lib/avtp_pipeline/util/openavb_plugin.c b/lib/avtp_pipeline/util/openavb_plugin.c new file mode 100644 index 00000000..404fa0f4 --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_plugin.c @@ -0,0 +1,75 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_array.h" +#include "openavb_plugin.h" + +#define AVB_LOG_COMPONENT "Plugin" +#include "openavb_log_pub.h" + +openavb_array_t staticMapModeleArray; +openavb_array_t staticIntfModeleArray; + +bool registerStaticMapModule(openavb_map_initialize_fn_t fn) +{ + if (!staticMapModeleArray) { + staticMapModeleArray = openavbArrayNewArray(sizeof(openavb_map_initialize_fn_t)); + if (!staticMapModeleArray) + return FALSE; + if (!openavbArraySetInitSize(staticMapModeleArray, 8)) + return FALSE; + } + + openavbArrayAdd(staticMapModeleArray, fn); + return TRUE; +} + +bool registerStaticIntfModule(openavb_intf_initialize_fn_t fn) +{ + if (!staticIntfModeleArray) { + staticIntfModeleArray = openavbArrayNewArray(sizeof(openavb_intf_initialize_fn_t)); + if (!staticIntfModeleArray) + return FALSE; + if (!openavbArraySetInitSize(staticIntfModeleArray, 8)) + return FALSE; + } + + openavbArrayAdd(staticIntfModeleArray, fn); + return TRUE; +} + + + diff --git a/lib/avtp_pipeline/util/openavb_plugin.h b/lib/avtp_pipeline/util/openavb_plugin.h new file mode 100644 index 00000000..ad8a3f66 --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_plugin.h @@ -0,0 +1,47 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#ifndef OPENAVB_PLUGIN_H +#define OPENAVB_PLUGIN_H 1 + +#include "openavb_platform.h" +#include "openavb_types.h" +#include "openavb_intf_pub.h" +#include "openavb_map_pub.h" + +// This module exist solely to allow for interface and mapping module (plugins) to be statically linked. +// There isn't good cross-platform linker support to force non-referenced functions from being removed +// from a final image. Therefore these functions exist to ensure the initizlation function for +// interface and mapping modules can have a reference as far as the linker is concerned. + +bool registerStaticMapModule(openavb_map_initialize_fn_t fn); +bool registerStaticIntfModule(openavb_intf_initialize_fn_t fn); + +#endif // OPENAVB_PLUGIN_H diff --git a/lib/avtp_pipeline/util/openavb_printbuf.c b/lib/avtp_pipeline/util/openavb_printbuf.c new file mode 100644 index 00000000..ebcc3726 --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_printbuf.c @@ -0,0 +1,112 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Implementation of buffered printf for debugging purposes. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#include "openavb_types_pub.h" +#include "openavb_trace_pub.h" +#include "openavb_printbuf.h" + +#define OPENAVB_PRINTBUF_MAX_MSG_SIZE 1024 + +struct openavb_printbuf { + U32 size; + U32 idx; + U32 outputInterval; + U32 cnt; + U8 *buffer; +}; + +openavb_printbuf_t openavbPrintbufNew(U32 size, U32 outputInterval) +{ + openavb_printbuf_t printbuf = calloc(1, sizeof(struct openavb_printbuf)); + if (printbuf) { + printbuf->buffer = calloc(1, size + 1); // Leave 1 for the string terminater + if (printbuf->buffer) { + printbuf->size = size; + printbuf->outputInterval = outputInterval; + } + } + return printbuf; +} + +void openavbPrintbufDelete(openavb_printbuf_t printbuf) +{ + if (printbuf) { + if (printbuf->buffer) { + free(printbuf->buffer); + printbuf->buffer = NULL; + } + free(printbuf); + } +} + +void openavbPrintbufOutput(openavb_printbuf_t printbuf) +{ + if (printbuf && printbuf->buffer) { + printf("%s", printbuf->buffer); + printbuf->idx = 0; + printbuf->cnt = 0; + } +} + +void openavbPrintbufPrintf(openavb_printbuf_t printbuf, const char *fmt, ...) +{ + if (printbuf) { + va_list args; + va_start(args, fmt); + + char msg[OPENAVB_PRINTBUF_MAX_MSG_SIZE]; + U32 len = vsprintf(msg, fmt, args); + + if (printbuf->idx + len > printbuf->size) { + openavbPrintbufOutput(printbuf); + } + + memcpy(printbuf->buffer + printbuf->idx, msg, len); + printbuf->cnt++; + printbuf->idx += len; + + if (printbuf->cnt >= printbuf->outputInterval) { + openavbPrintbufOutput(printbuf); + } + + va_end(args); + } +} + + diff --git a/lib/avtp_pipeline/util/openavb_printbuf.h b/lib/avtp_pipeline/util/openavb_printbuf.h new file mode 100644 index 00000000..57e6c015 --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_printbuf.h @@ -0,0 +1,54 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Implementation of buffered printf for debugging purposes. +*/ + +#ifndef AVB_PRINTBUF_H +#define AVB_PRINTBUF_H 1 + +#include "openavb_types.h" + +typedef struct openavb_printbuf * openavb_printbuf_t; + +// Create a new printbuf +openavb_printbuf_t openavbPrintbufNew(U32 size, U32 outputInterval); + +// Delete a printbuf +void openavbPrintbufDelete(openavb_printbuf_t printbuf); + +// Output the buffer to stdio +void openavbPrintbufOutput(openavb_printbuf_t printbuf); + +// Format and add a message to the buffer +void openavbPrintbufPrintf(openavb_printbuf_t printbuf, const char *fmt, ...); + +#endif // AVB_PRINTBUF_H diff --git a/lib/avtp_pipeline/util/openavb_queue.c b/lib/avtp_pipeline/util/openavb_queue.c new file mode 100644 index 00000000..65b479f4 --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_queue.c @@ -0,0 +1,196 @@ +/*************************************************************************************************************
+Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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.
+
+Attributions: The inih library portion of the source code is licensed from
+Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt.
+Complete license and copyright information can be found at
+https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
+*************************************************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "openavb_debug.h"
+#include "openavb_queue.h"
+
+OPENAVB_CODE_MODULE_PRI
+
+struct openavb_queue_elem {
+ bool setFlg;
+ void *data;
+};
+
+struct openavb_queue {
+ // Size of each element
+ U32 elemSize;
+
+ // Number of queue element slots
+ U32 queueSize;
+
+ // Next element to be filled
+ int head;
+
+ // Next element to be pulled
+ int tail;
+
+ openavb_queue_elem_t elemArray;
+};
+
+openavb_queue_t openavbQueueNewQueue(U32 elemSize, U32 queueSize)
+{
+ if (elemSize < 1 || queueSize < 1)
+ return NULL;
+
+ openavb_queue_t retQueue = calloc(1, sizeof(struct openavb_queue));
+ if (retQueue) {
+ retQueue->elemArray = calloc(queueSize, sizeof(struct openavb_queue_elem));
+ if (retQueue->elemArray) {
+ U32 i1;
+ for (i1 = 0; i1 < queueSize; i1++) {
+ retQueue->elemArray[i1].data = calloc(1, elemSize);
+ if (!retQueue->elemArray[i1].data) {
+ openavbQueueDeleteQueue(retQueue);
+ return NULL;
+ }
+ }
+ }
+ else {
+ openavbQueueDeleteQueue(retQueue);
+ return NULL;
+ }
+
+ retQueue->elemSize = elemSize;
+ retQueue->queueSize = queueSize;
+ retQueue->head = 0;
+ retQueue->tail = 0;
+ }
+
+ return retQueue;
+}
+
+void openavbQueueDeleteQueue(openavb_queue_t queue)
+{
+ if (queue) {
+ U32 i1;
+ for (i1 = 0; i1 < queue->queueSize; i1++) {
+ free(queue->elemArray[i1].data);
+ queue->elemArray[i1].data = NULL;
+ }
+ free(queue->elemArray);
+ queue->elemArray = NULL;
+ free(queue);
+ }
+}
+
+U32 openavbQueueGetQueueSize(openavb_queue_t queue)
+{
+ if (queue) {
+ return queue->queueSize;
+ }
+ return 0;
+}
+
+U32 openavbQueueGetElemCount(openavb_queue_t queue)
+{
+ U32 cnt = 0;
+ if (queue) {
+ if (queue->head > queue->tail) {
+ cnt += queue->head - queue->tail - 1;
+ }
+ else if (queue->head < queue->tail) {
+ cnt += queue->head + ((queue->queueSize - 1) - queue->tail);
+ }
+
+ if (queue->elemArray[queue->tail].setFlg) {
+ cnt++;
+ }
+ }
+ return cnt;
+}
+
+U32 openavbQueueGetElemSize(openavb_queue_t queue)
+{
+ if (queue) {
+ return queue->elemSize;
+ }
+ return 0;
+}
+
+void *openavbQueueData(openavb_queue_elem_t elem)
+{
+ if (elem) {
+ return elem->data;
+ }
+ return NULL;
+}
+
+openavb_queue_elem_t openavbQueueHeadLock(openavb_queue_t queue)
+{
+ if (queue) {
+ if (!queue->elemArray[queue->head].setFlg) {
+ return &queue->elemArray[queue->head];
+ }
+ }
+ return NULL;
+}
+
+void openavbQueueHeadUnlock(openavb_queue_t queue)
+{
+}
+
+void openavbQueueHeadPush(openavb_queue_t queue)
+{
+ if (queue) {
+ queue->elemArray[queue->head++].setFlg = TRUE;
+ if (queue->head >= queue->queueSize) {
+ queue->head = 0;
+ }
+ }
+}
+
+openavb_queue_elem_t openavbQueueTailLock(openavb_queue_t queue)
+{
+ if (queue) {
+ if (queue->elemArray[queue->tail].setFlg) {
+ return &queue->elemArray[queue->tail];
+ }
+ }
+ return NULL;
+}
+
+void openavbQueueTailUnlock(openavb_queue_t queue)
+{
+}
+
+void openavbQueueTailPull(openavb_queue_t queue)
+{
+ if (queue) {
+ queue->elemArray[queue->tail++].setFlg = FALSE;
+ if (queue->tail >= queue->queueSize) {
+ queue->tail = 0;
+ }
+ }
+}
diff --git a/lib/avtp_pipeline/util/openavb_queue.h b/lib/avtp_pipeline/util/openavb_queue.h new file mode 100644 index 00000000..6d1a8897 --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_queue.h @@ -0,0 +1,85 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Interface for a basic dynamic array abstraction. +* +* - Fixed size queue. +* - Only head and tail access possible. +* - Head and Tail locking. +* - If there is a single task accessing head and a single task accessing tail no synchronization is needed. +* - If synchronization is needed the Pull and Push functions should be protected before calling. +*/ + +#ifndef OPENAVB_QUEUE_H +#define OPENAVB_QUEUE_H 1 + +#include "openavb_types.h" + +typedef struct openavb_queue_elem * openavb_queue_elem_t; +typedef struct openavb_queue * openavb_queue_t; + +// Create an queue. Returns NULL on failure. +openavb_queue_t openavbQueueNewQueue(U32 elemSize, U32 queueSize); + +// Delete an array. +void openavbQueueDeleteQueue(openavb_queue_t queue); + +// Get number of queue slots +U32 openavbQueueGetQueueSize(openavb_queue_t queue); + +// Get number of element +U32 openavbQueueGetElemCount(openavb_queue_t queue); + +// Get element size +U32 openavbQueueGetElemSize(openavb_queue_t queue); + +// Get data of the element. Returns NULL on failure. +void *openavbQueueData(openavb_queue_elem_t elem); + +// Lock the head element. +openavb_queue_elem_t openavbQueueHeadLock(openavb_queue_t queue); + +// Unlock the head element. +void openavbQueueHeadUnlock(openavb_queue_t queue); + +// Push the head element making it available for tail access. +void openavbQueueHeadPush(openavb_queue_t queue); + +// Lock the tail element. +openavb_queue_elem_t openavbQueueTailLock(openavb_queue_t queue); + +// Unlock the tail element. +void openavbQueueTailUnlock(openavb_queue_t queue); + +// Pull (remove) the tail element +void openavbQueueTailPull(openavb_queue_t queue); + +#endif // OPENAVB_QUEUE_H diff --git a/lib/avtp_pipeline/util/openavb_result_codes.c b/lib/avtp_pipeline/util/openavb_result_codes.c new file mode 100755 index 00000000..07b0f796 --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_result_codes.c @@ -0,0 +1,174 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +#include "openavb_types.h" +#include "openavb_result_codes.h" + +openavbRC openavbUtilRCRecord(openavbRC rc) +{ + return rc; +} + +char *openavbUtilRCResultToString(openavbRC rc) +{ + if (IS_OPENAVB_SUCCESS(rc)) return "Success"; + else return "Failure"; +} + +char *openavbUtilRCModuleToString(openavbRC rc) +{ + switch (rc & OPENAVB_RC_MODULE_MASK) { + case OPENAVB_MODULE_GLOBAL: return "AVB"; + case OPENAVB_MODULE_GPTP: return "gPTP"; + case OPENAVB_MODULE_SRP: return "SRP"; + case OPENAVB_MODULE_AVTP: return "AVTP"; + case OPENAVB_MODULE_AVTP_TIME: return "AVTP TIME"; + case OPENAVB_MODULE_AVDECC: return "AVDECC"; + default: return "Unknown"; + } +} + +char *openavbUtilRCCodeToString(openavbRC rc) +{ + // General result codes + switch (rc & OPENAVB_RC_CODE_MASK) { + case OPENAVB_RC_GENERIC: return ""; + case OPENAVB_RC_RAWSOCK_OPEN: return "Failed to open rawsock"; + case OPENAVB_RC_OUT_OF_MEMORY: return "Out of memory"; + case OPENAVB_RC_INVALID_ARGUMENT: return "Invalid function argument"; + case OPENAVB_RC_FAILED_TO_OPEN: return "Failed to open"; + // No Default + } + + // gPTP Result Codes + if ((rc & OPENAVB_RC_MODULE_MASK) == OPENAVB_MODULE_GPTP) { + switch (rc & OPENAVB_RC_CODE_MASK) { + case OPENAVBPTP_RC_GENERIC: return ""; + case OPENAVBPTP_RC_SHARED_MEMORY_OPEN: return "Failed to open shared memory file"; + case OPENAVBPTP_RC_SHARED_MEMORY_TRANC: return "Failed to truncate shared memory file"; + case OPENAVBPTP_RC_SHARED_MEMORY_MMAP: return "Failed to memory map shared memory file"; + case OPENAVBPTP_RC_SHARED_MEMORY_ENTRY: return "Failed to locate matching shared memory item"; + case OPENAVBPTP_RC_SHARED_MEMORY_UPDATE: return "Failed to update shared memory item"; + case OPENAVBPTP_RC_PTP_DEV_OPEN: return "Failed to open ptp device"; + case OPENAVBPTP_RC_PTP_DEV_CLOCKID: return "Failed to obtain ptp device clock ID"; + case OPENAVBPTP_RC_SOCK_OPEN: return "Failed to open socket"; + case OPENAVBPTP_RC_SOCK_NET_INTERFACE: return "Unable to obtain network interface"; + case OPENAVBPTP_RC_SOCK_DEVICE_INDEX: return "Unable to obtain socket device index"; + case OPENAVBPTP_RC_SOCK_REUSE: return "Unable to reuse socket"; + case OPENAVBPTP_RC_SOCK_BIND: return "Unable to bind socket"; + case OPENAVBPTP_RC_SOCK_TIMESTAMP: return "Hardware timestamping not supported"; + case OPENAVBPTP_RC_SOCK_LINK_DOWN: return "Socket network link not active"; + case OPENAVBPTP_RC_TIMER_CREATE: return "Failed to create timer(s)"; + case OPENAVBPTP_RC_SIGNAL_HANDLER: return "Failed to create signal handler"; + case OPENAVBPTP_RC_CONFIG_FILE_OPEN: return "Failed to open configuration file"; + case OPENAVBPTP_RC_CONFIG_FILE_READ: return "Failed to read configuration file"; + case OPENAVBPTP_RC_CONFIG_FILE_DATA: return "Invalid data encountered in configuration file"; + case OPENAVBPTP_RC_CONFIG_FILE_WRITE: return "Failed to write configuration file"; + case OPENAVBPTP_RC_NEW_CONFIG_FILE_WRITE: return "SUCCESSFULLY wrote recreated configuration file"; + case OPENAVBPTP_RC_CLOCK_GET_TIME: return "Failed to obtain time"; + case OPENAVBPTP_RC_SEND_FAIL: return "Failed to send packet"; + case OPENAVBPTP_RC_SEND_SHORT: return "Failed to send complete packet"; + case OPENAVBPTP_RC_SOCK_ADD_MULTI: return "Failed to set multicast address"; + // No Default + } + } + + + // SRP Result Codes + if ((rc & OPENAVB_RC_MODULE_MASK) == OPENAVB_MODULE_SRP) { + switch (rc & OPENAVB_RC_CODE_MASK) { + case OPENAVBSRP_RC_GENERIC: return ""; + case OPENAVBSRP_RC_ALREADY_INIT: return "Already initialized"; + case OPENAVBSRP_RC_SOCK_OPEN: return "Failed to open socket"; + case OPENAVBSRP_RC_THREAD_CREATE: return "Failed to create thread"; + case OPENAVBSRP_RC_BAD_CLASS: return "Attempt to register / access stream with a bad class index"; + case OPENAVBSRP_RC_REG_NULL_HDL: return "Attempt to register a stream with a null handle"; + case OPENAVBSRP_RC_REREG: return "Attempt to [re]register an exisiting stream Id"; + case OPENAVBSRP_RC_NO_MEMORY: return "Out of memory"; + case OPENAVBSRP_RC_NO_BANDWIDTH: return "Insufficient bandwidth"; + case OPENAVBSRP_RC_SEND: return "Failed to send packet"; + case OPENAVBSRP_RC_DEREG_NO_XST: return "Attempt to deregister a non-existent stream"; + case OPENAVBSRP_RC_DETACH_NO_XST: return "Attempt to detach from non-existent stream"; + case OPENAVBSRP_RC_INVALID_SUBTYPE: return "Invalid listener declaration subtype"; + case OPENAVBSRP_RC_FRAME_BUFFER: return "Failed to get a transmit frame buffer"; + case OPENAVBSRP_RC_SHARED_MEM_MMAP: return "Failed to mmap openavb_gPTP shared memory"; + case OPENAVBSRP_RC_SHARED_MEM_OPEN: return "Failed to open openavb_gPTP shared memory"; + case OPENAVBSRP_RC_BAD_VERSION: return "AVB core stack version mismatch endpoint / srp vs gptp"; + // No Default + } + } + + // AVTP Result Codes + if ((rc & OPENAVB_RC_MODULE_MASK) == OPENAVB_MODULE_AVTP) { + switch (rc & OPENAVB_RC_CODE_MASK) { + case OPENAVBAVTP_RC_GENERIC: return ""; + case OPENAVBAVTP_RC_TX_PACKET_NOT_READY: return "Transmit packet not ready"; + case OPENAVBAVTP_RC_MAPPING_CB_NOT_SET: return "Mapping callback structure not set"; + case OPENAVBAVTP_RC_INTERFACE_CB_NOT_SET: return "Interface callback structure not set"; + case OPENAVBAVTP_RC_NO_FRAMES_PROCESSED: return "No frames processed"; + case OPENAVBAVTP_RC_INVALID_AVTP_VERSION: return "Invalid AVTP version configured"; + case OPENAVBAVTP_RC_IGNORING_STREAMID: return "Ignoring unexpected streamID"; + case OPENAVBAVTP_RC_IGNORING_CONTROL_PACKET: return "Ignoring unexpected AVTP control packet"; + case OPENAVBAVTP_RC_PARSING_FRAME_HEADER: return "Parsing frame header"; + // No Default + } + } + + // AVTP Time Result Codes + if ((rc & OPENAVB_RC_MODULE_MASK) == OPENAVB_MODULE_AVTP_TIME) { + switch (rc & OPENAVB_RC_CODE_MASK) { + case OPENAVBAVTPTIME_RC_GENERIC: return ""; + case OPENAVBAVTPTIME_RC_PTP_TIME_DESCRIPTOR: return "PTP file descriptor not available"; + case OPENAVBAVTPTIME_RC_OPEN_SRC_PTP_NOT_AVAIL: return "Open source PTP configured but not available in this build"; + case OPENAVBAVTPTIME_RC_GET_TIME_ERROR: return "PTP get time error"; + case OPENAVBAVTPTIME_RC_OPENAVB_PTP_NOT_AVAIL: return "OPENAVB PTP configured but not available in this build"; + case OPENAVBAVTPTIME_RC_INVALID_PTP_TIME: return "Invalid avtp_time_t"; + // No Default + } + } + + // AVDECC Result Codes + if ((rc & OPENAVB_RC_MODULE_MASK) == OPENAVB_MODULE_AVDECC) { + switch (rc & OPENAVB_RC_CODE_MASK) { + case OPENAVBAVDECC_RC_GENERIC: return ""; + case OPENAVBAVDECC_RC_BUFFER_TOO_SMALL: return "Buffer size is too small"; + case OPENAVBAVDECC_RC_ENTITY_MODEL_MISSING: return "The Entity Model has not been created"; + case OPENAVBAVDECC_RC_INVALID_CONFIG_IDX: return "Referenced an invalid configuration descriptor index"; + case OPENAVBAVDECC_RC_PARSING_MAC_ADDRESS: return "Parsing Mac Address"; + case OPENAVBAVDECC_RC_UNKNOWN_DESCRIPTOR: return "Unknown descriptor"; + // No Default + + } + } + + return ""; +} + + diff --git a/lib/avtp_pipeline/util/openavb_result_codes.h b/lib/avtp_pipeline/util/openavb_result_codes.h new file mode 100644 index 00000000..0aea33f1 --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_result_codes.h @@ -0,0 +1,209 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Common types and defines for result codes +*/ + +#ifndef AVB_RESULT_CODES_H +#define AVB_RESULT_CODES_H 1 + +// Result Code (success or error code) +// First (left) byte: +// bits 0 thru 6 reserved; +// bit 7 (rightmost) - 0 indicates success, 1 indicates failure. +// Second Byte: +// indicates specific software module - see enum openavbModules below +// Third & Forth (right) Bytes; +// defined independently by each module to indicate specific failure (or success) +typedef U32 openavbRC; + +// Example usage: +// openavbRC foo() +// { +// . +// . +// return AVB_RC(OPENAVB_AVTP_FAILURE | OPENAVB_RC_OUT_OF_MEMORY) +// } +// +// openavbRC result; +// result = foo(); +// if (IS_OPENAVB_SUCCESS(result)) { +// ... +// } + +#define OPENAVB_RC_MODULE_MASK 0x00FF0000 +#define OPENAVB_RC_CODE_MASK 0x0000FFFF + +#define OPENAVB_SUCCESS 0x00000000 +#define OPENAVB_FAILURE 0x01000000 + +// 2nd byte only +enum openavbModules { + OPENAVB_MODULE_GLOBAL = 0x00010000, + OPENAVB_MODULE_GPTP = 0x00020000, + OPENAVB_MODULE_SRP = 0x00030000, + OPENAVB_MODULE_AVTP = 0x00040000, + OPENAVB_MODULE_AVTP_TIME = 0x00050000, + OPENAVB_MODULE_AVDECC = 0x00060000, +}; + +// When adding result codes be sure to update the openavbUtilRCCodeToString() function in openavb_result_codes.c + +enum openavbCommonResultCodes { + OPENAVB_RC_GENERIC = 0x1000, + OPENAVB_RC_RAWSOCK_OPEN = 0x1001, // Failed to open rawsock + OPENAVB_RC_OUT_OF_MEMORY = 0x1002, // Out of memory + OPENAVB_RC_INVALID_ARGUMENT = 0x1003, // Invalid function argument + OPENAVB_RC_FAILED_TO_OPEN = 0x1004, // Failed to open +}; + +enum openavbPtpResultCodes { + OPENAVBPTP_RC_GENERIC = 0x0000, + OPENAVBPTP_RC_SHARED_MEMORY_OPEN = 0x0001, // Failed to open shared memory file + OPENAVBPTP_RC_SHARED_MEMORY_TRANC = 0x0002, // Failed to truncate shared memory file + OPENAVBPTP_RC_SHARED_MEMORY_MMAP = 0x0003, // Failed to memory map shared memory file + OPENAVBPTP_RC_SHARED_MEMORY_ENTRY = 0x0004, // Failed to locate matching shared memory item + OPENAVBPTP_RC_SHARED_MEMORY_UPDATE = 0x0005, // Failed to update shared memory item + OPENAVBPTP_RC_PTP_DEV_OPEN = 0x0006, // Failed to open ptp device + OPENAVBPTP_RC_PTP_DEV_CLOCKID = 0x0007, // Failed to obtain ptp device clock ID + OPENAVBPTP_RC_SOCK_OPEN = 0x0008, // Failed to open socket + OPENAVBPTP_RC_SOCK_NET_INTERFACE = 0x0009, // Unable to obtain network interface + OPENAVBPTP_RC_SOCK_DEVICE_INDEX = 0x0010, // Unable to obtain socket device index // + OPENAVBPTP_RC_SOCK_REUSE = 0x0011, // unable to reuse socket + OPENAVBPTP_RC_SOCK_BIND = 0x0012, // Unable to bind socket + OPENAVBPTP_RC_SOCK_TIMESTAMP = 0x0013, // Hardware timestamping not supported + OPENAVBPTP_RC_SOCK_LINK_DOWN = 0x0014, // Socket network link not active + OPENAVBPTP_RC_TIMER_CREATE = 0x0015, // Failed to create timer(s) + OPENAVBPTP_RC_SIGNAL_HANDLER = 0x0016, // Failed to create signal handler + OPENAVBPTP_RC_CONFIG_FILE_OPEN = 0x0017, // Failed to open configuration file + OPENAVBPTP_RC_CONFIG_FILE_READ = 0x0018, // Failed to read configuration file + OPENAVBPTP_RC_CONFIG_FILE_DATA = 0x0019, // Invalid data encountered in configuration file + OPENAVBPTP_RC_CONFIG_FILE_WRITE = 0x0020, // Failed to write configuration file + OPENAVBPTP_RC_NEW_CONFIG_FILE_WRITE = 0x0021, // SUCCESSFULLY wrote recreated configuration file + OPENAVBPTP_RC_CLOCK_GET_TIME = 0x0022, // Failed to obtain time + OPENAVBPTP_RC_SEND_FAIL = 0x0023, // Failed to send packet + OPENAVBPTP_RC_SEND_SHORT = 0x0024, // Failed to send complete packet + OPENAVBPTP_RC_SOCK_ADD_MULTI = 0x0025, // Failed to set multicast address + OPENAVBPTP_RC_TX_TIMESTAMP_FAIL = 0x0026, // Failed to get egress timestamp +}; + +enum openavbSrpResultCodes { + OPENAVBSRP_RC_GENERIC = 0x0000, + OPENAVBSRP_RC_ALREADY_INIT = 0x0001, // Already initialized + OPENAVBSRP_RC_SOCK_OPEN = 0x0002, // Failed to open socket + OPENAVBSRP_RC_THREAD_CREATE = 0x0003, // Failed to create thread + OPENAVBSRP_RC_BAD_CLASS = 0x0004, // Attempt to register / access stream with a bad class index + OPENAVBSRP_RC_REG_NULL_HDL = 0x0005, // Attempt to register a stream with a null handle + OPENAVBSRP_RC_REREG = 0x0006, // Attempt to [re]register an exisiting stream Id + OPENAVBSRP_RC_NO_MEMORY = 0x0007, // Out of memory + OPENAVBSRP_RC_NO_BANDWIDTH = 0x0008, // Insufficient bandwidth + OPENAVBSRP_RC_SEND = 0x0009, // Failed to send packet + OPENAVBSRP_RC_DEREG_NO_XST = 0x0010, // Attempt to deregister a non-existent stream + OPENAVBSRP_RC_DETACH_NO_XST = 0x0011, // Attempt to detach from non-existent stream + OPENAVBSRP_RC_INVALID_SUBTYPE = 0x0012, // Invalid listener declaration subtype + OPENAVBSRP_RC_FRAME_BUFFER = 0x0013, // Failed to get a transmit frame buffer + OPENAVBSRP_RC_SHARED_MEM_MMAP = 0x0014, // Failed to mmap openavb_gPTP shared memory + OPENAVBSRP_RC_SHARED_MEM_OPEN = 0x0015, // Failed to open openavb_gPTP shared memory + OPENAVBSRP_RC_BAD_VERSION = 0x0016, // AVB core stack version mismatch endpoint / srp vs gptp + OPENAVBSRP_RC_SOCK_JN_MULTI = 0x0017, // Failed to join multicast group for the Nearest Bridge group address + OPENAVBSRP_RC_SOCK_ADDR = 0x0018, // Failed to get our own mac address + OPENAVBSRP_RC_MUTEX_INIT = 0x0019, // Failed to create / initialize mutex + OPENAVBSRP_RC_SOCK_TX_HDR = 0x0020, // Failed to set Ethernet frame transmit header +// limited to two bytes; max 0xffff +}; + +enum openavbAVTPResultCodes { + OPENAVBAVTP_RC_GENERIC = 0x0000, + OPENAVBAVTP_RC_TX_PACKET_NOT_READY = 0x0001, // Transmit packet not ready + OPENAVBAVTP_RC_MAPPING_CB_NOT_SET = 0x0002, // Mapping callback structure not set + OPENAVBAVTP_RC_INTERFACE_CB_NOT_SET = 0x0003, // Interface callback structure not set + OPENAVBAVTP_RC_NO_FRAMES_PROCESSED = 0x0004, // No frames processed + OPENAVBAVTP_RC_INVALID_AVTP_VERSION = 0x0005, // Invalid AVTP version configured + OPENAVBAVTP_RC_IGNORING_STREAMID = 0x0006, // Ignoring unexpected streamID + OPENAVBAVTP_RC_IGNORING_CONTROL_PACKET = 0x0007, // Ignoring unexpected AVTP control packet + OPENAVBAVTP_RC_PARSING_FRAME_HEADER = 0x0008, // Parsing frame header +}; + +enum openavbAVTPTimeResultCodes { + OPENAVBAVTPTIME_RC_GENERIC = 0x0000, + OPENAVBAVTPTIME_RC_PTP_TIME_DESCRIPTOR = 0x0001, // PTP file descriptor not available + OPENAVBAVTPTIME_RC_OPEN_SRC_PTP_NOT_AVAIL = 0x0002, // Open source PTP configured but not available in this build + OPENAVBAVTPTIME_RC_GET_TIME_ERROR = 0x0003, // PTP get time error + OPENAVBAVTPTIME_RC_OPENAVB_PTP_NOT_AVAIL = 0x0004, // OPENAVB PTP configured but not available in this build + OPENAVBAVTPTIME_RC_INVALID_PTP_TIME = 0x0005, // Invalid avtp_time_t +}; + +enum openavbAVDECCResultCodes { + OPENAVBAVDECC_RC_GENERIC = 0x0000, + OPENAVBAVDECC_RC_BUFFER_TOO_SMALL = 0x0001, // Buffer size is too small + OPENAVBAVDECC_RC_ENTITY_MODEL_MISSING = 0x0002, // The Entity Model has not been created + OPENAVBAVDECC_RC_INVALID_CONFIG_IDX = 0x0003, // Referenced an invalid configuration descriptor index + OPENAVBAVDECC_RC_PARSING_MAC_ADDRESS = 0x0004, // Parsing Mac Address + OPENAVBAVDECC_RC_UNKNOWN_DESCRIPTOR = 0x0005, // Unknown descriptor +}; + + +#define OPENAVB_PTP_SUCCESS (OPENAVB_SUCCESS | OPENAVB_MODULE_GPTP) +#define OPENAVB_PTP_FAILURE (OPENAVB_FAILURE | OPENAVB_MODULE_GPTP) + +#define OPENAVB_SRP_SUCCESS (OPENAVB_SUCCESS | OPENAVB_MODULE_SRP) +#define OPENAVB_SRP_FAILURE (OPENAVB_FAILURE | OPENAVB_MODULE_SRP) + +#define OPENAVB_AVTP_SUCCESS (OPENAVB_SUCCESS | OPENAVB_MODULE_AVTP) +#define OPENAVB_AVTP_FAILURE (OPENAVB_FAILURE | OPENAVB_MODULE_AVTP) + +#define OPENAVB_AVTP_TIME_SUCCESS (OPENAVB_SUCCESS | OPENAVB_MODULE_AVTP_TIME) +#define OPENAVB_AVTP_TIME_FAILURE (OPENAVB_FAILURE | OPENAVB_MODULE_AVTP_TIME) + +#define OPENAVB_AVDECC_SUCCESS (OPENAVB_SUCCESS | OPENAVB_MODULE_AVDECC) +#define OPENAVB_AVDECC_FAILURE (OPENAVB_FAILURE | OPENAVB_MODULE_AVDECC) + + +// Helper functions +openavbRC openavbUtilRCRecord(openavbRC rc); +char* openavbUtilRCResultToString(openavbRC rc); +char* openavbUtilRCModuleToString(openavbRC rc); +char* openavbUtilRCCodeToString(openavbRC rc); + +// Helper macros +#define IS_OPENAVB_SUCCESS(_rc_) (!((_rc_) & 0x01000000)) +#define IS_OPENAVB_FAILURE(_rc_) ((_rc_) & 0x01000000) +#define AVB_RC(_rc_) (openavbUtilRCRecord(_rc_)) +#define AVB_RC_RET(_rc_) return (_rc_) +#define AVB_RC_MSG(_rc_) openavbUtilRCCodeToString(_rc_) +#define AVB_RC_LOG(_rc_) AVB_LOGF_ERROR("%s", openavbUtilRCCodeToString(_rc_)) +#define AVB_RC_LOG_RET(_rc_) AVB_LOGF_ERROR("%s", openavbUtilRCCodeToString(_rc_)); return (_rc_) +#define AVB_RC_LOG_TRACE_RET(_rc_, _trace_) AVB_LOGF_ERROR("%s", openavbUtilRCCodeToString(_rc_)); AVB_TRACE_EXIT(_trace_); return (_rc_) +#define AVB_RC_TRACE_RET(_rc_, _trace_) AVB_TRACE_EXIT(_trace_); return (_rc_) +#define AVB_RC_FAIL_RET(_rc_) if (IS_OPENAVB_FAILURE(_rc_)) return (_rc_) +#define AVB_RC_FAIL_TRACE_RET(_rc_, _trace_) if (IS_OPENAVB_FAILURE(_rc_)) {AVB_TRACE_EXIT(_trace_); return (_rc_);} + +#endif // AVB_RESULT_CODES_H diff --git a/lib/avtp_pipeline/util/openavb_time.c b/lib/avtp_pipeline/util/openavb_time.c new file mode 100644 index 00000000..73ef800e --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_time.c @@ -0,0 +1,134 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Implementation of time support functions. +*/ + +#include "openavb_time.h" + +OPENAVB_CODE_MODULE_PRI + +U64 openavbTimeTimespecToNSec(struct timespec *pTime) +{ + if (!pTime) { + return 0; // Error case + } + + return ((U64)pTime->tv_sec * (U64)NANOSECONDS_PER_SECOND) + (U64)pTime->tv_nsec; +} + +void openavbTimeTimespecFromNSec(struct timespec *pTime, U64 nSec) +{ + if (!pTime) { + return; // Error case + } + + pTime->tv_sec = nSec / NANOSECONDS_PER_SECOND; + pTime->tv_nsec = nSec % NANOSECONDS_PER_SECOND; +} + +void openavbTimeTimespecAddUsec(struct timespec *pTime, U32 us) +{ + if (!pTime) { + return; // Error case - undefined behavior + } + + pTime->tv_nsec += us * NANOSECONDS_PER_USEC; + pTime->tv_sec += pTime->tv_nsec / NANOSECONDS_PER_SECOND; + pTime->tv_nsec = pTime->tv_nsec % NANOSECONDS_PER_SECOND; +} + +void openavbTimeTimespecSubUsec(struct timespec *pTime, U32 us) +{ + if (!pTime) { + return; // Error case - undefined behavior + } + + U64 nSec = ((U64)pTime->tv_sec * (U64)NANOSECONDS_PER_SECOND) + (U64)pTime->tv_nsec; + nSec -= us * NANOSECONDS_PER_USEC; + pTime->tv_sec = nSec / NANOSECONDS_PER_SECOND; + pTime->tv_nsec = nSec % NANOSECONDS_PER_SECOND; +} + +S64 openavbTimeTimespecUsecDiff(struct timespec *pTime1, struct timespec *pTime2) +{ + if (!pTime1 || !pTime2) { + return 0; // Error case - undefined behavior + } + + U64 timeUSec1 = (pTime1->tv_sec * MICROSECONDS_PER_SECOND) + (pTime1->tv_nsec / NANOSECONDS_PER_USEC); + U64 timeUSec2 = (pTime2->tv_sec * MICROSECONDS_PER_SECOND) + (pTime2->tv_nsec / NANOSECONDS_PER_USEC); + return timeUSec2 - timeUSec1; +} + +S32 openavbTimeTimespecCmp(struct timespec *pTime1, struct timespec *pTime2) +{ + if (!pTime1 || !pTime2) { + return -1; // Error case - undefined behavior + } + + if (pTime1->tv_sec < pTime2->tv_sec) { + return -1; + } + if (pTime1->tv_sec > pTime2->tv_sec) { + return 1; + } + if (pTime1->tv_sec == pTime2->tv_sec) { + if (pTime1->tv_nsec < pTime2->tv_nsec) { + return -1; + } + if (pTime1->tv_nsec > pTime2->tv_nsec) { + return 1; + } + } + return 0; // Equal +} + +U64 openavbTimeUntilUSec(struct timespec *pTime1, struct timespec *pTime2) +{ + if (!pTime1 || !pTime2) { + return 0; // Error case - undefined behavior + } + + U64 timeUSec1 = (pTime1->tv_sec * MICROSECONDS_PER_SECOND) + (pTime1->tv_nsec / NANOSECONDS_PER_USEC); + U64 timeUSec2 = (pTime2->tv_sec * MICROSECONDS_PER_SECOND) + (pTime2->tv_nsec / NANOSECONDS_PER_USEC); + + if (timeUSec2 > timeUSec1) { + return timeUSec2 - timeUSec1; + } + return 0; +} + +U32 openavbTimeUntilMSec(struct timespec *pTime1, struct timespec *pTime2) +{ + return openavbTimeUntilUSec(pTime1, pTime2) / 1000; +} + diff --git a/lib/avtp_pipeline/util/openavb_time.h b/lib/avtp_pipeline/util/openavb_time.h new file mode 100644 index 00000000..83d51033 --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_time.h @@ -0,0 +1,65 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Implementation of Time support functions. +*/ + +#ifndef OPENAVB_TIME_H +#define OPENAVB_TIME_H 1 + +#include <stdlib.h> +#include "openavb_types.h" + +// Returns a time in nanoseconds +U64 openavbTimeTimespecToNSec(struct timespec *pTime); + +// Sets a timespec from nanoseconds +void openavbTimeTimespecFromNSec(struct timespec *pTime, U64 nSec); + +// Add micro seconds to a timespec +void openavbTimeTimespecAddUsec(struct timespec *pTime, U32 us); + +// Subtract micro seconds from a timespec +void openavbTimeTimespecSubUsec(struct timespec *pTime, U32 us); + +// Find the difference in micro seconds between 2 timespecs +S64 openavbTimeTimespecUsecDiff(struct timespec *pTime1, struct timespec *pTime2); + +// Compares 2 timespecs and returns -1, 0 or 1 depending on the compare. +S32 openavbTimeTimespecCmp(struct timespec *pTime1, struct timespec *pTime2); + +// Microseconds until Time2 reaches Time1. Returns 0 if Time2 is already past Time1 +U64 openavbTimeUntilUSec(struct timespec *pTime1, struct timespec *pTime2); + +// Milliseconds until Time2 reaches Time1. Returns 0 if Time2 is already past Time1 +U32 openavbTimeUntilMSec(struct timespec *pTime1, struct timespec *pTime2); + +#endif // OPENAVB_TIME_H diff --git a/lib/avtp_pipeline/util/openavb_timestamp.c b/lib/avtp_pipeline/util/openavb_timestamp.c new file mode 100644 index 00000000..a5d7fd89 --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_timestamp.c @@ -0,0 +1,171 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Implementation of Timestamp evaluation useful for reporting and smoothing jitter. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "openavb_printbuf.h" +#include "openavb_timestamp.h" + +OPENAVB_CODE_MODULE_PRI + +#define OPENAVB_TIMESTAMP_PRINT_BUFFER_SIZE 2048 +#define OPENAVB_TIMESTAMP_DUMP_PRINTBUF_INTERVAL 30 + +// CORE_TODO: This should be enhanced to account for dropped packet detection and perhaps PTP time adjusts + +struct openavb_timestamp_eval { + // Flags + bool started; + + // Settings + U32 tsRateInterval; + bool smoothing; + U32 tsSmoothingMaxJitter; + U32 tsSmoothingMaxDrift; + U32 reportInterval; + + // Data + U32 tsCnt; + U32 tsPrev; + U32 tsInterval; + U32 tsJitter; + U64 tsAccumJitter; + U32 tsDrift; + U64 tsTtlReal; + U64 tsTtlCalc; + U32 tsMaxJitter; + U32 tsMaxDrift; + openavb_printbuf_t printbuf; +}; + + +openavb_timestamp_eval_t openavbTimestampEvalNew(void) +{ + return calloc(1, sizeof(struct openavb_timestamp_eval)); +} + +void openavbTimestampEvalDelete(openavb_timestamp_eval_t tsEval) +{ + if (tsEval) { + free(tsEval); + tsEval = NULL; + } +} + +void openavbTimestampEvalInitialize(openavb_timestamp_eval_t tsEval, U32 tsRateInterval) +{ + if (tsEval) { + tsEval->started = FALSE; + tsEval->tsRateInterval = tsRateInterval; + tsEval->printbuf = openavbPrintbufNew(OPENAVB_TIMESTAMP_PRINT_BUFFER_SIZE, OPENAVB_TIMESTAMP_DUMP_PRINTBUF_INTERVAL); + } +} + +void openavbTimestampEvalSetReport(openavb_timestamp_eval_t tsEval, U32 reportInterval) +{ + if (tsEval) { + tsEval->reportInterval = reportInterval; + } +} + +void openavbTimestampEvalSetSmoothing(openavb_timestamp_eval_t tsEval, U32 tsMaxJitter, U32 tsMaxDrift) +{ + if (tsEval) { + tsEval->smoothing = TRUE; + tsEval->tsSmoothingMaxJitter = tsMaxJitter; + tsEval->tsSmoothingMaxDrift = tsMaxDrift; + } +} + +U32 openavbTimestampEvalTimestamp(openavb_timestamp_eval_t tsEval, U32 ts) +{ + U32 tsRet = ts; + + if (tsEval) { + // Determine the Jitter + if (tsEval->tsPrev > ts) { + tsEval->tsInterval = (((U32)-1) - tsEval->tsPrev) + ts; + } + else { + tsEval->tsInterval = ts - tsEval->tsPrev; + } + + // Save real ts for not interval + tsEval->tsPrev = ts; + + // Increment the main timestamp counter + tsEval->tsCnt++; + + // Accumulate totals + if (!tsEval->started) { + // First timestamp interval + tsEval->tsTtlCalc = 0; + tsEval->tsTtlReal = 0; + tsEval->started = TRUE; + } + else { + // All but first timestamp + tsEval->tsTtlCalc += tsEval->tsRateInterval; + tsEval->tsTtlReal += tsEval->tsInterval; + + tsEval->tsJitter = abs(tsEval->tsRateInterval - tsEval->tsInterval); + if (tsEval->tsJitter > tsEval->tsMaxJitter) { + tsEval->tsMaxJitter = tsEval->tsJitter; + } + tsEval->tsAccumJitter += tsEval->tsJitter; + + tsEval->tsDrift = abs(tsEval->tsTtlCalc - tsEval->tsTtlReal); + + // Reporting + if (tsEval->reportInterval) { + if ((tsEval->tsCnt % tsEval->reportInterval) == 0) { + //printf("Jitter:%9u AvgJitter:%9u MaxJitter:%9u Drift:%9u\n", tsEval->tsJitter, (U32)(tsEval->tsAccumJitter / tsEval->tsCnt), tsEval->tsMaxJitter, tsEval->tsDrift); + openavbPrintbufPrintf(tsEval->printbuf, "Jitter:%9u AvgJitter:%9u MaxJitter:%9u Drift:%9u\n", tsEval->tsJitter, (U32)(tsEval->tsAccumJitter / tsEval->tsCnt), tsEval->tsMaxJitter, tsEval->tsDrift); + } + } + } + } + + return tsRet; +} + + +void openavbTimestampEvalTimestampSkip(openavb_timestamp_eval_t tsEval, U32 cnt) +{ + if (tsEval) { + } +} + diff --git a/lib/avtp_pipeline/util/openavb_timestamp.h b/lib/avtp_pipeline/util/openavb_timestamp.h new file mode 100644 index 00000000..36398f4b --- /dev/null +++ b/lib/avtp_pipeline/util/openavb_timestamp.h @@ -0,0 +1,78 @@ +/************************************************************************************************************* +Copyright (c) 2012-2013, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "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 LISTED 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. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : Header for Timestamp evaluation useful for reporting and smoothing jitter. +*/ + +#ifndef OPENAVB_TIMESTAMP_H +#define OPENAVB_TIMESTAMP_H 1 + +#include <stdlib.h> +#include "openavb_types.h" + +typedef struct openavb_timestamp_eval * openavb_timestamp_eval_t; + +// Create and initialize the timestamp evaluator. +// tsInterval is the expected timestamp interval in nanoseconds. +openavb_timestamp_eval_t openavbTimestampEvalNew(void); + +// Delete a timestamp evaluator. +void openavbTimestampEvalDelete(openavb_timestamp_eval_t tsEval); + +// Set timestamp interval. +void openavbTimestampEvalInitialize(openavb_timestamp_eval_t tsEval, U32 tsRateInterval); + +// Set the report interval. If not set there will be no reporting. +void openavbTimestampEvalSetReport(openavb_timestamp_eval_t tsEval, U32 reportInterval); + +// Set the timestamp smoothing parameters. +void openavbTimestampEvalSetSmoothing(openavb_timestamp_eval_t tsEval, U32 tsMaxJitter, U32 tsMaxDrift); + +// Record timestamp and optionally smooth it. +U32 openavbTimestampEvalTimestamp(openavb_timestamp_eval_t tsEval, U32 ts); + +// Skip cnt number of timestamp intervals +void openavbTimestampEvalTimestampSkip(openavb_timestamp_eval_t tsEval, U32 cnt); + + + + + + + + + + + + + + +#endif // OPENAVB_TIMESTAMP_H diff --git a/run_echo_talker.sh b/run_echo_talker.sh new file mode 100755 index 00000000..33712285 --- /dev/null +++ b/run_echo_talker.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Simple script to run echo_talker + +if [ "$#" -eq "0" ]; then + echo "please enter network interface name as parameter. For example:" + echo "sudo ./run_echo_talker eth1" + exit -1 +fi + +# TODO_OPENAVB : Currently assumes a bin directory. +cd ../build/bin +exec ./openavb_host echo_talker.ini,ifname=$1 diff --git a/run_gptp.sh b/run_gptp.sh new file mode 100755 index 00000000..35fd4e38 --- /dev/null +++ b/run_gptp.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Simple script to run gptp + +if [ "$#" -eq "0" ]; then + echo "please enter network interface name as parameter. For example:" + echo "sudo ./run_gptp.sh eth1" + exit -1 +fi + +groupadd ptp +daemons/gptp/linux/build/obj/daemon_cl $1 diff --git a/run_igb.sh b/run_igb.sh new file mode 100755 index 00000000..cb2bdc76 --- /dev/null +++ b/run_igb.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Simple script to run igb_avb + +if [ "$#" -eq "0" ]; then + echo "please enter network interface name as parameter. For example:" + echo "sudo ./run_igb.sh eth1" + exit -1 +fi + +rmmod igb +modprobe i2c_algo_bit +modprobe dca +modprobe ptp +insmod kmod/igb/igb_avb.ko + +ethtool -i $1 diff --git a/run_simple_talker.sh b/run_simple_talker.sh new file mode 100755 index 00000000..1421c817 --- /dev/null +++ b/run_simple_talker.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# Simple script to the simple_talker + +if [ "$#" -eq "0" ]; then + echo "please enter network interface name as parameter. For example:" + echo "sudo ./run_simple_talker.sh eth1" + exit -1 +fi + +examples/simple_talker/simple_talker -i $1 -t 2 diff --git a/run_srp.sh b/run_srp.sh new file mode 100755 index 00000000..f75c9c12 --- /dev/null +++ b/run_srp.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# Simple script to run srp + +if [ "$#" -eq "0" ]; then + echo "please enter network interface name as parameter. For example:" + echo "sudo ./run_srp.sh eth1" + exit -1 +fi + +daemons/mrpd/mrpd -s -i $1 |