summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandrew-elder <aelder@audioscience.com>2019-10-11 07:51:59 -0400
committerGitHub <noreply@github.com>2019-10-11 07:51:59 -0400
commit4303926e523328ed49963a1dc5a700ac34816de0 (patch)
tree2611e8de86b1c0aeac0f273f41ca491e4bde9ee8
parentc225346c3c61c29647f3ca0c0ad0347ea917abbb (diff)
parentf8765fd2137a25251bebbd2fbfbd5cbab388d744 (diff)
downloadOpen-AVB-4303926e523328ed49963a1dc5a700ac34816de0.tar.gz
Merge pull request #887 from Aquantia/aq_atl_hw_support
Aquantia NIC support
-rw-r--r--.gitmodules3
-rw-r--r--Makefile15
-rw-r--r--examples/atl_simple_talker/Makefile39
-rw-r--r--examples/atl_simple_talker/README29
-rw-r--r--examples/atl_simple_talker/simple_talker.c828
-rw-r--r--examples/common/Makefile6
-rw-r--r--examples/common/async_pcap_storing.c314
-rw-r--r--examples/common/async_pcap_storing.h13
-rw-r--r--examples/jackd-talker/Makefile2
-rw-r--r--examples/live_stream/Makefile2
-rw-r--r--examples/send_packet_precisely/Makefile36
-rw-r--r--examples/send_packet_precisely/README25
-rw-r--r--examples/send_packet_precisely/send_packet_precisely.c1278
-rw-r--r--examples/simple_talker/Makefile2
m---------lib/atl_avb0
-rw-r--r--lib/avtp_pipeline/avtp/openavb_avtp.c8
-rw-r--r--lib/avtp_pipeline/avtp_pipeline.mk4
-rw-r--r--lib/avtp_pipeline/include/openavb_log_pub.h10
-rwxr-xr-xlib/avtp_pipeline/include/openavb_map_pub.h16
-rwxr-xr-xlib/avtp_pipeline/map_uncmp_audio/openavb_map_uncmp_audio.c48
-rw-r--r--lib/avtp_pipeline/mcs/openavb_mcs.c2
-rw-r--r--lib/avtp_pipeline/openavb_common/CMakeLists.txt7
-rw-r--r--lib/avtp_pipeline/openavb_common/mrp_client.c2
-rw-r--r--lib/avtp_pipeline/platform/Linux/CMakeLists.txt18
-rw-r--r--lib/avtp_pipeline/platform/Linux/rawsock/atl_rawsock.c271
-rw-r--r--lib/avtp_pipeline/platform/Linux/rawsock/atl_rawsock.h67
-rw-r--r--lib/avtp_pipeline/platform/Linux/rawsock/openavb_rawsock.c20
-rw-r--r--lib/avtp_pipeline/platform/Linux/x86_aqc_linux.cmake52
-rw-r--r--lib/avtp_pipeline/platform/Linux/x86_i210_linux.cmake6
-rw-r--r--lib/avtp_pipeline/platform/x86_aqc/mcr/openavb_mcr_hal.c63
-rw-r--r--lib/avtp_pipeline/platform/x86_aqc/mcr/openavb_mcr_hal.h38
-rw-r--r--lib/avtp_pipeline/platform/x86_aqc/openavb_atl.c278
-rw-r--r--lib/avtp_pipeline/platform/x86_aqc/openavb_atl.h54
-rw-r--r--lib/avtp_pipeline/platform/x86_aqc/openavb_hal.h39
-rw-r--r--lib/avtp_pipeline/qmgr/openavb_qmgr.c33
-rw-r--r--lib/avtp_pipeline/rawsock/CMakeLists.txt8
-rw-r--r--lib/avtp_pipeline/tl/openavb_talker.c2
-rw-r--r--lib/avtp_pipeline/util/openavb_log.c25
-rw-r--r--lib/common/Makefile10
-rw-r--r--lib/common/avb.h5
-rw-r--r--lib/common/avb_atl.c62
-rw-r--r--lib/common/avb_atl.h7
-rwxr-xr-xrun_atl.sh17
43 files changed, 3739 insertions, 25 deletions
diff --git a/.gitmodules b/.gitmodules
index b3ff098d..77209f59 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,3 +7,6 @@
[submodule "lib/igb_avb"]
path = lib/igb_avb
url = https://github.com/AVnu/igb_avb.git
+[submodule "lib/atl_avb"]
+ path = lib/atl_avb
+ url = https://github.com/Aquantia/atl_avb
diff --git a/Makefile b/Makefile
index 29926fe4..f31b002a 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@ descend = \
help:
@echo 'Possible targets:'
@echo ''
+ @echo ' atl_lib - atl library'
@echo ' lib - igb library'
@echo ''
@echo ' daemons_all - build all daemons (mrpd maap shaper)'
@@ -32,6 +33,14 @@ help:
@echo ' clean: a summary clean target to clean _all_ folders'
@echo ''
+atl_lib: FORCE
+ $(call descend,lib/atl_avb/lib)
+ $(call descend,lib/common)
+
+atl_lib_clean:
+ $(call descend,lib/atl_avb/lib/,clean)
+ $(call descend,lib/common/,clean)
+
lib: FORCE
$(call descend,lib/igb_avb/lib)
$(call descend,lib/common)
@@ -116,7 +125,7 @@ live_stream:
live_stream_clean:
$(call descend,examples/live_stream/,clean)
-avtp_pipeline: lib
+avtp_pipeline: lib atl_lib
$(MAKE) -s -C lib/avtp_pipeline -f avtp_pipeline.mk
avtp_pipeline_clean:
@@ -140,8 +149,8 @@ examples_all: examples_common simple_talker simple_listener mrp_client live_stre
examples_all_clean: examples_common_clean simple_talker_clean simple_listener_clean mrp_client_clean \
jackd-talker_clean jackd-listener_clean live_stream_clean simple_rx_clean
-all: lib daemons_all examples_all avtp_pipeline avtp_avdecc
+all: lib atl_lib daemons_all examples_all avtp_pipeline avtp_avdecc
-clean: lib_clean daemons_all_clean examples_all_clean avtp_pipeline_clean avtp_avdecc_clean
+clean: lib_clean atl_lib_clean daemons_all_clean examples_all_clean avtp_pipeline_clean avtp_avdecc_clean
.PHONY: FORCE
diff --git a/examples/atl_simple_talker/Makefile b/examples/atl_simple_talker/Makefile
new file mode 100644
index 00000000..8c953c94
--- /dev/null
+++ b/examples/atl_simple_talker/Makefile
@@ -0,0 +1,39 @@
+AVBLIB_DIR = ../../lib/common
+AVBLIB_OBJS = avb_avtp.o avb_gptp.o avb_atl.o
+AVBLIB_TARGETS = $(addprefix $(AVBLIB_DIR)/,$(AVBLIB_OBJS))
+
+MRPCLIENT_DIR = ../common
+MRPTALKER_OBJS = talker_mrp_client.o
+MRPTALKER_TARGETS = $(addprefix $(MRPCLIENT_DIR)/,$(MRPTALKER_OBJS))
+
+ATLLIB_DIR = ../../lib/atl_avb/lib
+DAEMONS_DIR = ../../daemons
+
+CC?=gcc
+OPT=-O2 -g
+WARN=-Wall -Wextra -Wno-parentheses
+CFLAGS=$(OPT) $(WARN)
+CPPFLAGS=-I$(ATLLIB_DIR) -I$(DAEMONS_DIR)/mrpd -I$(MRPCLIENT_DIR) -I$(AVBLIB_DIR) -I$(DAEMONS_DIR)/common -DAVB_FEATURE_IGB
+LDLIBS=-latl -lpci -lrt -lm -pthread
+LDFLAGS=-L$(ATLLIB_DIR)
+
+.PHONY: all clean
+
+all: simple_talker
+
+simple_talker: simple_talker.o $(MRPTALKER_TARGETS) $(AVBLIB_TARGETS)
+
+simple_talker.o: simple_talker.c
+
+$(AVBLIB_DIR)/%.o: $(AVBLIB_DIR)/%.h $(AVBLIB_DIR)/%.c
+ make -C $(AVBLIB_DIR) $@
+
+$(MRPCLIENT_DIR)/%.o: $(MRPCLIENT_DIR)/%.c $(MRPCLIENT_DIR)/%.h
+ make -C $(MRPCLIENT_DIR) $@
+
+%: %.o
+ $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
+
+clean:
+ $(RM) simple_talker
+ $(RM) `find . -name "*~" -o -name "*.[oa]" -o -name "\#*\#" -o -name TAGS -o -name core -o -name "*.orig"`
diff --git a/examples/atl_simple_talker/README b/examples/atl_simple_talker/README
new file mode 100644
index 00000000..93a778e6
--- /dev/null
+++ b/examples/atl_simple_talker/README
@@ -0,0 +1,29 @@
+EXAMPLE APPLICATIONS
+
+The 'simple_talker' application illustrates the various steps to publish a stream
+and streaming 1722/61883 audio frames after a listener connects. The audio is a
+simple sine wave. This application was tested with AQC107 as talker, and i210
+running existing simple_listener application.
+
+The simple talker application requires root permissions to execute and
+attach to the driver.
+ sudo ./simple_talker
+
+To exit the app, hit Ctrl-C. The application gracefully tears down
+the connection to the driver. If the application unexpectedly aborts the
+kernel-mode driver also reclaims the various buffers and attempts to clean up.
+The application should be able to re-initialize and use the transmit queues
+without restarting the driver.
+
+Note this application requires using the provided gptp timesync daemon to
+provide the 802.1AS presentation times included in the 1722 frames. This
+application also requires the mrpd daemon to be running to detect and
+establish various stream reservation parameters.
+
+Lastly, to build the application, you need to have the pciutils library
+installed. the latest version can be downloaded from:
+
+ < ftp://ftp.kernel.org/pub/software/utils/pciutils/ >.
+
+Download and extract the library, and run 'make;make install;make install-lib'.
+
diff --git a/examples/atl_simple_talker/simple_talker.c b/examples/atl_simple_talker/simple_talker.c
new file mode 100644
index 00000000..dd1d1437
--- /dev/null
+++ b/examples/atl_simple_talker/simple_talker.c
@@ -0,0 +1,828 @@
+/******************************************************************************
+
+ Copyright (c) 2019, Aquantia Corporation
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#include <errno.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <math.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <linux/if.h>
+#include "avb_atl.h"
+
+#include <pci/pci.h>
+
+#include "talker_mrp_client.h"
+#include "avb.h"
+
+#define VERSION_STR "1.0"
+
+#define SRC_CHANNELS (2)
+#define GAIN (0.5)
+#define L16_PAYLOAD_TYPE (96) /* for layer 4 transport - should be negotiated via RTSP */
+#define ID_B_HDR_EXT_ID (0) /* for layer 4 transport - should be negotiated via RTSP */
+#define L2_SAMPLES_PER_FRAME (6)
+#define L4_SAMPLES_PER_FRAME (60)
+#define L4_SAMPLE_SIZE (2)
+#define CHANNELS (2)
+#define RTP_SUBNS_SCALE_NUM (20000000)
+#define RTP_SUBNS_SCALE_DEN (4656613)
+#define XMIT_DELAY (200000000) /* us */
+#define RENDER_DELAY (XMIT_DELAY+2000000) /* us */
+#define L2_PACKET_IPG (125000) /* (1) packet every 125 usec */
+#define L4_PACKET_IPG (1250000) /* (1) packet every 1.25 millisec */
+#define L4_PORT ((uint16_t)5004)
+#define PKT_SZ (100)
+
+typedef long double FrequencyRatio;
+volatile int *halt_tx_sig;//Global variable for signal handler
+
+typedef struct __attribute__ ((packed)) {
+ uint8_t version_length;
+ uint8_t DSCP_ECN;
+ uint16_t ip_length;
+ uint16_t id;
+ uint16_t fragmentation;
+ uint8_t ttl;
+ uint8_t protocol;
+ uint16_t hdr_cksum;
+ uint32_t src;
+ uint32_t dest;
+
+ uint16_t source_port;
+ uint16_t dest_port;
+ uint16_t udp_length;
+ uint16_t cksum;
+
+ uint8_t version_cc;
+ uint8_t mark_payload;
+ uint16_t sequence;
+ uint32_t timestamp;
+ uint32_t ssrc;
+
+ uint8_t tag[2];
+ uint16_t total_length;
+ uint8_t tag_length;
+ uint8_t seconds[3];
+ uint32_t nanoseconds;
+} IP_RTP_Header;
+
+typedef struct __attribute__ ((packed)) {
+ uint32_t source;
+ uint32_t dest;
+ uint8_t zero;
+ uint8_t protocol;
+ uint16_t length;
+} IP_PseudoHeader;
+
+/* globals */
+
+static const char *version_str = "simple_talker v" VERSION_STR "\n"
+ "Copyright (c) 2012, Intel Corporation\n";
+
+unsigned char glob_station_addr[] = { 0, 0, 0, 0, 0, 0 };
+unsigned char glob_stream_id[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+/* IEEE 1722 reserved address */
+unsigned char glob_l2_dest_addr[] = { 0x91, 0xE0, 0xF0, 0x00, 0x0e, 0x80 };
+unsigned char glob_l3_dest_addr[] = { 224, 0, 0, 115 };
+
+uint16_t inet_checksum(uint8_t *ip, int len){
+ uint32_t sum = 0; /* assume 32 bit long, 16 bit short */
+
+ while(len > 1){
+ sum += *(( uint16_t *) ip); ip += 2;
+ if(sum & 0x80000000) /* if high order bit set, fold */
+ sum = (sum & 0xFFFF) + (sum >> 16);
+ len -= 2;
+ }
+
+ if(len) /* take care of left over byte */
+ sum += (uint16_t) *(uint8_t *)ip;
+
+ while(sum>>16)
+ sum = (sum & 0xFFFF) + (sum >> 16);
+
+ return ~sum;
+}
+
+#if 0
+ else {
+ if(sum & 0x80000000) /* if high order bit set, fold */
+ sum = (sum & 0xFFFF) + (sum >> 16);
+ sum += *(( uint16_t *) buf_iov->iov_base); buf_iov->iov_base += 2;
+ buf_iov->iov_len -= 2;
+ }
+#endif
+
+uint16_t inet_checksum_sg( struct iovec *buf_iov, size_t buf_iovlen ){
+ size_t i;
+ uint32_t sum = 0; /* assume 32 bit long, 16 bit short */
+ uint8_t residual;
+ int has_residual = 0;
+
+ for( i = 0; i < buf_iovlen; ++i,++buf_iov ) {
+ if( has_residual ) {
+ if( buf_iov->iov_len > 0 ) {
+ if(sum & 0x80000000) /* if high order bit set, fold */
+ sum = (sum & 0xFFFF) + (sum >> 16);
+ sum += residual | (*(( uint8_t *) buf_iov->iov_base) << 8);
+ buf_iov->iov_base += 1;
+ buf_iov->iov_len -= 1;
+ } else {
+ if(sum & 0x80000000) /* if high order bit set, fold */
+ sum = (sum & 0xFFFF) + (sum >> 16);
+ sum += (uint16_t) residual;
+ }
+ has_residual = 0;
+
+ }
+ while(buf_iov->iov_len > 1){
+ if(sum & 0x80000000) /* if high order bit set, fold */
+ sum = (sum & 0xFFFF) + (sum >> 16);
+ sum += *(( uint16_t *) buf_iov->iov_base); buf_iov->iov_base += 2;
+ buf_iov->iov_len -= 2;
+ }
+ if( buf_iov->iov_len ) {
+ residual = *(( uint8_t *) buf_iov->iov_base);
+ has_residual = 1;
+ }
+ }
+ if( has_residual ) {
+ sum += (uint16_t) residual;
+ }
+
+ while(sum>>16)
+ sum = (sum & 0xFFFF) + (sum >> 16);
+
+ return ~sum;
+}
+
+static inline uint64_t ST_rdtsc(void)
+{
+ uint64_t ret;
+ unsigned c, d;
+ asm volatile ("rdtsc":"=a" (c), "=d"(d));
+ ret = d;
+ ret <<= 32;
+ ret |= c;
+ return ret;
+}
+
+void gensine32(int32_t * buf, unsigned count)
+{
+ long double interval = (2 * ((long double)M_PI)) / count;
+ unsigned i;
+ for (i = 0; i < count; ++i) {
+ buf[i] =
+ (int32_t) (MAX_SAMPLE_VALUE * sinl(i * interval) * GAIN);
+ }
+}
+
+int get_samples(unsigned count, int32_t * buffer)
+{
+ static int init = 0;
+ static int32_t samples_onechannel[100];
+ static unsigned index = 0;
+
+ if (init == 0) {
+ gensine32(samples_onechannel, 100);
+ init = 1;
+ }
+
+ while (count > 0) {
+ int i;
+ for (i = 0; i < SRC_CHANNELS; ++i) {
+ *(buffer++) = samples_onechannel[index];
+ }
+ index = (index + 1) % 100;
+ --count;
+ }
+
+ return 0;
+}
+
+void sigint_handler(int signum)
+{
+ printf("got SIGINT\n");
+ *halt_tx_sig = signum;
+}
+
+void l3_to_l2_multicast( unsigned char *l2, unsigned char *l3 ) {
+ l2[0] = 0x1;
+ l2[1] = 0x0;
+ l2[2] = 0x5e;
+ l2[3] = l3[1] & 0x7F;
+ l2[4] = l3[2];
+ l2[5] = l3[3];
+}
+
+int get_mac_address(char *interface)
+{
+ struct ifreq if_request;
+ int lsock;
+ int rc;
+
+ lsock = socket(PF_PACKET, SOCK_RAW, htons(0x800));
+ if (lsock < 0)
+ return -1;
+
+ memset(&if_request, 0, sizeof(if_request));
+ strncpy(if_request.ifr_name, interface, sizeof(if_request.ifr_name) - 1);
+ rc = ioctl(lsock, SIOCGIFHWADDR, &if_request);
+ if (rc < 0) {
+ close(lsock);
+ return -1;
+ }
+
+ memcpy(glob_station_addr, if_request.ifr_hwaddr.sa_data,
+ sizeof(glob_station_addr));
+ close(lsock);
+ return 0;
+}
+
+static void usage(void)
+{
+ fprintf(stderr, "\n"
+ "usage: simple_talker [-h] -i interface-name"
+ "\n"
+ "options:\n"
+ " -h show this message\n"
+ " -i specify interface for AVB connection\n"
+ " -t transport equal to 2 for 1722 or 3 for RTP\n"
+ "\n" "%s" "\n", version_str);
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned i;
+ int err;
+ device_t atl_dev;
+ int atl_shm_fd = -1;
+ char *atl_mmap = NULL;
+ struct atl_dma_alloc a_page;
+ struct atl_packet a_packet;
+ struct atl_packet *tmp_packet;
+ struct atl_packet *cleaned_packets;
+ struct atl_packet *free_packets;
+ struct mrp_talker_ctx *ctx = malloc(sizeof(struct mrp_talker_ctx));
+ int c;
+ u_int64_t last_time;
+ int rc = 0;
+ char *interface = NULL;
+ int transport = -1;
+ uint16_t seqnum;
+ uint32_t rtp_timestamp;
+ uint64_t time_stamp;
+ unsigned total_samples = 0;
+ gPtpTimeData td;
+ int32_t sample_buffer[L4_SAMPLES_PER_FRAME * SRC_CHANNELS];
+
+ seventeen22_header *l2_header0;
+ six1883_header *l2_header1;
+ six1883_sample *sample;
+
+ IP_RTP_Header *l4_headers;
+ IP_PseudoHeader pseudo_hdr;
+ unsigned l4_local_address = 0;
+ int sd;
+ struct sockaddr_in local;
+ struct ifreq if_request;
+
+ uint64_t now_local, now_8021as;
+ uint64_t update_8021as;
+ unsigned delta_8021as, delta_local;
+ uint8_t dest_addr[6];
+ size_t packet_size;
+ struct mrp_domain_attr *class_a = malloc(sizeof(struct mrp_domain_attr));
+ struct mrp_domain_attr *class_b = malloc(sizeof(struct mrp_domain_attr));
+
+ for (;;) {
+ c = getopt(argc, argv, "hi:t:");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'h':
+ usage();
+ break;
+ case 'i':
+ if (interface) {
+ printf("only one interface per daemon is supported\n");
+ usage();
+ }
+ interface = strdup(optarg);
+ atl_dev.ifname = strdup(optarg);
+ break;
+ case 't':
+ transport = strtoul( optarg, NULL, 10 );
+ }
+ }
+ if (optind < argc)
+ usage();
+ if (NULL == interface) {
+ usage();
+ }
+ if( transport < 2 || transport > 4 ) {
+ fprintf( stderr, "Must specify valid transport\n" );
+ usage();
+ }
+
+ rc = mrp_talker_client_init(ctx);
+ if (rc) {
+ printf("MRP talker client initialization failed\n");
+ return errno;
+ }
+
+ halt_tx_sig = &ctx->halt_tx;
+
+ rc = mrp_connect(ctx);
+ if (rc) {
+ printf("socket creation failed\n");
+ return errno;
+ }
+ err = pci_connect(&atl_dev);
+ if (err) {
+ printf("connect failed (%s) - are you running as root?\n",
+ strerror(errno));
+ return errno;
+ }
+ err = atl_init(&atl_dev);
+ if (err) {
+ printf("init failed (%s) - is the driver really loaded?\n",
+ strerror(errno));
+ return errno;
+ }
+ err = atl_dma_malloc_page(&atl_dev, &a_page);
+ if (err) {
+ printf("malloc failed (%s) - out of memory?\n",
+ strerror(errno));
+ return errno;
+ }
+ signal(SIGINT, sigint_handler);
+ rc = get_mac_address(interface);
+ if (rc) {
+ printf("failed to open interface\n");
+ usage();
+ }
+ if( transport == 2 ) {
+ memcpy( dest_addr, glob_l2_dest_addr, sizeof(dest_addr));
+ } else {
+ memset( &local, 0, sizeof( local ));
+ local.sin_family = PF_INET;
+ local.sin_addr.s_addr = htonl( INADDR_ANY );
+ local.sin_port = htons(L4_PORT);
+ l3_to_l2_multicast( dest_addr, glob_l3_dest_addr );
+ memset( &if_request, 0, sizeof( if_request ));
+ strncpy(if_request.ifr_name, interface, sizeof(if_request.ifr_name)-1);
+ sd = socket( AF_INET, SOCK_DGRAM, 0 );
+ if( sd == -1 ) {
+ printf( "Failed to open socket: %s\n", strerror( errno ));
+ return errno;
+ }
+ if( bind( sd, (struct sockaddr *) &local, sizeof( local )) != 0 ) {
+ printf( "Failed to bind on socket: %s\n", strerror( errno ));
+ return errno;
+ }
+ if( ioctl( sd, SIOCGIFADDR, &if_request ) != 0 ) {
+ printf
+ ( "Failed to get interface address (ioctl) on socket: %s\n",
+ strerror( errno ));
+ return errno;
+ }
+ memcpy
+ ( &l4_local_address,
+ &(( struct sockaddr_in *)&if_request.ifr_addr)->sin_addr,
+ sizeof( l4_local_address ));
+
+ }
+
+ rc = mrp_get_domain(ctx, class_a, class_b);
+ if (rc) {
+ printf("failed calling msp_get_domain()\n");
+ return EXIT_FAILURE;
+ }
+ printf("detected domain Class A PRIO=%d VID=%04x...\n",class_a->priority,
+ class_a->vid);
+
+ rc = mrp_register_domain(class_a, ctx);
+ if (rc) {
+ printf("mrp_register_domain failed\n");
+ return EXIT_FAILURE;
+ }
+
+ rc = mrp_join_vlan(class_a, ctx);
+ if (rc) {
+ printf("mrp_join_vlan failed\n");
+ return EXIT_FAILURE;
+ }
+
+ if( transport == 2 ) {
+ atl_set_class_bandwidth
+ (&atl_dev, (PKT_SZ + 24) * 8000, 0); // 24 - IPG+PREAMBLE+FCS
+ } else {
+ atl_set_class_bandwidth
+ (&atl_dev, 8000*(sizeof(*l4_headers)+L4_SAMPLES_PER_FRAME*CHANNELS*2 + 24), 0);
+ }
+
+ memset(glob_stream_id, 0, sizeof(glob_stream_id));
+ memcpy(glob_stream_id, glob_station_addr, sizeof(glob_station_addr));
+
+ if( transport == 2 ) {
+ packet_size = PKT_SZ;
+ } else {
+ packet_size = 18 + sizeof(*l4_headers) +
+ (L4_SAMPLES_PER_FRAME * CHANNELS * L4_SAMPLE_SIZE );
+ }
+
+ a_packet.attime = a_packet.flags = 0;
+ a_packet.map.paddr = a_page.dma_paddr;
+ a_packet.map.mmap_size = a_page.mmap_size;
+ a_packet.offset = 0;
+ a_packet.vaddr = a_page.dma_vaddr + a_packet.offset;
+ a_packet.len = packet_size;
+ a_packet.next = NULL;
+ free_packets = NULL;
+ seqnum = 0;
+ rtp_timestamp = 0; /* Should be random start */
+
+ /* divide the dma page into buffers for packets */
+ for (i = 1; i < ((a_page.mmap_size) / packet_size); i++) {
+ tmp_packet = malloc(sizeof(struct atl_packet));
+ if (NULL == tmp_packet) {
+ printf("failed to allocate atl_packet memory!\n");
+ return errno;
+ }
+ *tmp_packet = a_packet;
+ tmp_packet->offset = (i * packet_size);
+ tmp_packet->vaddr += tmp_packet->offset;
+ tmp_packet->next = free_packets;
+ memset(tmp_packet->vaddr, 0, packet_size); /* MAC header at least */
+ memcpy(tmp_packet->vaddr, dest_addr, sizeof(dest_addr));
+ memcpy(tmp_packet->vaddr + 6, glob_station_addr,
+ sizeof(glob_station_addr));
+
+ /* Q-tag */
+ ((char *)tmp_packet->vaddr)[12] = 0x81;
+ ((char *)tmp_packet->vaddr)[13] = 0x00;
+ ((char *)tmp_packet->vaddr)[14] =
+ ((ctx->domain_class_a_priority << 13 | ctx->domain_class_a_vid)) >> 8;
+ ((char *)tmp_packet->vaddr)[15] =
+ ((ctx->domain_class_a_priority << 13 | ctx->domain_class_a_vid)) & 0xFF;
+ if( transport == 2 ) {
+ ((char *)tmp_packet->vaddr)[16] = 0x22; /* 1722 eth type */
+ ((char *)tmp_packet->vaddr)[17] = 0xF0;
+ } else {
+ ((char *)tmp_packet->vaddr)[16] = 0x08; /* IP eth type */
+ ((char *)tmp_packet->vaddr)[17] = 0x00;
+ }
+
+ if( transport == 2 ) {
+ /* 1722 header update + payload */
+ l2_header0 =
+ (seventeen22_header *) (((char *)tmp_packet->vaddr) + 18);
+ l2_header0->cd_indicator = 0;
+ l2_header0->subtype = 0;
+ l2_header0->sid_valid = 1;
+ l2_header0->version = 0;
+ l2_header0->reset = 0;
+ l2_header0->reserved0 = 0;
+ l2_header0->gateway_valid = 0;
+ l2_header0->reserved1 = 0;
+ l2_header0->timestamp_uncertain = 0;
+ memset(&(l2_header0->stream_id), 0, sizeof(l2_header0->stream_id));
+ memcpy(&(l2_header0->stream_id), glob_station_addr,
+ sizeof(glob_station_addr));
+ l2_header0->length = htons(32);
+ l2_header1 = (six1883_header *) (l2_header0 + 1);
+ l2_header1->format_tag = 1;
+ l2_header1->packet_channel = 0x1F;
+ l2_header1->packet_tcode = 0xA;
+ l2_header1->app_control = 0x0;
+ l2_header1->reserved0 = 0;
+ l2_header1->source_id = 0x3F;
+ l2_header1->data_block_size = 1;
+ l2_header1->fraction_number = 0;
+ l2_header1->quadlet_padding_count = 0;
+ l2_header1->source_packet_header = 0;
+ l2_header1->reserved1 = 0;
+ l2_header1->eoh = 0x2;
+ l2_header1->format_id = 0x10;
+ l2_header1->format_dependent_field = 0x02;
+ l2_header1->syt = 0xFFFF;
+ tmp_packet->len =
+ 18 + sizeof(seventeen22_header) + sizeof(six1883_header) +
+ (L2_SAMPLES_PER_FRAME * CHANNELS * sizeof(six1883_sample));
+ } else {
+ pseudo_hdr.source = l4_local_address;
+ memcpy
+ ( &pseudo_hdr.dest, glob_l3_dest_addr, sizeof( pseudo_hdr.dest ));
+ pseudo_hdr.zero = 0;
+ pseudo_hdr.protocol = 0x11;
+ pseudo_hdr.length = htons(packet_size-18-20);
+
+ l4_headers =
+ (IP_RTP_Header *) (((char *)tmp_packet->vaddr) + 18);
+ l4_headers->version_length = 0x45;
+ l4_headers->DSCP_ECN = 0x20;
+ l4_headers->ip_length = htons(packet_size-18);
+ l4_headers->id = 0;
+ l4_headers->fragmentation = 0;
+ l4_headers->ttl = 64;
+ l4_headers->protocol = 0x11;
+ l4_headers->hdr_cksum = 0;
+ l4_headers->src = l4_local_address;
+ memcpy
+ ( &l4_headers->dest, glob_l3_dest_addr, sizeof( l4_headers->dest ));
+ {
+ struct iovec iv0;
+ iv0.iov_base = l4_headers;
+ iv0.iov_len = 20;
+ l4_headers->hdr_cksum =
+ inet_checksum_sg( &iv0, 1 );
+ }
+
+ l4_headers->source_port = htons(L4_PORT);
+ l4_headers->dest_port = htons(L4_PORT);
+ l4_headers->udp_length = htons(packet_size-18-20);
+
+ l4_headers->version_cc = 2;
+ l4_headers->mark_payload = L16_PAYLOAD_TYPE;
+ l4_headers->sequence = 0;
+ l4_headers->timestamp = 0;
+ l4_headers->ssrc = 0;
+
+ l4_headers->tag[0] = 0xBE;
+ l4_headers->tag[1] = 0xDE;
+ l4_headers->total_length = htons(2);
+ l4_headers->tag_length = (6 << 4) | ID_B_HDR_EXT_ID;
+
+ tmp_packet->len =
+ 18 + sizeof(*l4_headers) +
+ (L4_SAMPLES_PER_FRAME * CHANNELS * L4_SAMPLE_SIZE );
+
+ }
+ free_packets = tmp_packet;
+ }
+
+ /*
+ * subtract 16 bytes for the MAC header/Q-tag - pktsz is limited to the
+ * data payload of the ethernet frame.
+ *
+ * IPG is scaled to the Class (A) observation interval of packets per 125 usec.
+ */
+ fprintf(stderr, "advertising stream ...\n");
+ if( transport == 2 ) {
+ rc = mrp_advertise_stream(glob_stream_id, dest_addr,
+ PKT_SZ - 16,
+ L2_PACKET_IPG / 125000,
+ 3900,ctx);
+ } else {
+ /*
+ * 1 is the wrong number for frame rate, but fractional values
+ * not allowed, not sure the significance of the value 6, but
+ * using it consistently
+ */
+ rc = mrp_advertise_stream(glob_stream_id, dest_addr,
+ sizeof(*l4_headers) + L4_SAMPLES_PER_FRAME * CHANNELS * 2 + 6,
+ 1,
+ 3900, ctx);
+ }
+ if (rc) {
+ printf("mrp_advertise_stream failed\n");
+ return EXIT_FAILURE;
+ }
+
+ fprintf(stderr, "awaiting a listener ...\n");
+ rc = mrp_await_listener(glob_stream_id, ctx);
+ if (rc) {
+ printf("mrp_await_listener failed\n");
+ return EXIT_FAILURE;
+ }
+ ctx->listeners = 1;
+ printf("got a listener ...\n");
+ ctx->halt_tx = 0;
+
+ if(-1 == gptpinit(&atl_shm_fd, &atl_mmap)) {
+ fprintf(stderr, "GPTP init failed.\n");
+ return EXIT_FAILURE;
+ }
+
+ if (-1 == gptpscaling(atl_mmap, &td)) {
+ fprintf(stderr, "GPTP scaling failed.\n");
+ return EXIT_FAILURE;
+ }
+
+ if(atl_get_wallclock( &atl_dev, &now_local, NULL ) > 0) {
+ fprintf( stderr, "Failed to get wallclock time\n" );
+ return EXIT_FAILURE;
+ }
+ update_8021as = td.local_time - td.ml_phoffset;
+ delta_local = (unsigned)(now_local - td.local_time);
+ delta_8021as = (unsigned)(td.ml_freqoffset * delta_local);
+ now_8021as = update_8021as + delta_8021as;
+
+ last_time = now_local + XMIT_DELAY;
+ time_stamp = now_8021as + RENDER_DELAY;
+
+ rc = nice(-20);
+
+ struct atl_packet *send_packets = NULL, *last_attached = NULL;
+
+ tmp_packet = free_packets;
+ while (ctx->listeners && !ctx->halt_tx) {
+ if (NULL == tmp_packet)
+ goto cleanup;
+
+ free_packets = tmp_packet->next;
+ tmp_packet->next = NULL;
+
+ if( transport == 2 ) {
+ uint32_t timestamp_l;
+ get_samples( L2_SAMPLES_PER_FRAME, sample_buffer );
+ l2_header0 =
+ (seventeen22_header *) (((char *)tmp_packet->vaddr) + 18);
+ l2_header1 = (six1883_header *) (l2_header0 + 1);
+
+ /* unfortuntely unless this thread is at rtprio
+ * you get pre-empted between fetching the time
+ * and programming the packet and get a late packet
+ */
+ tmp_packet->attime = last_time + L2_PACKET_IPG;
+ last_time += L2_PACKET_IPG;
+
+ l2_header0->seq_number = seqnum++;
+ if (seqnum % 4 == 0)
+ l2_header0->timestamp_valid = 0;
+
+ else
+ l2_header0->timestamp_valid = 1;
+
+ timestamp_l = time_stamp;
+ l2_header0->timestamp = htonl(timestamp_l);
+ time_stamp += L2_PACKET_IPG;
+ l2_header1->data_block_continuity = total_samples;
+ total_samples += L2_SAMPLES_PER_FRAME*CHANNELS;
+ sample =
+ (six1883_sample *) (((char *)tmp_packet->vaddr) +
+ (18 + sizeof(seventeen22_header) +
+ sizeof(six1883_header)));
+
+ for (i = 0; i < L2_SAMPLES_PER_FRAME * CHANNELS; ++i) {
+ uint32_t tmp = htonl(sample_buffer[i]);
+ sample[i].label = 0x40;
+ memcpy(&(sample[i].value), &(tmp),
+ sizeof(sample[i].value));
+ }
+ } else {
+ uint16_t *l16_sample;
+ uint8_t *tmp;
+ get_samples( L4_SAMPLES_PER_FRAME, sample_buffer );
+
+ l4_headers =
+ (IP_RTP_Header *) (((char *)tmp_packet->vaddr) + 18);
+
+ l4_headers->sequence = seqnum++;
+ l4_headers->timestamp = rtp_timestamp;
+
+ tmp_packet->attime = last_time + L4_PACKET_IPG;
+ last_time += L4_PACKET_IPG;
+
+ l4_headers->nanoseconds = time_stamp/1000000000;
+ tmp = (uint8_t *) &l4_headers->nanoseconds;
+ l4_headers->seconds[0] = tmp[2];
+ l4_headers->seconds[1] = tmp[1];
+ l4_headers->seconds[2] = tmp[0];
+ {
+ uint64_t tmp;
+ tmp = time_stamp % 1000000000;
+ tmp *= RTP_SUBNS_SCALE_NUM;
+ tmp /= RTP_SUBNS_SCALE_DEN;
+ l4_headers->nanoseconds = (uint32_t) tmp;
+ }
+ l4_headers->nanoseconds = htons(l4_headers->nanoseconds);
+
+
+ time_stamp += L4_PACKET_IPG;
+
+ l16_sample = (uint16_t *) (l4_headers+1);
+
+ for (i = 0; i < L4_SAMPLES_PER_FRAME * CHANNELS; ++i) {
+ uint16_t tmp = sample_buffer[i]/65536;
+ l16_sample[i] = htons(tmp);
+ }
+ l4_headers->cksum = 0;
+ {
+ struct iovec iv[2];
+ iv[0].iov_base = &pseudo_hdr;
+ iv[0].iov_len = sizeof(pseudo_hdr);
+ iv[1].iov_base = ((uint8_t *)l4_headers) + 20;
+ iv[1].iov_len = packet_size-18-20;
+ l4_headers->cksum =
+ inet_checksum_sg( iv, 2 );
+ }
+ }
+
+ if( send_packets == NULL ) {
+ send_packets = tmp_packet;
+ }
+
+ if( last_attached ) {
+ last_attached->next = tmp_packet;
+ }
+
+ last_attached = tmp_packet;
+ tmp_packet = free_packets;
+ cleanup:
+ err = atl_xmit(&atl_dev, 0, &send_packets);
+ if (ENOSPC == err) {
+ /* put back for now */
+ tmp_packet->next = free_packets;
+ free_packets = tmp_packet;
+ }
+
+ cleaned_packets = NULL;
+ atl_clean(&atl_dev, &cleaned_packets);
+
+ if( free_packets ) {
+ tmp_packet = free_packets;
+ while (tmp_packet->next) {
+ tmp_packet = tmp_packet->next;
+ }
+ tmp_packet->next = cleaned_packets;
+ }
+ else {
+ free_packets = cleaned_packets;
+ }
+
+ send_packets = NULL;
+ last_attached = NULL;
+ }
+ rc = nice(0);
+ atl_stop_tx(&atl_dev, 0, &cleaned_packets);
+ if (ctx->halt_tx == 0)
+ printf("listener left ...\n");
+ ctx->halt_tx = 1;
+ if( transport == 2 ) {
+ rc = mrp_unadvertise_stream
+ (glob_stream_id, dest_addr, PKT_SZ - 16, L2_PACKET_IPG / 125000,
+ 3900, ctx);
+ } else {
+ rc = mrp_unadvertise_stream
+ (glob_stream_id, dest_addr,
+ sizeof(*l4_headers)+L4_SAMPLES_PER_FRAME*CHANNELS*2 + 6, 1,
+ 3900, ctx);
+ }
+ if (rc)
+ printf("mrp_unadvertise_stream failed\n");
+
+ atl_set_class_bandwidth(&atl_dev, 0, 0); /* disable Qav */
+
+ rc = mrp_disconnect(ctx);
+ if (rc)
+ printf("mrp_disconnect failed\n");
+ free(ctx);
+ free(class_a);
+ free(class_b);
+ atl_dma_free_page(&atl_dev, &a_page);
+ rc = gptpdeinit(&atl_shm_fd, &atl_mmap);
+ err = atl_detach(&atl_dev);
+
+ pthread_exit(NULL);
+
+ return EXIT_SUCCESS;
+}
diff --git a/examples/common/Makefile b/examples/common/Makefile
index 68a313fd..a243d23e 100644
--- a/examples/common/Makefile
+++ b/examples/common/Makefile
@@ -6,12 +6,14 @@ WARN = -Wall -Wextra -Wno-parentheses
CFLAGS = $(OPT) $(WARN)
CPPFLAGS = -I$(DAEMONS_DIR)/mrpd -I$(DAEMONS_DIR)/common
-all: talker_mrp_client.o listener_mrp_client.o
+all: talker_mrp_client.o listener_mrp_client.o async_pcap_storing.o
talker_mrp_client.o: talker_mrp_client.c talker_mrp_client.h
listener_mrp_client.o: listener_mrp_client.c listener_mrp_client.h
+async_pcap_storing.o: async_pcap_storing.c async_pcap_storing.h
+
clean:
- $(RM) talker_mrp_client.o listener_mrp_client.o
+ $(RM) talker_mrp_client.o listener_mrp_client.o async_pcap_storing.o
$(RM) `find . -name "*~" -o -name "*.[oa]" -o -name "\#*\#" -o -name TAGS -o -name core -o -name "*.orig"`
diff --git a/examples/common/async_pcap_storing.c b/examples/common/async_pcap_storing.c
new file mode 100644
index 00000000..56f404e8
--- /dev/null
+++ b/examples/common/async_pcap_storing.c
@@ -0,0 +1,314 @@
+#include <pthread.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <pcap.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include "async_pcap_storing.h"
+
+struct storing_packet {
+ void *mem;
+ uint32_t size;
+ uint32_t used_size;
+ uint32_t real_size;
+ uint64_t ts;
+ struct storing_packet *next;
+};
+
+struct pcap_store_control {
+ struct storing_packet *free;
+ struct storing_packet *busy;
+ struct storing_packet *last_busy;
+ pcap_dumper_t *pd;
+ pthread_t thread_id;
+ pthread_mutex_t free_lock;
+ pthread_mutex_t busy_lock;
+ uint32_t stop_signal;
+ uint32_t stop_confirm;
+};
+
+static void release_packets(struct storing_packet *packet)
+{
+ struct storing_packet *next;
+ while( packet ) {
+ next = packet->next;
+ if( packet->mem ) {
+ free(packet->mem);
+ }
+ free(packet);
+ packet = next;
+ }
+}
+
+static struct storing_packet *alloc_packet(uint32_t size)
+{
+ struct storing_packet *packet = malloc(sizeof(*packet));
+ if( packet ) {
+ packet->next = NULL;
+ packet->size = size;
+ packet->used_size = 0;
+ packet->real_size = 0;
+ packet->ts = 0;
+ packet->mem = malloc(size);
+ if( !packet->mem ) {
+ free(packet);
+ packet = NULL;
+ }
+ }
+ return packet;
+}
+
+static void dump_in_file(void *context, struct storing_packet *store_packet)
+{
+ struct pcap_store_control *ctrl = (struct pcap_store_control *)context;
+ while( store_packet ) {
+ if( store_packet->real_size ) {
+ struct pcap_pkthdr pkt_hdr;
+ pkt_hdr.ts.tv_sec = store_packet->ts / 1000000000;
+ pkt_hdr.ts.tv_usec = (store_packet->ts - 1000000000 * pkt_hdr.ts.tv_sec); // / 1000;
+ pkt_hdr.caplen = store_packet->used_size;
+ pkt_hdr.len = store_packet->real_size;
+ pcap_dump((u_char *)ctrl->pd, &pkt_hdr, store_packet->mem);
+ store_packet->used_size = 0;
+ store_packet->real_size = 0;
+ //printf("Store packet %p in file\n", store_packet);
+ }
+ store_packet = store_packet->next;
+ }
+}
+
+static void *store_thread(void *context)
+{
+ int res;
+ struct pcap_store_control *ctrl = (struct pcap_store_control *)context;
+ struct storing_packet *store_packet = NULL;
+ ctrl->stop_confirm = 0;
+ while( !ctrl->stop_signal ) {
+ if( store_packet ) {
+ res = pthread_mutex_trylock(&ctrl->free_lock);
+ if( res == EBUSY ) {
+ usleep(1);
+ continue;
+ } else if ( res ) {
+ printf("Mutex error: %s!", strerror(errno));
+ break;
+ }
+ store_packet->next = ctrl->free;
+ ctrl->free = store_packet;
+ pthread_mutex_unlock(&ctrl->free_lock);
+ store_packet = NULL;
+ }
+
+ if( !ctrl->busy ) {
+ usleep(1);
+ continue;
+ }
+
+ res = pthread_mutex_trylock(&ctrl->busy_lock);
+ if( res == EBUSY ) {
+ usleep(1);
+ continue;
+ } else if ( res ) {
+ printf("Mutex error: %s!", strerror(errno));
+ break;
+ }
+ store_packet = ctrl->busy;
+ ctrl->busy = ctrl->busy->next;
+ if( ctrl->last_busy == store_packet ) {
+ ctrl->last_busy = ctrl->busy;
+ }
+ pthread_mutex_unlock(&ctrl->busy_lock);
+ store_packet->next = NULL;
+ dump_in_file(context, store_packet);
+ }
+ //printf("Thread exit. Busy packets: %p\n", ctrl->busy);
+ if( store_packet ) {
+ release_packets(store_packet);
+ }
+
+ ctrl->stop_confirm = 1;
+ return NULL;
+}
+
+int async_pcap_store_packet(void *context, void *buf, uint32_t size, uint64_t ts)
+{
+ struct pcap_store_control *ctrl = (struct pcap_store_control *)context;
+ struct storing_packet *store_packet = NULL;
+ int i, res = 0;
+ if( ctrl->free ) {
+ for( i = 100; i; i--) {
+ res = pthread_mutex_trylock(&ctrl->free_lock);
+ if( !res ) {
+ break;
+ } else if ( res !=EBUSY ) {
+ printf("Mutex error: %s!", strerror(errno));
+ break;
+ }
+ usleep(1);
+ }
+ } else {
+ res = ENOMEM;
+ }
+
+ if( res ) {
+ store_packet = alloc_packet(size);
+ } else {
+ store_packet = ctrl->free;
+ ctrl->free = store_packet->next;
+ pthread_mutex_unlock(&ctrl->free_lock);
+ store_packet->next = NULL;
+ }
+
+ //printf("Add packet. %p. Res %x\n", store_packet, res);
+ if( !store_packet ) {
+ return -ENOMEM;
+ }
+
+ store_packet->used_size = size < store_packet->size ? size : store_packet->size;
+ store_packet->real_size = size;
+ memcpy(store_packet->mem, buf, store_packet->used_size);
+ store_packet->ts = ts;
+
+ for( i = 1000; i; i--) {
+ res = pthread_mutex_trylock(&ctrl->busy_lock);
+ if( !res ) {
+ break;
+ } else if ( res !=EBUSY ) {
+ printf("Mutex error: %s!", strerror(errno));
+ break;
+ }
+ usleep(1);
+ }
+ if( !res ) {
+ if( ctrl->last_busy ) {
+ ctrl->last_busy->next = store_packet;
+ } else {
+ ctrl->busy = store_packet;
+ }
+ ctrl->last_busy = store_packet;
+ pthread_mutex_unlock(&ctrl->busy_lock);
+ } else {
+ printf("Loose packet! ts %lu\n", ts);
+ release_packets(store_packet);
+ }
+ return res;
+}
+
+void async_pcap_release_context(void *context)
+{
+ struct pcap_store_control *ctrl = (struct pcap_store_control *)context;
+ if( ctrl ) {
+ int i;
+ ctrl->stop_signal = 1;
+ for(i = 100; i && !ctrl->stop_confirm; i-- ) {
+ usleep(1);
+ continue;
+ };
+
+ if( !ctrl->stop_confirm ) {
+ void *ret;
+ pthread_cancel(ctrl->thread_id);
+ pthread_join(ctrl->thread_id, &ret);
+ printf("Cannot close thread correctly! Cancel it! Result: %p.", ret);
+ }
+
+ if( ctrl->busy ) {
+ dump_in_file(context, ctrl->busy);
+ release_packets(ctrl->busy);
+ ctrl->busy = NULL;
+ ctrl->last_busy = NULL;
+ }
+
+ if( ctrl->pd ) {
+ pcap_dump_close(ctrl->pd);
+ ctrl->pd = NULL;
+ }
+
+ if( ctrl->free ) {
+ release_packets(ctrl->free);
+ ctrl->free = NULL;
+ }
+ pthread_mutex_destroy(&ctrl->busy_lock);
+ pthread_mutex_destroy(&ctrl->free_lock);
+ free(ctrl);
+ }
+}
+
+int async_pcap_initialize_context(char *file_name, uint32_t packet_count, uint32_t packet_size, void **context)
+{
+ int res;
+ struct pcap_store_control *ctrl;
+ pcap_t *pcap;
+
+ if( !context || !packet_count || !packet_size ) {
+ return -EINVAL;
+ }
+
+ ctrl = (struct pcap_store_control *)malloc(sizeof(*ctrl));
+ if( !ctrl ) {
+ return -ENOMEM;
+ }
+ ctrl->free = NULL;
+ ctrl->busy = NULL;
+ ctrl->stop_signal = 0;
+ ctrl->stop_confirm = 1;
+ ctrl->last_busy = NULL;
+
+ if( res = pthread_mutex_init(&ctrl->busy_lock, NULL) ) {
+ free(ctrl);
+ return res;
+ }
+
+ if( res = pthread_mutex_init(&ctrl->free_lock, NULL) ) {
+ pthread_mutex_destroy(&ctrl->busy_lock);
+ free(ctrl);
+ return res;
+ }
+
+ pcap = pcap_open_dead_with_tstamp_precision(DLT_EN10MB, packet_size, PCAP_TSTAMP_PRECISION_NANO);
+ ctrl->pd = pcap_dump_open(pcap, file_name);
+ if( ctrl->pd == NULL ) {
+ async_pcap_release_context(ctrl);
+ printf("PCAP Dump open error! %s\n", pcap_geterr(pcap));
+ return -ENFILE;
+ }
+
+ while( --packet_count ) {
+ struct storing_packet *new_pkt = alloc_packet(packet_size);
+ if( !new_pkt ) {
+ printf("Cannot allocate memory for packets! Rest of packets: %d. Packet size %d\n", packet_count, packet_size);
+ break;
+ }
+ new_pkt->next = ctrl->free;
+ ctrl->free = new_pkt;
+ }
+
+ if( packet_count > 0 ) {
+ async_pcap_release_context(ctrl);
+ printf("Cannot allocate memory! Packet count %d. Error: %s\n", packet_count, strerror(res));
+ return -ENOMEM;
+ }
+
+ res = pthread_create(&ctrl->thread_id, NULL, &store_thread, ctrl);
+ if( res )
+ {
+ printf("Cannot create thread! %s\n", strerror(res));
+ async_pcap_release_context(ctrl);
+ return res;
+ }
+ for( res = 100; res && ctrl->stop_confirm; res-- ) {
+ usleep(100);
+ }
+
+ if( ctrl->stop_confirm ) {
+ printf("Thread is not run! Exit.");
+ async_pcap_release_context(ctrl);
+ return res;
+ }
+
+ *context = ctrl;
+ return 0;
+}
+//EOF
diff --git a/examples/common/async_pcap_storing.h b/examples/common/async_pcap_storing.h
new file mode 100644
index 00000000..fa5b5468
--- /dev/null
+++ b/examples/common/async_pcap_storing.h
@@ -0,0 +1,13 @@
+/*
+ Copyright (c) 2019 Egor Pomozov <Egor.Pomozov@aquantia.com>
+*/
+#include <stdint.h>
+
+#ifndef _ASYNC_PCAP_STORING_H_
+#define _ASYNC_PCAP_STORING_H_
+
+int async_pcap_store_packet(void *context, void *buf, uint32_t size, uint64_t ts);
+void async_pcap_release_context(void *context);
+int async_pcap_initialize_context(char *file_name, uint32_t packet_count, uint32_t packet_size, void **context);
+
+#endif //_ASYNC_PCAP_STORING_H_
diff --git a/examples/jackd-talker/Makefile b/examples/jackd-talker/Makefile
index 2ff78c0f..62b2c377 100644
--- a/examples/jackd-talker/Makefile
+++ b/examples/jackd-talker/Makefile
@@ -13,7 +13,7 @@ CC ?= gcc
OPT = -O2 -g
WARN = -Wall -Wextra -Wno-parentheses
CFLAGS = $(OPT) $(WARN) -std=gnu99
-CPPFLAGS = -I$(IGBLIB_DIR) -I$(DAEMONS_DIR)/mrpd -I$(MRPCLIENT_DIR) -I$(AVBLIB_DIR) -I$(DAEMONS_DIR)/common
+CPPFLAGS = -I$(IGBLIB_DIR) -I$(DAEMONS_DIR)/mrpd -I$(MRPCLIENT_DIR) -I$(AVBLIB_DIR) -I$(DAEMONS_DIR)/common -DAVB_FEATURE_ATL=0
LDLIBS = -ligb -lpci -lrt -pthread -ljack
LDFLAGS = -L$(IGBLIB_DIR)
diff --git a/examples/live_stream/Makefile b/examples/live_stream/Makefile
index b1fc083e..811ab122 100644
--- a/examples/live_stream/Makefile
+++ b/examples/live_stream/Makefile
@@ -15,7 +15,7 @@ CC?=gcc
OPT=-O2 -g
WARN=-Wall -Wextra -Wno-parentheses
CFLAGS=$(OPT) $(WARN)
-CPPFLAGS=-I$(IGBLIB_DIR) -I$(DAEMONS_DIR)/mrpd -I$(MRPCLIENT_DIR) -I$(AVBLIB_DIR) -I$(DAEMONS_DIR)/common
+CPPFLAGS=-I$(IGBLIB_DIR) -I$(DAEMONS_DIR)/mrpd -I$(MRPCLIENT_DIR) -I$(AVBLIB_DIR) -I$(DAEMONS_DIR)/common -DAVB_FEATURE_ATL=0
LDLIBS=-ligb -lpci -lrt -lpthread
LDFLAGS=-L$(IGBLIB_DIR)
diff --git a/examples/send_packet_precisely/Makefile b/examples/send_packet_precisely/Makefile
new file mode 100644
index 00000000..393d7b75
--- /dev/null
+++ b/examples/send_packet_precisely/Makefile
@@ -0,0 +1,36 @@
+AVBLIB_DIR = ../../lib/common
+AVBLIB_OBJS = avb_atl.o
+AVBLIB_TARGETS = $(addprefix $(AVBLIB_DIR)/,$(AVBLIB_OBJS))
+
+ASYNC_PCAP_DIR = ../common
+ASYNC_PCAP_OBJS = async_pcap_storing.o
+ASYNC_PCAP_TARGETS = $(addprefix $(ASYNC_PCAP_DIR)/,$(ASYNC_PCAP_OBJS))
+
+ATLLIB_DIR = ../../lib/atl_avb/lib
+DAEMONS_DIR = ../../daemons
+
+CC?=gcc
+OPT=-O0 -g
+WARN=-Wall -Wextra -Wno-parentheses
+CFLAGS=$(OPT) $(WARN)
+CPPFLAGS=-I$(ATLLIB_DIR) -I$(AVBLIB_DIR) -I$(ASYNC_PCAP_DIR) -I$(DAEMONS_DIR)/common -DAVB_FEATURE_ATL -DAVB_FEATURE_IGB
+LDLIBS=-latl -lpci -lrt -lm -pthread -lpcap
+LDFLAGS=-L$(ATLLIB_DIR)
+
+.PHONY: all clean
+
+all: send_packet_precisely
+
+send_packet_precisely: send_packet_precisely.o $(ASYNC_PCAP_TARGETS) $(AVBLIB_TARGETS)
+
+send_packet_precisely.o: send_packet_precisely.c
+
+$(AVBLIB_DIR)/%.o: $(AVBLIB_DIR)/%.h $(AVBLIB_DIR)/%.c
+ make -C $(AVBLIB_DIR) $@
+
+%: %.o
+ $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
+
+clean:
+ $(RM) send_packet_precisely
+ $(RM) `find . -name "*~" -o -name "*.[oa]" -o -name "\#*\#" -o -name TAGS -o -name core -o -name "*.orig"`
diff --git a/examples/send_packet_precisely/README b/examples/send_packet_precisely/README
new file mode 100644
index 00000000..20b88fd8
--- /dev/null
+++ b/examples/send_packet_precisely/README
@@ -0,0 +1,25 @@
+EXAMPLE APPLICATIONS
+
+The 'send_packet_precisely' application illustrates the various steps to schedule
+packets for sending.
+
+The simple spp application requires root permissions to execute and
+attach to the driver.
+ sudo ./send_packet_precisely
+
+To exit the app, hit Ctrl-C. The application gracefully tears down
+the connection to the driver. If the application unexpectedly aborts the
+kernel-mode driver also reclaims the various buffers and attempts to clean up.
+The application should be able to re-initialize and use the transmit queues
+without restarting the driver.
+
+/* Note this application requires using the provided gptp timesync daemon to
+provide the 802.1AS presentation times included in the 1722 frames. */
+
+Lastly, to build the application, you need to have the pciutils library
+installed. the latest version can be downloaded from:
+
+ < ftp://ftp.kernel.org/pub/software/utils/pciutils/ >.
+
+Download and extract the library, and run 'make;make install;make install-lib'.
+
diff --git a/examples/send_packet_precisely/send_packet_precisely.c b/examples/send_packet_precisely/send_packet_precisely.c
new file mode 100644
index 00000000..f79684fb
--- /dev/null
+++ b/examples/send_packet_precisely/send_packet_precisely.c
@@ -0,0 +1,1278 @@
+/******************************************************************************
+
+ Copyright (c) 2019, Aquantia Corporation
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#include <errno.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <math.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <linux/if.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <pthread.h>
+
+#include <pci/pci.h>
+
+//#include "avb.h"
+#include "avb_gptp.h"
+#include "avb_srp.h"
+#include "avb_avtp.h"
+#ifndef AVB_FEATURE_ATL
+/* IGB has not been disabled, so assume it is enabled. */
+#define AVB_FEATURE_ATL 1
+#endif
+#include "avb_atl.h"
+#include "async_pcap_storing.h"
+
+#define VERSION_STR "0.1.1"
+
+#define RAMP_SIZE (256)
+#define ETH_HDR_LEN (14) //18 with VLAN
+#define IPV4_HDR_LEN (20)
+#define IPV6_HDR_LEN (40)
+#define UDP_HDR_LEN (8)
+#define SRC_CHANNELS (2)
+#define GAIN (0.5)
+#define SAMPLES_PER_FRAME (60)
+#define SAMPLE_SIZE (4)
+//#define L2_PACKET_IPG (125000) /* (1) packet every 125 usec */
+#define L4_PORT ((uint16_t)5004)
+#define PKT_SZ (100)
+
+#define DEFAULT_ETHERTYPE 0x22f0
+#define DEFAULT_UDP_PORT 17220
+
+volatile int halt_tx_sig;//Global variable for signal handler
+
+enum {
+ spp_pat_zero_lt = 0,
+ spp_pat_ramp_lt,
+ spp_pat_iramp_lt,
+ spp_pat_lt_sin,
+ spp_pat_1722_sin,
+ spp_pat_pcap = -1,
+};
+
+struct refill_pkt {
+ u64 refill_time;
+ char *payload;
+ uint32_t pyld_size;
+ struct refill_pkt *next;
+};
+
+/* globals */
+unsigned char glob_station_addr[] = { 0, 0, 0, 0, 0, 0 };
+//unsigned char glob_stream_id[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+/* IEEE 1722 reserved address */
+unsigned char glob_l2_dest_addr[] = { 0x91, 0xE0, 0xF0, 0x00, 0x0e, 0x80 };
+unsigned char glob_l3_dest_addr[] = { 224, 0, 0, 115 };
+unsigned short glob_l3_dest_ipv6_addr[] = {0xFF02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF17, 0xFC0F};
+unsigned char glob_l3_ip_addr[] = {0, 0, 0 ,0};
+unsigned short glob_l3_ipv6_addr[] = {0, 0, 0 ,0, 0, 0, 0, 0};
+
+#ifndef _LOG_H_DEFINED_
+#define _LOG_H_DEFINED_
+
+#define LOG_LVL_CRITICAL 1
+#define LOG_LVL_ERROR 2
+#define LOG_LVL_WARNING 3
+#define LOG_LVL_INFO 4
+#define LOG_LVL_VERBOSE 5
+#define LOG_LVL_DEBUG 6
+
+u_int32_t log_level;
+void dump(u_int32_t lvl, const char *name, u_int8_t *buf, u_int32_t len) {};
+#endif
+
+static void *async_pcap_context = NULL;
+
+static inline uint64_t ST_rdtsc(void)
+{
+ uint64_t ret;
+ unsigned c, d;
+ asm volatile ("rdtsc":"=a" (c), "=d"(d));
+ ret = d;
+ ret <<= 32;
+ ret |= c;
+ return ret;
+}
+
+void gensine32(int32_t * buf, unsigned count)
+{
+ long double interval = (2 * ((long double)M_PI)) / count;
+ unsigned i;
+ for (i = 0; i < count; ++i) {
+ buf[i] =
+ (int32_t) (MAX_SAMPLE_VALUE * sinl(i * interval) * GAIN);
+ }
+}
+
+uint16_t inet_checksum(char *ip, int len)
+{
+ uint32_t sum = 0; /* assume 32 bit long, 16 bit short */
+ while(len > 1){
+ sum += (((uint8_t *)ip)[0] << 8) + ((uint8_t *)ip)[1];
+ ip += 2;
+ if(sum & 0x80000000) /* if high order bit set, fold */
+ sum = (sum & 0xFFFF) + (sum >> 16);
+ len -= 2;
+ }
+
+ if(len) /* take care of left over byte */
+ sum += (uint16_t) *(uint8_t *)ip;
+
+ while(sum>>16)
+ sum = (sum & 0xFFFF) + (sum >> 16);
+
+ return ~sum;
+}
+
+void l3_to_l2_multicast(unsigned char *l2, unsigned char *l3)
+{
+ l2[0] = 0x1;
+ l2[1] = 0x0;
+ l2[2] = 0x5e;
+ l2[3] = l3[1] & 0x7F;
+ l2[4] = l3[2];
+ l2[5] = l3[3];
+}
+
+void l3_to_l2_ipv6_multicast(unsigned char *l2, unsigned short *l3)
+{
+ l2[0] = 0x33;
+ l2[1] = 0x33;
+ l2[2] = ( l3[6] >> 8 ) & 0xFF;
+ l2[3] = (l3[6] & 0xFF);
+ l2[4] = ( l3[7] >> 8 ) & 0xFF;
+ l2[5] = ( l3[7] & 0xFF);
+}
+
+int get_samples(unsigned count, int32_t * buffer)
+{
+ static int init = 0;
+ static int32_t samples_onechannel[100];
+ static unsigned index = 0;
+
+ if (init == 0) {
+ gensine32(samples_onechannel, 100);
+ init = 1;
+ }
+
+ while (count > 0) {
+ int i;
+ for (i = 0; i < SRC_CHANNELS; ++i) {
+ *(buffer++) = samples_onechannel[index];
+ }
+ index = (index + 1) % 100;
+ --count;
+ }
+
+ return 0;
+}
+
+void sigint_handler(int signum)
+{
+ logprint(LOG_LVL_CRITICAL, "got SIGINT\n");
+ halt_tx_sig = signum;
+}
+
+int get_mac_address(char *interface)
+{
+ struct ifreq if_request;
+ int lsock;
+ int rc;
+
+ lsock = socket(PF_PACKET, SOCK_RAW, htons(0x800));
+ if (lsock < 0)
+ return -1;
+
+ memset(&if_request, 0, sizeof(if_request));
+ strncpy(if_request.ifr_name, interface, sizeof(if_request.ifr_name) - 1);
+ rc = ioctl(lsock, SIOCGIFHWADDR, &if_request);
+ if (rc < 0) {
+ close(lsock);
+ return -1;
+ }
+
+ memcpy(glob_station_addr, if_request.ifr_hwaddr.sa_data,
+ sizeof(glob_station_addr));
+ close(lsock);
+ return 0;
+}
+
+bool get_local_ipv6_address(char *interface)
+{
+ FILE *fptr;
+ char *ch;
+ ssize_t read;
+ size_t len = 0;
+ bool found = false;
+
+ fptr = fopen("/proc/net/if_inet6", "r");
+ if (fptr == NULL)
+ {
+ printf("Cannot open file \n");
+ return(0);
+ }
+
+ while ((read = getline(&ch, &len, fptr)) != -1) {
+ if (strstr(ch, interface) != NULL){
+ found = true;
+ char piece[4];
+ char *ptr;
+ long ret;
+
+ for (int i=0; i < 8; i++){
+ memcpy(piece, ch, 4);
+ ret = strtoul(piece, &ptr, 16);
+ glob_l3_ipv6_addr[i] = (unsigned short)ret;
+ ch += 4;
+ }
+ break;
+ }
+ }
+ fclose(fptr);
+ if (!found)
+ return 1;
+
+ return 0;
+}
+
+int get_local_ipv4_address(char *interface)
+{
+ struct ifreq if_request;
+ int lsock;
+ int rc;
+
+ lsock = socket(PF_PACKET, SOCK_RAW, htons(0x800));
+ if (lsock < 0)
+ return -1;
+
+ memset(&if_request, 0, sizeof(if_request));
+ strncpy(if_request.ifr_name, interface, sizeof(if_request.ifr_name) - 1);
+ rc = ioctl(lsock, SIOCGIFADDR, &if_request);
+
+ if (rc < 0) {
+ close(lsock);
+ return -1;
+ }
+ memcpy(glob_l3_ip_addr, if_request.ifr_addr.sa_data+2, sizeof(glob_l3_ip_addr));
+ close(lsock);
+
+ return 0;
+}
+
+static int add_ipv4_headers(char *pkt, uint16_t pkt_len)
+{
+ uint16_t checksum;
+ pkt[0] = 0x45;
+ pkt[1] = 0x00;
+ pkt[2] = (pkt_len >> 8) & 0xff;
+ pkt[3] = pkt_len & 0xFF;
+ pkt[4] = 0x12;
+ pkt[5] = 0x34;
+ pkt[6] = 0x40;
+ pkt[7] = 0x00;
+ pkt[8] = 0x01;
+ pkt[9] = 0x11;
+ pkt[10] = 0;
+ pkt[11] = 0;
+ pkt[12] = glob_l3_ip_addr[0];
+ pkt[13] = glob_l3_ip_addr[1];
+ pkt[14] = glob_l3_ip_addr[2];
+ pkt[15] = glob_l3_ip_addr[3];
+ pkt[16] = glob_l3_dest_addr[0];
+ pkt[17] = glob_l3_dest_addr[1];
+ pkt[18] = glob_l3_dest_addr[2];
+ pkt[19] = glob_l3_dest_addr[3];
+ checksum = inet_checksum(pkt, IPV4_HDR_LEN);
+ pkt[10] = (checksum >> 8) & 0xFF;
+ pkt[11] = checksum & 0xFF;
+
+ return IPV4_HDR_LEN;
+}
+
+static int add_ipv6_headers(char *pkt, uint16_t pkt_len)
+{
+ pkt[0] = 0x60;
+ pkt[4] = (pkt_len >> 8) & 0xFF;
+ pkt[5] = pkt_len & 0xFF;
+ pkt[6] = 0x11;
+ pkt[7] = 0x08;
+
+ pkt[8] = glob_l3_ipv6_addr[0] >> 8 & 0xFF;
+ pkt[9] = glob_l3_ipv6_addr[0];
+
+ pkt[10] = glob_l3_ipv6_addr[1] >> 8 & 0xFF;
+ pkt[11] = glob_l3_ipv6_addr[1];
+
+ pkt[12] = glob_l3_ipv6_addr[2] >> 8 & 0xFF;
+ pkt[13] = glob_l3_ipv6_addr[2];
+
+ pkt[14] = glob_l3_ipv6_addr[3] >> 8 & 0xFF;
+ pkt[15] = glob_l3_ipv6_addr[3];
+
+ pkt[16] = glob_l3_ipv6_addr[4] >> 8 & 0xFF;
+ pkt[17] = glob_l3_ipv6_addr[4];
+
+ pkt[18] = glob_l3_ipv6_addr[5] >> 8 & 0xFF;
+ pkt[19] = glob_l3_ipv6_addr[5];
+
+ pkt[20] = glob_l3_ipv6_addr[6] >> 8 & 0xFF;
+ pkt[21] = glob_l3_ipv6_addr[6];
+
+ pkt[22] = glob_l3_ipv6_addr[7] >> 8 & 0xFF;
+ pkt[23] = glob_l3_ipv6_addr[7];
+
+ for(int i = 24, j = 0; i < 40; i++ ){
+ if (i % 2 == 0) pkt[i] = (glob_l3_dest_ipv6_addr[j] >> 8) & 0xFF;
+ else{
+ pkt[i] = (glob_l3_dest_ipv6_addr[j] & 0xFF);
+ j++;
+ }
+ }
+
+ return IPV6_HDR_LEN;
+}
+
+static int add_udp_headers(char *pkt, const int udp_port, uint16_t len)
+{
+ pkt[0] = (L4_PORT >> 8) & 0xFF;
+ pkt[1] = L4_PORT & 0xFF;
+
+ pkt[2] = (udp_port >> 8) & 0xFF;
+ pkt[3] = udp_port & 0xFF;
+
+ pkt[4] = (len >> 8) & 0xFF;
+ pkt[5] = len & 0xFF;
+ pkt[6] = 0;
+ pkt[7] = 0;
+ return UDP_HDR_LEN;
+}
+
+static void fill_data_by_pattern(int pattern, char *pkt, uint32_t pkt_size, char *pcap, uint32_t pcap_size)
+{
+ unsigned k;
+ switch( pattern ) {
+ case spp_pat_1722_sin:
+ {
+ /* 1722 header update + payload */
+ seventeen22_header *l2_header0 = (seventeen22_header *)pkt;
+ l2_header0->cd_indicator = 0;
+ l2_header0->subtype = 0;
+ l2_header0->sid_valid = 1;
+ l2_header0->version = 0;
+ l2_header0->reset = 0;
+ l2_header0->reserved0 = 0;
+ l2_header0->gateway_valid = 0;
+ l2_header0->reserved1 = 0;
+ l2_header0->timestamp_uncertain = 0;
+ memset(&(l2_header0->stream_id), 0, sizeof(l2_header0->stream_id));
+ memcpy(&(l2_header0->stream_id), glob_station_addr,
+ sizeof(glob_station_addr));
+ l2_header0->length = htons(32);
+ break;
+ }
+ case spp_pat_ramp_lt:
+ for( k = 0; k < pkt_size; k ++) {
+ pkt[k] = k % RAMP_SIZE;
+ }
+ break;
+ case spp_pat_iramp_lt:
+ for( k = 0; k < pkt_size; k ++) {
+ pkt[k] = RAMP_SIZE-(k % RAMP_SIZE)-1;
+ }
+ break;
+ case spp_pat_zero_lt:
+ memset(pkt, 0, pkt_size);
+ break;
+ case spp_pat_lt_sin:
+ break;
+ case spp_pat_pcap:
+ memcpy(pkt, pcap, pcap_size);
+ break;
+ }
+}
+static void usage(void)
+{
+ fprintf(stderr, "\n"
+ "usage: send_packet_precisely [-h] -i interface-name [-I iteration-count]"
+ "[-c packet-in-iteration][-T launch-time-offset][-t launch-time-increment]"
+ "[-f pcap-file | -p pattern][-q 0|1][-o pcap-file]"
+ "\n"
+ "options:\n"
+ " -h show this message\n"
+ " -i specify interface for AVB connection\n"
+ " -I specify iteration count\n"
+ " -c specify sent packet count per iteration\n"
+ " -s specify sent packet payload size (0 or default - defined by pattern)\n"
+ " -T specify launch time to send first packet. If it is negative - it is an offset from current time\n"
+ " -t specify launch time increment between iterations\n"
+ " -f PCAP file name with packet pattern (requested launchtime "
+ "will be added at the end of this packet\n"
+ " -p specify sent packet pattern: 0-'zeros+lt', 1-'ramp+lt', 2-'inverse ramp+lt', 3-'lt+sin', 4-'AVTP1722+sin'\n"
+ " -r specify packet refill pattern: 0-'zeros+lt', 1-'ramp+lt', 2-'inverse ramp+lt', 3-'lt+sin', 4-'AVTP1722+sin'\n"
+ " -R specify launch time offset to refill packet. (Default value:) 0 - disable feature.\n"
+ " -q specify TSN queue (if supported by HW) 0 (default value) or 1\n"
+ " -v specify VLAN Id (HEX) [None by default]\n"
+ " -e specify Ethertype (HEX) [0x22f0 by default]\n"
+ " -V specify VLAN PCP [None by default, 0 if VLAN id is specified]\n"
+ " -o PCAP file name to store all send packets\n"
+ " -l Log level: 0 - Critical, 1 - Error, 2 - Warning, 3 - Info [Default], 4 - Verbose\n"
+ "\n"
+ "send_packet_precisely v" VERSION_STR "\n"
+ "Copyright (c) 2019, Aquantia Corporation\n"
+ );
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned i, j, k;
+ int err, sent, confirmed;
+ device_t atl_dev = {0};
+ /* Uncomment next line to sync with gPTP
+ int atl_shm_fd = -1;
+ char *atl_mmap = NULL;
+ gPtpTimeData td;
+ uint64_t now_local, now_8021as;
+ uint64_t update_8021as;
+ unsigned delta_8021as, delta_local;
+ */
+ struct atl_dma_alloc *a_pages;
+ uint32_t packet_per_page;
+ uint32_t a_page_count;
+ struct atl_packet a_packet;
+ uint32_t a_packet_count;
+ struct atl_packet *tmp_packet;
+ struct atl_packet *cleaned_packets;
+ struct atl_packet *free_packets;
+ int c, hw_vlan = 0;
+ unsigned int iteration_count = 1;
+ unsigned int packet_count = 1;
+ u_int64_t launch_time_offset = 1000000000u; //1 s
+ u_int64_t launch_time_increment = 125000u; //125 us
+ u_int64_t last_time = 0;
+ int rc = 0;
+ char *interface = NULL;
+ char *pcap_file = NULL;
+ char *output_file = NULL;
+ char *pcap_buf = NULL;
+ char *token;
+ const char delim[2] = {':'};
+
+ uint32_t pcap_size = 0;
+ int32_t vlan_id = -1;
+ int32_t vlan_pcp = -1;
+ int32_t et = -1;
+ int pattern = spp_pat_pcap;
+ int refill_pattern = spp_pat_pcap;
+ u_int64_t refill_lt_offset = 0u; //0
+ uint16_t seqnum;
+ uint64_t time_stamp = 0;
+ int32_t queue = 0;
+ int32_t lt_offset = -1;
+ int32_t seq_offset = -1;
+ int32_t sin_offset = -1;
+ int32_t lt_refill_offset = -1;
+ int32_t seq_refill_offset = -1;
+ int32_t sin_refill_offset = -1;
+
+ bool ipv4 = false;
+ bool ipv6 = false;
+ int16_t udp_port = -1;
+ uint16_t pseudohdr_chksum = 0;
+ uint8_t dest_addr[6];
+ size_t eth_hdr_size = ETH_HDR_LEN;
+ size_t total_hdr_size = eth_hdr_size;
+ size_t packet_size = 0, payload_size = 0, payload_refill_size = 0;
+
+ struct refill_pkt *last_refill = NULL;
+ struct refill_pkt *next_refill = NULL;
+
+ struct refill_pkt *refill_pkt = NULL;
+ //struct mrp_domain_attr *class_a = malloc(sizeof(struct mrp_domain_attr));
+ //struct mrp_domain_attr *class_b = malloc(sizeof(struct mrp_domain_attr));
+
+ for (;;) {
+ c = getopt(argc, argv, "h246i:I:c:T:t:f:p:q:v:V:e:R:r:s:S:l:u:d:o:");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'h':
+ usage();
+ break;
+ case 'i':
+ if (interface) {
+ logprint(LOG_LVL_WARNING, "only one interface per daemon is supported\n");
+ usage();
+ }
+ interface = strdup(optarg);
+ atl_dev.ifname = strdup(optarg);
+ break;
+ case 'o':
+ output_file = strdup(optarg);
+ break;
+ case 'f':
+ if (pcap_file || pattern != -1) {
+ logprint(LOG_LVL_WARNING, "only one pcap file or pattern is supported\n");
+ usage();
+ }
+ pcap_file = strdup(optarg);
+ break;
+ case 'p':
+ if (pcap_file || pattern != -1) {
+ logprint(LOG_LVL_WARNING, "only one from pcap file and pattern is supported\n");
+ usage();
+ }
+ pattern = strtoul( optarg, NULL, 10 );
+ break;
+ case 'r':
+ if (pcap_file || refill_pattern != -1) {
+ logprint(LOG_LVL_WARNING, "only one from pcap file and pattern is supported\n");
+ usage();
+ }
+ refill_pattern = strtoul( optarg, NULL, 10 );
+ break;
+ case 'R':
+ refill_lt_offset = strtoull( optarg, NULL, 10 );
+ break;
+ case 's':
+ payload_size = strtoul( optarg, NULL, 10 );
+ break;
+ case 'S':
+ payload_refill_size = strtoul( optarg, NULL, 10 );
+ break;
+ case 'c':
+ packet_count = strtoul( optarg, NULL, 10 );
+ break;
+ case 'I':
+ iteration_count = strtoul( optarg, NULL, 10 );
+ break;
+ case 't':
+ launch_time_increment = strtoull( optarg, NULL, 10 );
+ break;
+ case 'T':
+ {
+ int64_t launch_time = strtoll( optarg, NULL, 10 );
+ if( launch_time > 0 ) {
+ time_stamp = (u_int64_t)launch_time;
+ } else {
+ launch_time_offset = (u_int64_t)(-launch_time);
+ }
+ }
+ break;
+ case 'q':
+ queue = strtoul( optarg, NULL, 10 );
+ break;
+ case 'e':
+ if( ipv4 || ipv6 || udp_port >= 0 ) {
+ logprint(LOG_LVL_WARNING, "L2 packet cannot be mixed with IP/UDP settings\n");
+ usage();
+ }
+ et = strtol( optarg, NULL, 16 );
+ break;
+ case 'v':
+ vlan_id = strtol( optarg, NULL, 16 );
+ break;
+ case 'V':
+ vlan_pcp = strtol( optarg, NULL, 10 );
+ break;
+ case 'l':
+ log_level = strtol( optarg, NULL, 10 );
+ break;
+ case '2':
+ if( ipv4 || ipv6 || udp_port >= 0 ) {
+ logprint(LOG_LVL_WARNING, "L2 packet cannot be mixed with IP/UDP settings\n");
+ usage();
+ }
+ break;
+ case '4':
+ if( et >= 0 || ipv6 ) {
+ logprint(LOG_LVL_WARNING, "IPv4 packet cannot be mixed with L2/IPv6 settings\n");
+ usage();
+ }
+ ipv4 = true;
+ break;
+
+ case '6':
+ if( et >= 0 || ipv4 ) {
+ logprint(LOG_LVL_WARNING, "IPv6 packet cannot be mixed with L2/IPv4 settings\n");
+ usage();
+ }
+ ipv6 = true;
+ break;
+ case 'u':
+ if( et >= 0 ) {
+ logprint(LOG_LVL_WARNING, "UDP packet cannot be mixed with L2 settings\n");
+ usage();
+ }
+ udp_port = strtol( optarg, NULL, 10 );
+ break;
+
+ case 'd':
+ if( ipv4 || ipv6 || udp_port >= 0 ) {
+ logprint(LOG_LVL_WARNING, "L2 packet cannot be mixed with IP/UDP settings\n");
+ usage();
+ }
+ token = strtok(optarg, delim);
+ i = 0;
+ while(token != NULL)
+ {
+ glob_l2_dest_addr[i] = strtol(token, NULL, 16);
+ token = strtok(NULL, delim);
+ i++;
+ }
+ if( et < 0 )
+ et = DEFAULT_ETHERTYPE;
+
+ break;
+ }
+ }
+
+ if (optind < argc)
+ usage();
+ if (NULL == interface) {
+ usage();
+ }
+ if( (pattern < 0 && pcap_file == NULL) || pattern >= spp_pat_1722_sin ) {
+ logprint(LOG_LVL_ERROR, "Must specify valid pattern or pcap file\n" );
+ usage();
+ }
+ if( refill_lt_offset > 0 && (refill_pattern < 0 && pcap_file == NULL) || refill_pattern >= spp_pat_1722_sin ) {
+ logprint(LOG_LVL_ERROR, "Must specify valid refill pattern or pcap file\n" );
+ usage();
+ }
+ if( refill_lt_offset > 0 && pattern == refill_pattern ) {
+ logprint(LOG_LVL_ERROR, "Fill pattern and refill pattern should be different.\n" );
+ usage();
+ }
+ if ( udp_port >= 0 && ipv4 == ipv6 ) {
+ logprint(LOG_LVL_ERROR, "IPv4 or IPv6 must be specified for UDP port!.\n" );
+ usage();
+ }
+
+ if( (ipv4 || ipv6 ) && udp_port < 0 ){
+ udp_port = DEFAULT_UDP_PORT;
+ }
+
+ if ( udp_port >= 0 ){
+ if ( ipv4 ) {
+ et = 0x0800;
+ total_hdr_size += IPV4_HDR_LEN + UDP_HDR_LEN;
+ }
+ if ( ipv6 ) {
+ et = 0x86DD;
+ total_hdr_size += IPV6_HDR_LEN + UDP_HDR_LEN;
+ }
+ } else if( et < 0 ) {
+ et = DEFAULT_ETHERTYPE;
+ }
+ if( vlan_id >= 0 || vlan_pcp >= 0 ) {
+ eth_hdr_size += 0x4;
+ total_hdr_size += 0x4;
+ if( vlan_id < 0 ) vlan_id = 0;
+ if( vlan_pcp < 0 ) vlan_pcp = 0;
+ }
+
+ logprint(LOG_LVL_DEBUG, "Ethertype %x\n", et);
+ logprint(LOG_LVL_DEBUG, "VLAN: PCP %d TAG %03x\n", vlan_pcp, vlan_id);
+
+ logprint(LOG_LVL_VERBOSE, "Fill pattern %x\n", pattern);
+ switch(pattern){
+ case spp_pat_zero_lt: //zero + launch time
+ case spp_pat_ramp_lt: //ramp + launch time
+ case spp_pat_iramp_lt: //inversed ramp + launch time
+ if( !payload_size ) {
+ payload_size = RAMP_SIZE;
+ }
+ packet_size = total_hdr_size + sizeof(uint64_t) + payload_size;
+ lt_offset = total_hdr_size + payload_size;
+ break;
+ case spp_pat_lt_sin: //launch time + sin
+ if( !payload_size ) {
+ payload_size = SAMPLES_PER_FRAME*SRC_CHANNELS*SAMPLE_SIZE;
+ }
+ packet_size = total_hdr_size + sizeof(uint64_t) + payload_size;
+ lt_offset = total_hdr_size;
+ sin_offset = total_hdr_size + sizeof(uint64_t);
+ break;
+ case spp_pat_1722_sin: //1722 + sin
+ if( !payload_size ) {
+ payload_size = SAMPLES_PER_FRAME*SRC_CHANNELS*SAMPLE_SIZE;
+ }
+ packet_size = total_hdr_size + sizeof(seventeen22_header) + payload_size;
+ /* 1722 header update + payload */
+ lt_offset = total_hdr_size + 8;//offsetof(seventeen22_header, timestamp);
+ seq_offset = total_hdr_size + 2; //offsetof(seventeen22_header, seq_number);
+ sin_offset = total_hdr_size + sizeof(seventeen22_header);
+ break;
+ default:
+ //read file
+ packet_size = PKT_SZ;
+ }
+
+ logprint(LOG_LVL_VERBOSE, "Refill pattern %x\n", refill_pattern);
+ switch(refill_pattern){
+ case spp_pat_zero_lt: //zero + launch time
+ case spp_pat_ramp_lt: //ramp + launch time
+ case spp_pat_iramp_lt: //inversed ramp + launch time
+ if( !payload_refill_size ) {
+ payload_refill_size = RAMP_SIZE;
+ }
+ lt_refill_offset = payload_refill_size;
+ break;
+ case spp_pat_lt_sin: //launch time + sin
+ if( !payload_refill_size ) {
+ payload_refill_size = SAMPLES_PER_FRAME*SRC_CHANNELS*SAMPLE_SIZE;
+ }
+ lt_refill_offset = 0;
+ sin_refill_offset = sizeof(uint64_t);
+ break;
+ case spp_pat_1722_sin: //1722 + sin
+ if( !payload_refill_size ) {
+ payload_refill_size = SAMPLES_PER_FRAME*SRC_CHANNELS*SAMPLE_SIZE;
+ }
+ /* 1722 header update + payload */
+ lt_refill_offset = 8;//offsetof(seventeen22_header, timestamp);
+ seq_refill_offset = 2; //offsetof(seventeen22_header, seq_number);
+ sin_refill_offset = sizeof(seventeen22_header);
+ break;
+ }
+
+ packet_per_page = atl_getpagesize() / packet_size;
+ a_packet_count = iteration_count * packet_count;
+ if( a_packet_count > ATL_AVB_RING_SIZE ) {
+ a_packet_count = ATL_AVB_RING_SIZE;
+ }
+ a_page_count = (a_packet_count + packet_per_page - 1) / packet_per_page;
+
+ logprint(LOG_LVL_DEBUG, "Allocate page count %x\n", a_page_count);
+ a_pages = malloc(a_page_count*sizeof(*a_pages));
+ if( a_pages == NULL ){
+ logprint(LOG_LVL_ERROR, "malloc failed (%s) - out of memory?\n",
+ strerror(errno));
+ return errno;
+ }
+
+ err = pci_connect(&atl_dev);
+ if (err) {
+ logprint(LOG_LVL_CRITICAL, "connect failed (%s) - are you running as root?\n",
+ strerror(errno));
+ return errno;
+ }
+ err = atl_init(&atl_dev);
+ if (err) {
+ logprint(LOG_LVL_CRITICAL, "init failed (%s) - is the driver really loaded?\n",
+ strerror(errno));
+ return errno;
+ }
+
+ signal(SIGINT, sigint_handler);
+
+ rc = get_mac_address(interface);
+ if (rc) {
+ logprint(LOG_LVL_ERROR, "failed to open interface\n");
+ usage();
+ goto error_alloc_dma;
+ }
+
+ if (ipv4 | ipv6) {
+ if (ipv4) rc = get_local_ipv4_address(interface);
+ else rc = get_local_ipv6_address(interface);
+ if (rc) {
+ logprint(LOG_LVL_CRITICAL, "Failed to obtain ip address!\n",
+ strerror(errno));
+ goto error_alloc_dma;
+ }
+ }
+
+ if( ipv4 ) l3_to_l2_multicast(dest_addr, glob_l3_dest_addr);
+ else if( ipv6 ) l3_to_l2_ipv6_multicast(dest_addr, glob_l3_dest_ipv6_addr);
+ else memcpy(dest_addr, glob_l2_dest_addr, sizeof(dest_addr));
+
+ err = atl_set_class_bandwidth(&atl_dev,
+ queue ? 0 : packet_count*8000*(packet_size + 24),
+ queue ? packet_count*8000*(packet_size + 24) : 0); // 24 - IPG+PREAMBLE+FCS
+ if (err) {
+ logprint(LOG_LVL_ERROR, "A bandwidth Request failed\n");
+ goto error_alloc_dma;
+ }
+ //memset(glob_stream_id, 0, sizeof(glob_stream_id));
+ //memcpy(glob_stream_id, glob_station_addr, sizeof(glob_station_addr));
+
+ for( i = 0; i < a_page_count; i++ ) {
+ err = atl_dma_malloc_page(&atl_dev, &a_pages[i]);
+ if (err) {
+ logprint(LOG_LVL_ERROR, "malloc failed (%s) - out of memory?\n",
+ strerror(errno));
+ goto error_alloc_dma;
+ }
+ }
+
+ if( output_file ) {
+ if( async_pcap_initialize_context(output_file, a_page_count*packet_per_page, packet_size, &async_pcap_context) ) {
+ logprint(LOG_LVL_ERROR, "Cannot create async_pcap_context. %s\n",
+ strerror(errno));
+ return errno;
+ }
+ logprint(LOG_LVL_VERBOSE, "Create async_pcap_context. File path %s\n", output_file);
+ }
+
+ a_packet.offset = 0;
+ a_packet.next = NULL;
+ free_packets = NULL;
+ seqnum = 0;
+ j = 0;
+ /* divide the dma page into buffers for packets */
+ for (i = 0; i < a_packet_count; i++) {
+ tmp_packet = malloc(sizeof(struct atl_packet));
+ if (NULL == tmp_packet) {
+ logprint(LOG_LVL_ERROR, "failed to allocate atl_packet memory!\n");
+ goto error_alloc_packet;
+ }
+
+ if( !(i % packet_per_page) ) {
+ a_packet.map.paddr = a_pages[j].dma_paddr;
+ a_packet.map.mmap_size = a_pages[j].mmap_size;
+ a_packet.vaddr = a_pages[j].dma_vaddr + a_packet.offset;
+ a_packet.len = packet_size;
+ j++;
+ }
+
+ *tmp_packet = a_packet;
+ tmp_packet->offset = (i % packet_per_page) * packet_size;
+ tmp_packet->vaddr += tmp_packet->offset;
+ tmp_packet->next = free_packets;
+ memset(tmp_packet->vaddr, 0, packet_size); /* MAC header at least */
+ memcpy(tmp_packet->vaddr, dest_addr, sizeof(dest_addr));
+ memcpy(tmp_packet->vaddr + 6, glob_station_addr,
+ sizeof(glob_station_addr));
+
+ if( refill_lt_offset > 0 ) {
+ refill_pkt = (struct refill_pkt *)malloc(sizeof(struct refill_pkt));
+ if( !refill_pkt ) {
+ logprint(LOG_LVL_ERROR, "No memory for packet refilling\n");
+ goto error_alloc_packet;
+ }
+ tmp_packet->extra = refill_pkt;
+ } else {
+ tmp_packet->extra = NULL;
+ }
+
+ k = ETH_HDR_LEN - 2;
+ /* Q-tag */
+ tmp_packet->vlan = 0;
+ if( hw_vlan && vlan_id >= 0 ) {
+ tmp_packet->vlan = vlan_id;
+ }else if( vlan_id >= 0 ) {
+ ((char *)tmp_packet->vaddr)[k++] = 0x81;
+ ((char *)tmp_packet->vaddr)[k++] = 0x00;
+ ((char *)tmp_packet->vaddr)[k++] = ((vlan_pcp << 5) | (vlan_id >> 8)) & 0xff;
+ //((ctx->domain_class_a_priority << 13 | ctx->domain_class_a_vid)) >> 8;
+ ((char *)tmp_packet->vaddr)[k++] = vlan_id & 0xff;
+ //((ctx->domain_class_a_priority << 13 | ctx->domain_class_a_vid)) & 0xFF;
+ }
+ ((char *)tmp_packet->vaddr)[k++] = (et >> 8) & 0xff; /* 1722 eth type */
+ ((char *)tmp_packet->vaddr)[k++] = (et >> 0) & 0xff;
+
+ if( udp_port >= 0 ) {
+ if( ipv4 ) {
+ k += add_ipv4_headers((char *)tmp_packet->vaddr + k, packet_size - eth_hdr_size);
+ } else {
+ k += add_ipv6_headers((char *)tmp_packet->vaddr + k, packet_size - eth_hdr_size - IPV6_HDR_LEN);
+ }
+ add_udp_headers((char *)tmp_packet->vaddr + k, udp_port, packet_size - k);
+ }
+
+ fill_data_by_pattern(pattern, ((char *)tmp_packet->vaddr) + total_hdr_size, payload_size, pcap_buf, pcap_size);
+ tmp_packet->len = packet_size;
+ free_packets = tmp_packet;
+ //dump(0,"New AVB packet", (u_int8_t *)tmp_packet->vaddr, packet_size);
+ }
+
+ if( udp_port >= 0 ) {
+ char pseudo_hdr[40] = {0};
+ if( ipv4 ) {
+ pseudo_hdr[0] = glob_l3_ip_addr[0];
+ pseudo_hdr[1] = glob_l3_ip_addr[1];
+ pseudo_hdr[2] = glob_l3_ip_addr[2];
+ pseudo_hdr[3] = glob_l3_ip_addr[3];
+ pseudo_hdr[4] = glob_l3_dest_addr[0];
+ pseudo_hdr[5] = glob_l3_dest_addr[1];
+ pseudo_hdr[6] = glob_l3_dest_addr[2];
+ pseudo_hdr[7] = glob_l3_dest_addr[3];
+ pseudo_hdr[8] = 0x00;
+ pseudo_hdr[9] = 0x11; //UDP
+ pseudo_hdr[10] = ((packet_size - k) >> 8) & 0xFF;
+ pseudo_hdr[11] = (packet_size - k) & 0xFF;
+ }
+ else {
+ pseudo_hdr[0] = (glob_l3_ipv6_addr[0] >> 8) & 0xFF;
+ pseudo_hdr[1] = glob_l3_ipv6_addr[0] & 0xFF;
+
+ pseudo_hdr[2] = (glob_l3_ipv6_addr[1] >> 8) & 0xFF;
+ pseudo_hdr[3] = glob_l3_ipv6_addr[1] & 0xFF;
+
+ pseudo_hdr[4] = (glob_l3_ipv6_addr[2] >> 8) & 0xFF;
+ pseudo_hdr[5] = glob_l3_ipv6_addr[2] & 0xFF;
+
+ pseudo_hdr[6] = (glob_l3_ipv6_addr[3] >> 8) & 0xFF;
+ pseudo_hdr[7] = glob_l3_ipv6_addr[3] & 0xFF;
+
+ pseudo_hdr[8] = (glob_l3_ipv6_addr[4] >> 8) & 0xFF;
+ pseudo_hdr[9] = glob_l3_ipv6_addr[4] & 0xFF;
+
+ pseudo_hdr[10] = (glob_l3_ipv6_addr[5] >> 8) & 0xFF;
+ pseudo_hdr[11] = glob_l3_ipv6_addr[5] & 0xFF;
+
+ pseudo_hdr[12] = (glob_l3_ipv6_addr[6] >> 8) & 0xFF;
+ pseudo_hdr[13] = glob_l3_ipv6_addr[6] & 0xFF;
+
+ pseudo_hdr[14] = (glob_l3_ipv6_addr[7] >> 8) & 0xFF;
+ pseudo_hdr[15] = glob_l3_ipv6_addr[7] & 0xFF;
+
+ pseudo_hdr[16] = (glob_l3_dest_ipv6_addr[0] >> 8) & 0xFF;
+ pseudo_hdr[17] = glob_l3_dest_ipv6_addr[0] & 0xFF;
+
+ pseudo_hdr[18] = (glob_l3_dest_ipv6_addr[1] >> 8) & 0xFF;
+ pseudo_hdr[19] = glob_l3_dest_ipv6_addr[1] & 0xFF;
+
+ pseudo_hdr[20] = (glob_l3_dest_ipv6_addr[2] >> 8) & 0xFF;
+ pseudo_hdr[21] = glob_l3_dest_ipv6_addr[2] & 0xFF;
+
+ pseudo_hdr[22] = (glob_l3_dest_ipv6_addr[3] >> 8) & 0xFF;
+ pseudo_hdr[23] = glob_l3_dest_ipv6_addr[3] & 0xFF;
+
+ pseudo_hdr[24] = (glob_l3_dest_ipv6_addr[4] >> 8) & 0xFF;
+ pseudo_hdr[25] = glob_l3_dest_ipv6_addr[4] & 0xFF;
+
+ pseudo_hdr[26] = (glob_l3_dest_ipv6_addr[5] >> 8) & 0xFF;
+ pseudo_hdr[27] = glob_l3_dest_ipv6_addr[5] & 0xFF;
+
+ pseudo_hdr[28] = (glob_l3_dest_ipv6_addr[6] >> 8) & 0xFF;
+ pseudo_hdr[29] = glob_l3_dest_ipv6_addr[6] & 0xFF;
+
+ pseudo_hdr[30] = (glob_l3_dest_ipv6_addr[7] >> 8) & 0xFF;
+ pseudo_hdr[31] = glob_l3_dest_ipv6_addr[7] & 0xFF;
+
+ pseudo_hdr[32] = 0;
+ pseudo_hdr[33] = 0;
+
+ pseudo_hdr[34] = ((packet_size - k) >> 8) & 0xFF;
+ pseudo_hdr[35] = (packet_size - k) & 0xFF;
+
+ pseudo_hdr[36] = 0;
+ pseudo_hdr[37] = 0;
+ pseudo_hdr[38] = 0;
+
+ pseudo_hdr[39] = 0x11;
+
+ }
+ pseudohdr_chksum = ~inet_checksum(pseudo_hdr, 40);
+ }
+
+ halt_tx_sig = 0;
+
+ /* Uncomment for sync time with gPTP
+ if(-1 == gptpinit(&atl_shm_fd, &atl_mmap)) {
+ fprintf(stderr, "GPTP init failed.\n");
+ return EXIT_FAILURE;
+ }
+
+ if (-1 == gptpscaling(atl_mmap, &td)) {
+ fprintf(stderr, "GPTP scaling failed.\n");
+ return EXIT_FAILURE;
+ }
+
+ if(atl_get_wallclock( &atl_dev, &now_local, NULL ) > 0) {
+ fprintf( stderr, "Failed to get wallclock time\n" );
+ return EXIT_FAILURE;
+ }
+ update_8021as = td.local_time - td.ml_phoffset;
+ delta_local = (unsigned)(now_local - td.local_time);
+ delta_8021as = (unsigned)(td.ml_freqoffset * delta_local);
+ now_8021as = update_8021as + delta_8021as;
+ */
+
+ if(atl_get_wallclock( &atl_dev, &last_time, NULL ) > 0) {
+ logprint(LOG_LVL_ERROR, "Failed to get wallclock time\n" );
+ return EXIT_FAILURE;
+ }
+ if( !time_stamp ) {
+ time_stamp = last_time + launch_time_offset;//now_8021as + RENDER_DELAY;
+ }
+ logprint(LOG_LVL_INFO, "Current time: %lu, launch time: %lu\n", last_time, time_stamp);
+
+ logprint(LOG_LVL_DEBUG, "Packet offsets: lt %d, seq_offset %d, sin_offset %d\n", lt_offset, seq_offset, sin_offset);
+ rc = nice(-20);
+ sent = confirmed = 0;
+ j = 0;
+ while ( !halt_tx_sig && (iteration_count || sent > confirmed )) {
+ uint32_t empty_space = 0;
+ if( next_refill ) {
+ if(atl_get_wallclock( &atl_dev, &last_time, NULL ) > 0) {
+ logprint(LOG_LVL_ERROR, "Failed to get wallclock time\n" );
+ break;
+ }
+
+ while( next_refill && next_refill->refill_time < last_time ) {
+ uint64_t prev_seq, prev_lt;
+ refill_pkt = next_refill;
+ prev_lt = ((uint64_t *)(refill_pkt->payload + (lt_offset - total_hdr_size)))[0];
+ if( seq_refill_offset >= 0 && seq_offset >= 0 ) {
+ prev_seq = ((uint64_t *)(refill_pkt->payload + (seq_offset - total_hdr_size)))[0];
+ }
+ fill_data_by_pattern(refill_pattern, refill_pkt->payload, refill_pkt->pyld_size, pcap_buf, pcap_size);
+ if( sin_refill_offset >= 0 ) {
+ get_samples(refill_pkt->pyld_size / (SRC_CHANNELS*SAMPLE_SIZE),
+ (int32_t *)(refill_pkt->payload + sin_refill_offset));
+ }
+ *((uint64_t *)(refill_pkt->payload + lt_refill_offset)) = prev_lt;
+ if( seq_refill_offset >= 0 ) {
+ *((uint64_t *)(refill_pkt->payload + seq_refill_offset)) = seq_offset >= 0 ? prev_seq : htonl(seqnum++);
+ }
+ if( udp_port >= 0 ) {
+ char *udp_hdr = refill_pkt->payload - UDP_HDR_LEN;
+ int16_t chksum = 0;
+ udp_hdr[6] = (pseudohdr_chksum >> 8) & 0xFF;
+ udp_hdr[7] = pseudohdr_chksum & 0xFF;
+ chksum = inet_checksum(udp_hdr, packet_size - k);
+ udp_hdr[6] = (chksum >> 8) & 0xff;
+ udp_hdr[7] = chksum & 0xff;
+ }
+
+ logprint(LOG_LVL_VERBOSE, "refill packet with refill time %lu at time %lu\n", next_refill->refill_time, last_time);
+ next_refill = next_refill->next;
+ }
+
+ if( !next_refill ) {
+ last_refill = next_refill;
+ }
+ }
+ empty_space = atl_get_xmit_space(&atl_dev, queue);
+ tmp_packet = free_packets;
+ if (tmp_packet && iteration_count && empty_space > packet_count) {
+ struct atl_packet *send_packets = NULL, *last_attached = NULL;
+ while( tmp_packet && j < packet_count && empty_space > 1 ) {
+ free_packets = tmp_packet->next;
+ tmp_packet->next = NULL;
+ if( sin_offset >= 0 ) {
+ get_samples( payload_size / (SRC_CHANNELS*SAMPLE_SIZE), (int32_t *)(((char *)tmp_packet->vaddr) + sin_offset));
+ }
+
+ /* unfortuntely unless this thread is at rtprio
+ * you get pre-empted between fetching the time
+ * and programming the packet and get a late packet
+ */
+ a_packet.hwtime = a_packet.flags = 0;
+ tmp_packet->attime = time_stamp;
+ logprint(LOG_LVL_VERBOSE, "Packet %p, time %lu\n", tmp_packet, time_stamp);
+ *((uint64_t *)(((char *)tmp_packet->vaddr) + lt_offset)) = htole64(time_stamp);
+ if( seq_offset >= 0 ) {
+ *((uint64_t *)((char *)tmp_packet->vaddr + seq_offset)) = htonl(seqnum++);
+ }
+
+ if( udp_port >= 0 ) {
+ char *udp_hdr = ((char *)tmp_packet->vaddr) + total_hdr_size - UDP_HDR_LEN;
+ int16_t chksum = 0;
+ udp_hdr[6] = (pseudohdr_chksum >> 8) & 0xFF;
+ udp_hdr[7] = pseudohdr_chksum & 0xFF;
+ chksum = inet_checksum(udp_hdr, packet_size - k);
+ udp_hdr[6] = (chksum >> 8) & 0xff;
+ udp_hdr[7] = chksum & 0xff;
+ }
+
+ if( send_packets == NULL ) {
+ send_packets = tmp_packet;
+ }
+ if( last_attached ) {
+ last_attached->next = tmp_packet;
+ }
+ last_attached = tmp_packet;
+ dump(LOG_LVL_VERBOSE, "Prepare AVB packet", (u_int8_t *)tmp_packet->vaddr, packet_size);
+
+ if( refill_lt_offset > 0 ) {
+ refill_pkt = (struct refill_pkt *)tmp_packet->extra;
+ refill_pkt->payload = ((char *)tmp_packet->vaddr) + total_hdr_size;
+ refill_pkt->pyld_size = payload_refill_size;
+ refill_pkt->refill_time = time_stamp - refill_lt_offset;
+ refill_pkt->next = NULL;
+ if( last_refill )
+ last_refill->next = refill_pkt;
+ last_refill = refill_pkt;
+ if( !next_refill ) {
+ next_refill = refill_pkt;
+ }
+ }
+
+ tmp_packet = free_packets;
+ j++;
+ }
+
+ logprint(LOG_LVL_DEBUG, "Prepare %d packets.\n", j);
+ err = atl_xmit(&atl_dev, queue, &send_packets);
+
+ while( send_packets ) {
+ // Clean up non-send packets and refill areas
+ logprint(LOG_LVL_DEBUG, "Clean up unsent packet %p.\n", send_packets);
+ tmp_packet = send_packets;
+ send_packets = tmp_packet->next;
+ if( refill_lt_offset > 0 && next_refill ) {
+ struct refill_pkt *tmp_refill = next_refill;
+ refill_pkt = (struct refill_pkt *)tmp_packet->extra;
+
+ if( next_refill != refill_pkt ) {
+ while( tmp_refill && tmp_refill->next != refill_pkt ) {
+ tmp_refill = tmp_refill->next;
+ }
+
+ if( tmp_refill && tmp_refill->next == refill_pkt ) {
+ tmp_refill->next = NULL;
+ last_refill = tmp_refill;
+ }
+ } else {
+ next_refill = NULL;
+ last_refill = NULL;
+ }
+ }
+ tmp_packet->next = free_packets;
+ free_packets = tmp_packet;
+ j--;
+ }
+
+ if( err < 0 ) {
+ break;
+ }
+
+ if( j == packet_count ) {
+ sent += packet_count;
+ time_stamp += launch_time_increment;
+ iteration_count--;
+ logprint(LOG_LVL_DEBUG, "Complete iteration. Rest iteration count %d.\n", iteration_count);
+ j = 0;
+ continue;
+ }
+ }
+
+ cleaned_packets = NULL;
+ err = atl_clean(&atl_dev, &cleaned_packets);
+ if( err < 0 ) {
+ break;
+ }
+
+ if( !err ) {
+ continue;
+ }
+ tmp_packet = cleaned_packets;
+ i = 0;
+ while( tmp_packet ) {
+ if( tmp_packet->flags & ATL_PKT_FLAG_HW_TS_VLD ){
+ logprint(LOG_LVL_INFO, "AVB Packet %05d sent time %016lu\n", confirmed+i, tmp_packet->hwtime);
+
+ if( async_pcap_context ) {
+ logprint(LOG_LVL_DEBUG, "Dump packet to pcap file\n");
+ async_pcap_store_packet(async_pcap_context, tmp_packet->vaddr, tmp_packet->len, tmp_packet->hwtime);
+ }
+ }
+ i++;
+ if( refill_lt_offset > 0 ) {
+ //Fill packet with original pattern
+ fill_data_by_pattern(pattern, refill_pkt->payload, refill_pkt->pyld_size, pcap_buf, pcap_size);
+ }
+ tmp_packet = tmp_packet->next;
+ }
+
+ if( free_packets ) {
+ tmp_packet = free_packets;
+ while (tmp_packet->next) {
+ tmp_packet = tmp_packet->next;
+ }
+ tmp_packet->next = cleaned_packets;
+ }
+ else {
+ free_packets = cleaned_packets;
+ }
+ confirmed += err;
+ }
+ logprint(LOG_LVL_VERBOSE, "Iteration_count %d, sent %d, confirmed %d\n", iteration_count, sent, confirmed);
+ //sleep(5);
+ rc = nice(0);
+ atl_stop_tx(&atl_dev, queue, &cleaned_packets);
+error_alloc_packet:
+ tmp_packet = free_packets;
+ while( tmp_packet ) {
+ free_packets = tmp_packet->next;
+ if( tmp_packet->extra ) {
+ free(tmp_packet->extra);
+ }
+ free(tmp_packet);
+ tmp_packet = free_packets;
+ }
+
+ tmp_packet = cleaned_packets;
+ while( tmp_packet ) {
+ cleaned_packets = tmp_packet->next;
+ if( tmp_packet->extra ) {
+ free(tmp_packet->extra);
+ }
+ free(tmp_packet);
+ tmp_packet = cleaned_packets;
+ }
+
+ atl_set_class_bandwidth(&atl_dev, 0, 0); /* disable Qav ??? */
+ for( i = 0; i < a_page_count; i++){
+ atl_dma_free_page(&atl_dev, &a_pages[i]);
+ }
+error_alloc_dma:
+ free( a_pages );
+
+ // rc = gptpdeinit(&atl_shm_fd, &atl_mmap);
+ err = atl_detach(&atl_dev);
+
+ if( async_pcap_context ) {
+ async_pcap_release_context(async_pcap_context);
+ async_pcap_context = NULL;
+ }
+
+ pthread_exit(NULL);
+
+ return EXIT_SUCCESS;
+}
diff --git a/examples/simple_talker/Makefile b/examples/simple_talker/Makefile
index 756f888c..e85eb8b8 100644
--- a/examples/simple_talker/Makefile
+++ b/examples/simple_talker/Makefile
@@ -13,7 +13,7 @@ CC?=gcc
OPT=-O2 -g
WARN=-Wall -Wextra -Wno-parentheses
CFLAGS=$(OPT) $(WARN)
-CPPFLAGS=-I$(IGBLIB_DIR) -I$(DAEMONS_DIR)/mrpd -I$(MRPCLIENT_DIR) -I$(AVBLIB_DIR) -I$(DAEMONS_DIR)/common
+CPPFLAGS=-I$(IGBLIB_DIR) -I$(DAEMONS_DIR)/mrpd -I$(MRPCLIENT_DIR) -I$(AVBLIB_DIR) -I$(DAEMONS_DIR)/common -DAVB_FEATURE_ATL=0
LDLIBS=-ligb -lpci -lrt -lm -pthread
LDFLAGS=-L$(IGBLIB_DIR)
diff --git a/lib/atl_avb b/lib/atl_avb
new file mode 160000
+Subproject 1536d2e0d6a950fa0db7320e19f49c1eff257ed
diff --git a/lib/avtp_pipeline/avtp/openavb_avtp.c b/lib/avtp_pipeline/avtp/openavb_avtp.c
index 82932d23..83886078 100644
--- a/lib/avtp_pipeline/avtp/openavb_avtp.c
+++ b/lib/avtp_pipeline/avtp/openavb_avtp.c
@@ -338,6 +338,10 @@ openavbRC openavbAvtpTx(void *pv, bool bSend, bool txBlockingInIntf)
timeNsec = item->pAvtpTime->timeNsec;
openavbMediaQTailUnlock(pStream->pMediaQ);
}
+#elif ATL_LAUNCHTIME_ENABLED
+ if( pStream->pMapCB->map_lt_calc_cb ) {
+ pStream->pMapCB->map_lt_calc_cb(pStream->pMediaQ, &timeNsec);
+ }
#endif
// Call mapping module to move data into AVTP frame
@@ -354,6 +358,10 @@ openavbRC openavbAvtpTx(void *pv, bool bSend, bool txBlockingInIntf)
timeNsec = item->pAvtpTime->timeNsec;
openavbMediaQTailUnlock(pStream->pMediaQ);
}
+#elif ATL_LAUNCHTIME_ENABLED
+ if( pStream->pMapCB->map_lt_calc_cb ) {
+ pStream->pMapCB->map_lt_calc_cb(pStream->pMediaQ, &timeNsec);
+ }
#endif
// Blocking in interface mode. Pull from media queue for tx first
diff --git a/lib/avtp_pipeline/avtp_pipeline.mk b/lib/avtp_pipeline/avtp_pipeline.mk
index e08ac9b9..6e9565ca 100644
--- a/lib/avtp_pipeline/avtp_pipeline.mk
+++ b/lib/avtp_pipeline/avtp_pipeline.mk
@@ -1,7 +1,8 @@
AVB_FEATURE_ENDPOINT ?= 1
IGB_LAUNCHTIME_ENABLED ?= 0
+ATL_LAUNCHTIME_ENABLED ?= 1
AVB_FEATURE_GSTREAMER ?= 0
-PLATFORM_TOOLCHAIN ?= generic
+PLATFORM_TOOLCHAIN ?= x86_aqc_linux
.PHONY: all clean
@@ -23,5 +24,6 @@ build/Makefile:
-DCMAKE_TOOLCHAIN_FILE=../platform/Linux/$(PLATFORM_TOOLCHAIN).cmake \
-DAVB_FEATURE_ENDPOINT=$(AVB_FEATURE_ENDPOINT) \
-DIGB_LAUNCHTIME_ENABLED=$(IGB_LAUNCHTIME_ENABLED) \
+ -DATL_LAUNCHTIME_ENABLED=$(ATL_LAUNCHTIME_ENABLED) \
-DAVB_FEATURE_GSTREAMER=$(AVB_FEATURE_GSTREAMER) \
..
diff --git a/lib/avtp_pipeline/include/openavb_log_pub.h b/lib/avtp_pipeline/include/openavb_log_pub.h
index 846c169e..6d824af4 100644
--- a/lib/avtp_pipeline/include/openavb_log_pub.h
+++ b/lib/avtp_pipeline/include/openavb_log_pub.h
@@ -182,6 +182,16 @@ 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,
+ va_list args);
+
void avbLogFn(
int level,
const char *tag,
diff --git a/lib/avtp_pipeline/include/openavb_map_pub.h b/lib/avtp_pipeline/include/openavb_map_pub.h
index 02a58957..4f360454 100755
--- a/lib/avtp_pipeline/include/openavb_map_pub.h
+++ b/lib/avtp_pipeline/include/openavb_map_pub.h
@@ -177,6 +177,17 @@ typedef void (*openavb_map_set_src_bitrate_cb_t)(media_q_t *pMediaQ, unsigned in
*/
typedef unsigned int (*openavb_map_get_max_interval_frames_cb_t)(media_q_t *pMediaQ, SRClassIdx_t sr_class);
+#if ATL_LAUNCHTIME_ENABLED
+/** This talker callback will be called for each AVB observation interval to calculate a launchtime of packet.
+ *
+ * \param pMediaQ A pointer to the media queue for this stream
+ * \param pData pointer to data
+ * \param lt launchtime of media q item
+ * \return One of enum \ref tx_cb_ret_t values.
+ */
+typedef bool (*openavb_map_lt_calc_cb_t)(media_q_t *pMediaQ, U64 *lt);
+#endif
+
/** Mapping callbacks structure.
*/
typedef struct {
@@ -208,6 +219,11 @@ typedef struct {
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;
+
+#if ATL_LAUNCHTIME_ENABLED
+ // Launchtime calculation
+ openavb_map_lt_calc_cb_t map_lt_calc_cb;
+#endif
} openavb_map_cb_t;
/** Main initialization entry point into the mapping module.
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
index ca342b2b..6b703478 100755
--- a/lib/avtp_pipeline/map_uncmp_audio/openavb_map_uncmp_audio.c
+++ b/lib/avtp_pipeline/map_uncmp_audio/openavb_map_uncmp_audio.c
@@ -149,7 +149,10 @@ typedef struct {
U8 DBC;
avb_audio_mcr_t audioMcr;
-
+#if ATL_LAUNCHTIME_ENABLED
+ // Transmit interval in nanoseconds.
+ U32 txIntervalNs;
+#endif
} pvt_data_t;
static void x_calculateSizes(media_q_t *pMediaQ)
@@ -232,6 +235,10 @@ static void x_calculateSizes(media_q_t *pMediaQ)
pPubMapInfo->packetSampleSizeBytes = 4;
+#if ATL_LAUNCHTIME_ENABLED
+ pPvtData->txIntervalNs = 1000000000u / pPvtData->txInterval;
+ AVB_LOGF_INFO("LT interval ns:%d", pPvtData->txIntervalNs);
+#endif
AVB_LOGF_INFO("Rate:%d", pPubMapInfo->audioRate);
AVB_LOGF_INFO("Bits:%d", pPubMapInfo->audioBitDepth);
AVB_LOGF_INFO("Channels:%d", pPubMapInfo->audioChannels);
@@ -395,6 +402,42 @@ void openavbMapUncmpAudioAVDECCInitCB(media_q_t *pMediaQ, U16 configIdx, U16 des
AVB_TRACE_EXIT(AVB_TRACE_MAP);
}
+#if ATL_LAUNCHTIME_ENABLED
+#define ATL_LT_OFFSET 500000
+bool openavbMapUncmpLaunchtimCalculationCB(media_q_t *pMediaQ, U64 *lt)
+{
+static U64 last_time = 0;
+ bool res = false;
+ media_q_item_t* pMediaQItem;
+ pvt_data_t *pPvtData = pMediaQ->pPvtMapInfo;
+ AVB_TRACE_ENTRY(AVB_TRACE_INTF);
+ if (!lt) {
+ AVB_LOG_ERROR("Mapping module launchtime argument incorrect.");
+ return false;
+ }
+ if (!pPvtData) {
+ AVB_LOG_ERROR("Private mapping module data not allocated.");
+ return false;
+ }
+
+ pMediaQItem = openavbMediaQTailLock(pMediaQ, true);
+ if (pMediaQItem) {
+ if (pMediaQItem->readIdx == 0) {
+ last_time = pMediaQItem->pAvtpTime->timeNsec;
+ *lt = last_time + ATL_LT_OFFSET;
+ res = true;
+ } else if( last_time != 0 ) {
+ last_time += pPvtData->txIntervalNs;
+ *lt = last_time + ATL_LT_OFFSET;
+ res = true;
+ }
+ openavbMediaQTailUnlock(pMediaQ);
+ }
+ AVB_TRACE_EXIT(AVB_TRACE_INTF);
+ return res;
+}
+#endif
+
// 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)
@@ -737,6 +780,9 @@ extern DLL_EXPORT bool openavbMapUncmpAudioInitialize(media_q_t *pMediaQ, openav
pMapCB->map_rx_cb = openavbMapUncmpAudioRxCB;
pMapCB->map_end_cb = openavbMapUncmpAudioEndCB;
pMapCB->map_gen_end_cb = openavbMapUncmpAudioGenEndCB;
+#if ATL_LAUNCHTIME_ENABLED
+ pMapCB->map_lt_calc_cb = openavbMapUncmpLaunchtimCalculationCB;
+#endif
pPvtData->itemCount = 20;
pPvtData->txInterval = 0;
diff --git a/lib/avtp_pipeline/mcs/openavb_mcs.c b/lib/avtp_pipeline/mcs/openavb_mcs.c
index 51d7fe9a..441fc52c 100644
--- a/lib/avtp_pipeline/mcs/openavb_mcs.c
+++ b/lib/avtp_pipeline/mcs/openavb_mcs.c
@@ -65,7 +65,7 @@ void openavbMcsAdvance(mcs_t *mediaClockSynth)
if (mediaClockSynth->tickCount % mediaClockSynth->correctionInterval == 0) {
mediaClockSynth->edgeTime += mediaClockSynth->correctionAmount;
}
-#if !IGB_LAUNCHTIME_ENABLED
+#if !IGB_LAUNCHTIME_ENABLED && !ATL_LAUNCHTIME_ENABLED
IF_LOG_INTERVAL(8000) {
U64 nowNS;
CLOCK_GETTIME64(OPENAVB_CLOCK_WALLTIME, &nowNS);
diff --git a/lib/avtp_pipeline/openavb_common/CMakeLists.txt b/lib/avtp_pipeline/openavb_common/CMakeLists.txt
index e5f7d7f4..80dacfda 100644
--- a/lib/avtp_pipeline/openavb_common/CMakeLists.txt
+++ b/lib/avtp_pipeline/openavb_common/CMakeLists.txt
@@ -4,9 +4,16 @@ if (AVB_FEATURE_IGB)
${AVB_SRC_DIR}/../common/avb_igb.c
)
endif ()
+if (AVB_FEATURE_ATL)
+ message("-- common ATL library included")
+ SET (ATL_FILES
+ ${AVB_SRC_DIR}/../common/avb_atl.c
+ )
+endif ()
SET (SRC_FILES ${SRC_FILES}
${AVB_SRC_DIR}/../common/avb_gptp.c
${AVB_SRC_DIR}/openavb_common/mrp_client.c
${IGB_FILES}
+ ${ATL_FILES}
PARENT_SCOPE
)
diff --git a/lib/avtp_pipeline/openavb_common/mrp_client.c b/lib/avtp_pipeline/openavb_common/mrp_client.c
index 1a285b5f..c7b28e63 100644
--- a/lib/avtp_pipeline/openavb_common/mrp_client.c
+++ b/lib/avtp_pipeline/openavb_common/mrp_client.c
@@ -615,7 +615,7 @@ int mrp_get_domain(int *class_a_id, int *a_priority, u_int16_t * a_vid,
*b_priority = domain_class_b_priority;
*b_vid = domain_class_b_vid;
}
- return domain_a_valid && domain_b_valid ? 0 : -1;
+ return domain_a_valid || domain_b_valid ? 0 : -1;
}
int mrp_join_vlan()
diff --git a/lib/avtp_pipeline/platform/Linux/CMakeLists.txt b/lib/avtp_pipeline/platform/Linux/CMakeLists.txt
index 14f427cc..43818e8a 100644
--- a/lib/avtp_pipeline/platform/Linux/CMakeLists.txt
+++ b/lib/avtp_pipeline/platform/Linux/CMakeLists.txt
@@ -90,6 +90,7 @@ if (DEFINED AVB_FEATURE_AVDECC)
set ( AVB_FEATURE_GSTREAMER 0 )
set ( AVB_FEATURE_ENDPOINT 0 )
set ( AVB_FEATURE_IGB 0 )
+ set ( AVB_FEATURE_ATL 0 )
set ( IGB_LAUNCHTIME_ENABLED 0 )
endif ()
endif ()
@@ -112,6 +113,7 @@ if (NOT DEFINED AVB_FEATURE_AVDECC)
endif ()
if (NOT DEFINED AVB_FEATURE_IGB)
set ( AVB_FEATURE_IGB 1 )
+ set ( AVB_FEATURE_ATL 0 )
endif ()
# Default launchtime feature
@@ -122,6 +124,12 @@ else ()
endif ()
set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DIGB_LAUNCHTIME_ENABLED=${IGB_LAUNCHTIME_ENABLED}" )
+# Default launchtime feature for ATL
+if (NOT DEFINED ATL_LAUNCHTIME_ENABLED)
+ set ( ATL_LAUNCHTIME_ENABLED 1 )
+endif ()
+set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DATL_LAUNCHTIME_ENABLED=${ATL_LAUNCHTIME_ENABLED}" )
+
# Export feature flags for sub-builds
if (AVB_FEATURE_FQTSS)
@@ -138,8 +146,15 @@ if (AVB_FEATURE_AVDECC)
endif ()
if (AVB_FEATURE_IGB)
set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAVB_FEATURE_IGB=1" )
+ set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAVB_FEATURE_ATL=0" )
else ()
set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAVB_FEATURE_IGB=0" )
+endif()
+if (AVB_FEATURE_ATL)
+ set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAVB_FEATURE_IGB=0" )
+ set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAVB_FEATURE_ATL=1" )
+else ()
+ set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAVB_FEATURE_ATL=0" )
endif ()
#Export Platform defines
@@ -266,6 +281,9 @@ if ( AVB_FEATURE_PCAP )
if ( AVB_FEATURE_IGB )
set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAVB_FEATURE_IGB" )
endif ()
+ if ( AVB_FEATURE_ATL )
+ set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAVB_FEATURE_ATL" )
+ endif ()
endif ()
add_subdirectory ( util )
diff --git a/lib/avtp_pipeline/platform/Linux/rawsock/atl_rawsock.c b/lib/avtp_pipeline/platform/Linux/rawsock/atl_rawsock.c
new file mode 100644
index 00000000..9424ea96
--- /dev/null
+++ b/lib/avtp_pipeline/platform/Linux/rawsock/atl_rawsock.c
@@ -0,0 +1,271 @@
+/*************************************************************************************************************
+Copyright (c) 2019, Aquantia Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+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 "atl_rawsock.h"
+#include "pcap_rawsock.h"
+#include "simple_rawsock.h"
+#include "avb.h"
+#include "openavb_atl.h"
+#include "avb_sched.h"
+
+#include "openavb_trace.h"
+
+#define AVB_LOG_COMPONENT "Raw Socket"
+#include "openavb_log.h"
+
+// needed for gptplocaltime()
+extern gPtpTimeData gPtpTD;
+
+void *atlRawsockOpen(atl_rawsock_t* rawsock, const char *ifname, bool rx_mode, bool tx_mode, U16 ethertype, U32 frame_size, U32 num_frames)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK);
+
+ if (!pcapRawsockOpen((pcap_rawsock_t*)rawsock, ifname, rx_mode,
+ tx_mode, ethertype, frame_size, num_frames))
+ {
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK);
+ return NULL;
+ }
+
+ if (tx_mode) {
+ // Deal with frame size.
+ if (frame_size == 0) {
+ // use interface MTU as max frames size, if none specified
+ rawsock->base.frameSize = ATL_MTU;
+ }
+ else if (frame_size > ATL_MTU) {
+ AVB_LOGF_ERROR("Creating rawsock; requested frame size exceeds %d", ATL_MTU);
+ atlRawsockClose(rawsock);
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK);
+ return NULL;
+ } else {
+ rawsock->base.frameSize = frame_size;
+ }
+
+ // ATL setup
+ rawsock->atl_dev = atlAcquireDevice(ifname);
+
+ // select class B queue by default
+ rawsock->queue = 1;
+ }
+
+ // fill virtual functions table
+ rawsock_cb_t *cb = &rawsock->base.cb;
+ cb->close = atlRawsockClose;
+ cb->getTxFrame = atlRawsockGetTxFrame;
+ cb->relTxFrame = atlRawsockRelTxFrame;
+ cb->txSetMark = atlRawsockTxSetMark;
+ cb->txFrameReady = atlRawsockTxFrameReady;
+ cb->send = atlRawsockSend;
+ cb->txBufLevel = atlRawsockTxBufLevel;
+ cb->getTXOutOfBuffers = atlRawsockGetTXOutOfBuffers;
+ cb->getTXOutOfBuffersCyclic = atlRawsockGetTXOutOfBuffersCyclic;
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK);
+ return rawsock;
+}
+
+void atlRawsockClose(void *pvRawsock)
+{
+ atl_rawsock_t *rawsock = (atl_rawsock_t*)pvRawsock;
+ if (rawsock->atl_dev) {
+ atlReleaseDevice(rawsock->atl_dev);
+ }
+
+ pcapRawsockClose((pcap_rawsock_t*)rawsock);
+}
+
+bool atlRawsockTxSetMark(void *pvRawsock, int mark)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+
+ atl_rawsock_t *rawsock = (atl_rawsock_t*)pvRawsock;
+ if (!VALID_TX_RAWSOCK(rawsock)) {
+ AVB_LOG_ERROR("Setting TX mark; invalid argument passed");
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return FALSE;
+ }
+
+ int fwmarkClass = TC_AVB_MARK_CLASS(mark);
+
+ if (fwmarkClass == SR_CLASS_A) {
+ rawsock->queue = 0;
+ } else if (fwmarkClass == SR_CLASS_B) {
+ rawsock->queue = 1;
+ } else {
+ AVB_LOGF_ERROR("fwmarkClass %d is not proper SR_CLASS", fwmarkClass);
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return FALSE;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return TRUE;
+}
+
+// Get a buffer from the ring to use for TX
+U8 *atlRawsockGetTxFrame(void *pvRawsock, bool blocking, unsigned int *len)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+
+ atl_rawsock_t *rawsock = (atl_rawsock_t*)pvRawsock;
+
+ if (!VALID_TX_RAWSOCK(rawsock) || len == NULL) {
+ AVB_LOG_ERROR("Getting TX frame; bad arguments");
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return NULL;
+ }
+
+ U8 *ret = NULL;
+ int bBufferBusyReported = 0;
+ U32 iterationCount = 10;
+
+ do {
+ rawsock->tx_packet = atlGetTxPacket(rawsock->atl_dev);
+ if (!rawsock->tx_packet && blocking) {
+ if (0 == bBufferBusyReported) {
+ if (!rawsock->txOutOfBuffer) {
+ AVB_LOGF_DEBUG("Getting TX frame (%p): TX buffer busy", rawsock);
+ }
+ ++rawsock->txOutOfBuffer;
+ ++rawsock->txOutOfBufferCyclic;
+ } else if (1 == bBufferBusyReported) {
+ AVB_LOGF_DEBUG("Getting TX frame (%p): TX buffer busy after usleep(10) verify if there are any late frames", rawsock);
+ }
+ ++bBufferBusyReported;
+ usleep(10);
+ }
+ } while (!rawsock->tx_packet && blocking && iterationCount--);
+
+ if (rawsock->tx_packet) {
+ *len = rawsock->base.frameSize;
+ ret = rawsock->tx_packet->vaddr;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return ret;
+}
+
+// Release a TX frame, without marking it as ready to send
+bool atlRawsockRelTxFrame(void *pvRawsock, U8 *pBuffer)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+ atl_rawsock_t *rawsock = (atl_rawsock_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;
+ }
+
+ atlRelTxPacket(rawsock->atl_dev, rawsock->queue, rawsock->tx_packet);
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return TRUE;
+}
+
+// Release a TX frame, and mark it as ready to send
+bool atlRawsockTxFrameReady(void *pvRawsock, U8 *pBuffer, unsigned int len, U64 timeNsec)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+ atl_rawsock_t *rawsock = (atl_rawsock_t*)pvRawsock;
+
+ if (!VALID_TX_RAWSOCK(rawsock)) {
+ AVB_LOG_ERROR("Send; invalid argument");
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return -1;
+ }
+
+ int err;
+
+ rawsock->tx_packet->len = len;
+
+#if ATL_LAUNCHTIME_ENABLED
+ gptpmaster2local(&gPtpTD, timeNsec, &rawsock->tx_packet->attime);
+#else
+ rawsock->tx_packet->attime = 0;
+#endif
+ err = atl_xmit(rawsock->atl_dev, rawsock->queue, &rawsock->tx_packet);
+ if (err) {
+ AVB_LOGF_ERROR("atl_xmit failed: %s", strerror(err));
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return !err;
+}
+// Send all packets that are ready (i.e. tell kernel to send them)
+int atlRawsockSend(void *pvRawsock)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+
+ // atlRawsock sends frames in atlRawsockTxFrameReady
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return 1;
+}
+
+int atlRawsockTxBufLevel(void *pvRawsock)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL);
+ atl_rawsock_t *rawsock = (atl_rawsock_t*)pvRawsock;
+
+ int nInUse = atlTxBufLevel(rawsock->atl_dev);
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL);
+ return nInUse;
+}
+
+unsigned long atlRawsockGetTXOutOfBuffers(void *pvRawsock)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK);
+ unsigned long counter = 0;
+ atl_rawsock_t *rawsock = (atl_rawsock_t*)pvRawsock;
+
+ if(VALID_TX_RAWSOCK(rawsock)) {
+ counter = rawsock->txOutOfBuffer;
+ }
+
+ AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK);
+ return counter;
+}
+
+unsigned long atlRawsockGetTXOutOfBuffersCyclic(void *pvRawsock)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK);
+ unsigned long counter = 0;
+ atl_rawsock_t *rawsock = (atl_rawsock_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/atl_rawsock.h b/lib/avtp_pipeline/platform/Linux/rawsock/atl_rawsock.h
new file mode 100644
index 00000000..76e8de11
--- /dev/null
+++ b/lib/avtp_pipeline/platform/Linux/rawsock/atl_rawsock.h
@@ -0,0 +1,67 @@
+/*************************************************************************************************************
+Copyright (c) 2019, Aquantia Corporation
+All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+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 ATL_RAWSOCK_H
+#define ATL_RAWSOCK_H
+
+#include "rawsock_impl.h"
+#include <pcap/pcap.h>
+#include "atl.h"
+
+typedef struct {
+ base_rawsock_t base;
+ pcap_t *handle;
+ device_t *atl_dev;
+ struct atl_packet *tx_packet;
+ int queue;
+ unsigned long txOutOfBuffer;
+ unsigned long txOutOfBufferCyclic;
+} atl_rawsock_t;
+
+void *atlRawsockOpen(atl_rawsock_t* rawsock, const char *ifname, bool rx_mode, bool tx_mode, U16 ethertype, U32 frame_size, U32 num_frames);
+
+void atlRawsockClose(void *pvRawsock);
+
+bool atlRawsockTxSetMark(void *pvRawsock, int mark);
+
+U8 *atlRawsockGetTxFrame(void *pvRawsock, bool blocking, unsigned int *len);
+
+bool atlRawsockRelTxFrame(void *pvRawsock, U8 *pBuffer);
+
+bool atlRawsockTxFrameReady(void *pvRawsock, U8 *pBuffer, unsigned int len, U64 timeNsec);
+
+int atlRawsockSend(void *pvRawsock);
+
+int atlRawsockTxBufLevel(void *pvRawsock);
+
+unsigned long atlRawsockGetTXOutOfBuffers(void *pvRawsock);
+
+unsigned long atlRawsockGetTXOutOfBuffersCyclic(void *pvRawsock);
+
+#endif
diff --git a/lib/avtp_pipeline/platform/Linux/rawsock/openavb_rawsock.c b/lib/avtp_pipeline/platform/Linux/rawsock/openavb_rawsock.c
index c05053a5..bc67cd80 100644
--- a/lib/avtp_pipeline/platform/Linux/rawsock/openavb_rawsock.c
+++ b/lib/avtp_pipeline/platform/Linux/rawsock/openavb_rawsock.c
@@ -37,6 +37,9 @@ https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
#if AVB_FEATURE_IGB
#include "igb_rawsock.h"
#endif
+#if AVB_FEATURE_ATL
+#include "atl_rawsock.h"
+#endif
#endif
#include "openavb_rawsock.h"
@@ -79,6 +82,8 @@ void *openavbRawsockOpen(const char *ifname_uri, bool rx_mode, bool tx_mode, U16
#if AVB_FEATURE_PCAP
#if AVB_FEATURE_IGB
char proto[IF_NAMESIZE] = "igb";
+#elif AVB_FEATURE_ATL
+ char proto[IF_NAMESIZE] = "atl";
#else
char proto[IF_NAMESIZE] = "pcap";
#endif
@@ -166,6 +171,21 @@ void *openavbRawsockOpen(const char *ifname_uri, bool rx_mode, bool tx_mode, U16
// call constructor
pvRawsock = igbRawsockOpen((igb_rawsock_t*)rawsock, ifname, rx_mode, tx_mode, ethertype, frame_size, num_frames);
#endif
+#if AVB_FEATURE_ATL
+ } else if (strcmp(proto, "atl") == 0) {
+
+ AVB_LOG_INFO("Using *atl* implementation");
+
+ // allocate memory for rawsock object
+ atl_rawsock_t *rawsock = calloc(1, sizeof(atl_rawsock_t));
+ if (!rawsock) {
+ AVB_LOG_ERROR("Creating rawsock; malloc failed");
+ return NULL;
+ }
+
+ // call constructor
+ pvRawsock = atlRawsockOpen((atl_rawsock_t*)rawsock, ifname, rx_mode, tx_mode, ethertype, frame_size, num_frames);
+#endif
#endif
} else {
AVB_LOGF_ERROR("Unknown proto %s specified.", proto);
diff --git a/lib/avtp_pipeline/platform/Linux/x86_aqc_linux.cmake b/lib/avtp_pipeline/platform/Linux/x86_aqc_linux.cmake
new file mode 100644
index 00000000..8be9aaf2
--- /dev/null
+++ b/lib/avtp_pipeline/platform/Linux/x86_aqc_linux.cmake
@@ -0,0 +1,52 @@
+
+if (AVB_FEATURE_AVDECC)
+ set ( AVB_FEATURE_GSTREAMER 0 )
+ set ( AVB_FEATURE_PCAP 0 )
+ set ( AVB_FEATURE_IGB 0 )
+ set ( AVB_FEATURE_ATL 0 )
+else ()
+ set ( AVB_FEATURE_PCAP 1 )
+ set ( AVB_FEATURE_ATL 1 )
+ set ( AVB_FEATURE_IGB 0 )
+
+ set ( GSTREAMER_1_0 0 )
+endif ()
+
+
+# and another kernel sources
+#set ( LINUX_KERNEL_DIR "/usr/src/kernel" )
+
+# build configuration
+set ( OPENAVB_HAL "x86_aqc" )
+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_aqc/include
+if (AVB_FEATURE_ATL)
+ ${CMAKE_SOURCE_DIR}/../atl_avb/lib
+endif ()
+ ${CMAKE_SOURCE_DIR}/openavb_common
+ ${CMAKE_SOURCE_DIR}/../../daemons/common
+ ${CMAKE_SOURCE_DIR}/../../daemons/mrpd
+ ${CMAKE_SOURCE_DIR}/../../daemons/maap/common
+)
+
+if (AVB_FEATURE_ATL)
+ set ( PLATFORM_LINK_DIRECTORIES
+ ${CMAKE_SOURCE_DIR}/../atl_avb/lib
+ )
+endif ()
+
+if (AVB_FEATURE_ATL)
+ set ( PLATFORM_LINK_LIBRARIES
+ atl
+ pci
+ )
+endif ()
+
+# TODO_OPENAVB : need this?
+# Set platform specific define
+#set ( PLATFORM_DEFINE "AVB_DELAY_TWEAK_USEC=15" )
diff --git a/lib/avtp_pipeline/platform/Linux/x86_i210_linux.cmake b/lib/avtp_pipeline/platform/Linux/x86_i210_linux.cmake
index 48e62223..2b2f7714 100644
--- a/lib/avtp_pipeline/platform/Linux/x86_i210_linux.cmake
+++ b/lib/avtp_pipeline/platform/Linux/x86_i210_linux.cmake
@@ -3,9 +3,11 @@ if (AVB_FEATURE_AVDECC)
set ( AVB_FEATURE_GSTREAMER 0 )
set ( AVB_FEATURE_PCAP 0 )
set ( AVB_FEATURE_IGB 0 )
+ set ( AVB_FEATURE_ATL 0 )
else ()
set ( AVB_FEATURE_PCAP 1 )
set ( AVB_FEATURE_IGB 1 )
+ set ( AVB_FEATURE_ATL 0 )
set ( GSTREAMER_1_0 0 )
endif ()
@@ -24,7 +26,7 @@ set ( OPENAVB_PLATFORM "${OPENAVB_HAL}-${OPENAVB_OSAL}" )
set ( PLATFORM_INCLUDE_DIRECTORIES
${CMAKE_SOURCE_DIR}/platform/x86_i210/include
if (AVB_FEATURE_IGB)
- ${CMAKE_SOURCE_DIR}/../igb
+ ${CMAKE_SOURCE_DIR}/../igb_avb/lib
endif ()
${CMAKE_SOURCE_DIR}/openavb_common
${CMAKE_SOURCE_DIR}/../../daemons/common
@@ -34,7 +36,7 @@ endif ()
if (AVB_FEATURE_IGB)
set ( PLATFORM_LINK_DIRECTORIES
- ${CMAKE_SOURCE_DIR}/../igb
+ ${CMAKE_SOURCE_DIR}/../igb_avb/lib
)
endif ()
diff --git a/lib/avtp_pipeline/platform/x86_aqc/mcr/openavb_mcr_hal.c b/lib/avtp_pipeline/platform/x86_aqc/mcr/openavb_mcr_hal.c
new file mode 100644
index 00000000..13236986
--- /dev/null
+++ b/lib/avtp_pipeline/platform/x86_aqc/mcr/openavb_mcr_hal.c
@@ -0,0 +1,63 @@
+/*************************************************************************************************************
+Copyright (c) 2012-2015, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+Copyright (c) 2016-2017, Harman International Industries, Incorporated
+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_aqc/mcr/openavb_mcr_hal.h b/lib/avtp_pipeline/platform/x86_aqc/mcr/openavb_mcr_hal.h
new file mode 100644
index 00000000..c6db6044
--- /dev/null
+++ b/lib/avtp_pipeline/platform/x86_aqc/mcr/openavb_mcr_hal.h
@@ -0,0 +1,38 @@
+/*************************************************************************************************************
+Copyright (c) 2012-2015, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+Copyright (c) 2016-2017, Harman International Industries, Incorporated
+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_aqc/openavb_atl.c b/lib/avtp_pipeline/platform/x86_aqc/openavb_atl.c
new file mode 100644
index 00000000..d881204f
--- /dev/null
+++ b/lib/avtp_pipeline/platform/x86_aqc/openavb_atl.c
@@ -0,0 +1,278 @@
+/*************************************************************************************************************
+Copyright (c) 2019, Aquantia Corporation
+All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+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_atl.h"
+#include "openavb_osal.h"
+#include "avb_atl.h"
+#include "atl.h"
+
+#define AVB_LOG_COMPONENT "HAL Ethernet"
+#include "openavb_pub.h"
+#include "openavb_log.h"
+#include "openavb_trace.h"
+
+
+static pthread_mutex_t gAtlDeviceMutex = PTHREAD_MUTEX_INITIALIZER;
+#define LOCK() pthread_mutex_lock(&gAtlDeviceMutex)
+#define UNLOCK() pthread_mutex_unlock(&gAtlDeviceMutex)
+
+static struct atl_dma_alloc g_pages[ATL_PAGES];
+static struct atl_packet *g_free_packets;
+
+static device_t *atl_dev = NULL;
+static int atl_dev_users = 0; // time uses it
+
+static int g_totalBuffers = 0;
+static int g_usedBuffers = -1;
+
+static int count_packets(struct atl_packet *packet)
+{
+ int count=0;
+ while (packet) {
+ count++;
+ packet = packet->next;
+ }
+ return count;
+}
+
+static struct atl_packet* alloc_page(device_t* dev, struct atl_dma_alloc *a_page)
+{
+ int err = atl_dma_malloc_page(dev, a_page);
+ if (err) {
+ AVB_LOGF_ERROR("atl_dma_malloc_page failed: %s", strerror(err));
+ return NULL;
+ }
+
+ struct atl_packet *free_packets;
+ struct atl_packet a_packet;
+
+ a_packet.attime = a_packet.flags = 0;
+ a_packet.map.paddr = a_page->dma_paddr;
+ a_packet.map.mmap_size = a_page->mmap_size;
+ a_packet.offset = 0;
+ a_packet.vaddr = a_page->dma_vaddr + a_packet.offset;
+ a_packet.len = ATL_MTU;
+ a_packet.next = NULL;
+
+ free_packets = NULL;
+
+ /* divide the dma page into buffers for packets */
+ int i;
+ for (i = 0; i < a_page->mmap_size / ATL_MTU; i++) {
+ struct atl_packet *tmp_packet = malloc(sizeof(struct atl_packet));
+ if (!tmp_packet) {
+ AVB_LOG_ERROR("failed to allocate atl_packet memory!");
+ return false;
+ }
+ *tmp_packet = a_packet;
+ tmp_packet->offset = (i * ATL_MTU);
+ tmp_packet->vaddr += tmp_packet->offset;
+ tmp_packet->next = free_packets;
+ memset(tmp_packet->vaddr, 0, ATL_MTU);
+ free_packets = tmp_packet;
+ }
+ return free_packets;
+}
+
+device_t *atlAcquireDevice(const char *ifname)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+
+ LOCK();
+ if (!atl_dev) {
+ device_t *tmp_dev = calloc(1, sizeof(device_t));
+ const char *complex_ifname = NULL;
+ if (!tmp_dev) {
+ AVB_LOGF_ERROR("Cannot allocate memory for device: %s", strerror(errno));
+ goto unlock;
+ }
+
+ complex_ifname = strchr(ifname, 0x3a);
+ if( complex_ifname != NULL && strlen(complex_ifname) > 1 ) {
+ tmp_dev->ifname = strdup(&complex_ifname[1]);
+ } else {
+ tmp_dev->ifname = strdup(ifname);
+ }
+
+#ifdef AVB_LOG_ON
+ atl_init_avb_log(&__avbLogFn, "AQ_AVTP");
+#endif // AVB_LOG_ON
+
+ int err = pci_connect(tmp_dev);
+ if (err) {
+ AVB_LOGF_ERROR("connect failed (%s) - are you running as root?", strerror(err));
+ goto unlock;
+ }
+
+ err = atl_init(tmp_dev);
+ if (err) {
+ AVB_LOGF_ERROR("init failed (%s) - is the driver really loaded?", strerror(err));
+ atl_detach(tmp_dev);
+ goto unlock;
+ }
+
+
+ int i;
+ for (i = 0; i < ATL_PAGES; i++) {
+ struct atl_packet* free_packets = alloc_page(tmp_dev, &g_pages[i]);
+ if (!g_free_packets) {
+ g_free_packets = free_packets;
+ } else {
+ struct atl_packet* last_packet = g_free_packets;
+ while (last_packet->next) {
+ last_packet = last_packet->next;
+ }
+ last_packet->next = free_packets;
+ }
+ }
+
+ g_totalBuffers = count_packets(g_free_packets);
+
+ AVB_LOGF_INFO("TX buffers: %d", g_totalBuffers);
+
+ AVB_LOGF_INFO("ATL launch time feature is %s", ATL_LAUNCHTIME_ENABLED ? "ENABLED" : "DISABLED");
+
+ atl_dev = tmp_dev;
+unlock:
+ if (!atl_dev)
+ free(tmp_dev);
+ }
+
+ if (atl_dev) {
+ atl_dev_users += 1;
+ AVB_LOGF_DEBUG("atl_dev_users %d", atl_dev_users);
+ }
+ UNLOCK();
+
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return atl_dev;
+}
+
+void atlReleaseDevice(device_t* dev)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+
+ struct atl_packet *cleaned_packets;
+ struct atl_packet *tmp_packet;
+
+ LOCK();
+
+ atl_dev_users -= 1;
+ AVB_LOGF_DEBUG("atl_dev_users %d", atl_dev_users);
+
+ if (atl_dev && atl_dev_users <= 0) {
+ int i;
+
+ atl_stop_tx(dev, 0, &cleaned_packets);
+
+ tmp_packet = cleaned_packets;
+
+ while( tmp_packet ) {
+ cleaned_packets = tmp_packet->next;
+ if( tmp_packet->extra ) {
+ free(tmp_packet->extra);
+ }
+ free(tmp_packet);
+ tmp_packet = cleaned_packets;
+ }
+
+ tmp_packet = g_free_packets;
+ while( tmp_packet ) {
+ g_free_packets = tmp_packet->next;
+ if( tmp_packet->extra ) {
+ free(tmp_packet->extra);
+ }
+ free(tmp_packet);
+ tmp_packet = g_free_packets;
+ }
+
+ for (i = 0; i < ATL_PAGES; i++)
+ atl_dma_free_page(atl_dev, &g_pages[i]);
+
+ atl_detach(atl_dev);
+ free(atl_dev);
+ atl_dev = NULL;
+ g_free_packets = NULL;
+ }
+
+ UNLOCK();
+
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+}
+
+struct atl_packet *atlGetTxPacket(device_t* dev)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+
+ LOCK();
+
+ struct atl_packet* tx_packet = g_free_packets;
+ if (!tx_packet && atl_dev) {
+ struct atl_packet *cleaned_packets;
+ atl_clean(dev, &cleaned_packets);
+ while (cleaned_packets) {
+ struct atl_packet *tmp_packet = cleaned_packets;
+ cleaned_packets = cleaned_packets->next;
+ tmp_packet->next = g_free_packets;
+ g_free_packets = tmp_packet;
+ }
+ tx_packet = g_free_packets;
+
+ g_usedBuffers = g_totalBuffers - count_packets(g_free_packets);
+ }
+
+ if (tx_packet) {
+ g_free_packets = tx_packet->next;
+ tx_packet->next = NULL;
+ }
+ UNLOCK();
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return tx_packet;
+}
+
+void atlRelTxPacket(device_t* dev, int queue, struct atl_packet *tx_packet)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+
+ LOCK();
+ tx_packet->next = g_free_packets;
+ g_free_packets = tx_packet;
+
+ UNLOCK();
+
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+}
+
+int atlTxBufLevel(device_t *dev)
+{
+ AVB_TRACE_ENTRY(AVB_TRACE_HAL_ETHER);
+ AVB_TRACE_EXIT(AVB_TRACE_HAL_ETHER);
+ return g_usedBuffers;
+}
diff --git a/lib/avtp_pipeline/platform/x86_aqc/openavb_atl.h b/lib/avtp_pipeline/platform/x86_aqc/openavb_atl.h
new file mode 100644
index 00000000..b6cd95c8
--- /dev/null
+++ b/lib/avtp_pipeline/platform/x86_aqc/openavb_atl.h
@@ -0,0 +1,54 @@
+/*************************************************************************************************************
+Copyright (c) 2019, Aquantia Corporation
+All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+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_ATL_H
+#define OPENAVB_ATL_H 1
+
+#include "openavb_platform.h"
+#include "openavb_types_base.h"
+
+#include "atl.h"
+
+#define ATL_MTU 1522
+
+// how many pages to alloc for tx buffers (2 frames fit in one page)
+#define ATL_PAGES 2
+
+device_t *atlAcquireDevice(const char *ifname);
+
+void atlReleaseDevice(device_t *atl_dev);
+
+struct atl_packet *atlGetTxPacket(device_t* dev);
+
+void atlRelTxPacket(device_t* dev, int queue, struct atl_packet *tx_packet);
+
+int atlTxBufLevel(device_t *dev);
+
+#endif // OPENAVB_ATL_H
diff --git a/lib/avtp_pipeline/platform/x86_aqc/openavb_hal.h b/lib/avtp_pipeline/platform/x86_aqc/openavb_hal.h
new file mode 100644
index 00000000..10114eb4
--- /dev/null
+++ b/lib/avtp_pipeline/platform/x86_aqc/openavb_hal.h
@@ -0,0 +1,39 @@
+/*************************************************************************************************************
+Copyright (c) 2012-2015, Symphony Teleca Corporation, a Harman International Industries, Incorporated company
+Copyright (c) 2016-2017, Harman International Industries, Incorporated
+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/qmgr/openavb_qmgr.c b/lib/avtp_pipeline/qmgr/openavb_qmgr.c
index 5c054eb2..bc25ffa9 100644
--- a/lib/avtp_pipeline/qmgr/openavb_qmgr.c
+++ b/lib/avtp_pipeline/qmgr/openavb_qmgr.c
@@ -46,6 +46,9 @@ https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175.
#if (AVB_FEATURE_IGB)
#include "openavb_igb.h"
#endif
+#if (AVB_FEATURE_ATL)
+#include "openavb_atl.h"
+#endif
#define AVB_DEFAULT_QDISC_MODE AVB_SHAPER_HWQ_PER_CLASS
@@ -56,6 +59,9 @@ typedef struct {
#if (AVB_FEATURE_IGB)
device_t *igb_dev;
#endif
+#if (AVB_FEATURE_ATL)
+ device_t *atl_dev;
+#endif
int mode;
int ifindex;
char ifname[IFNAMSIZ + 10]; // Include space for the socket type prefix (e.g. "simple:eth0")
@@ -69,6 +75,9 @@ static qdisc_data_t qdisc_data = {
#if (AVB_FEATURE_IGB)
NULL,
#endif
+#if (AVB_FEATURE_ATL)
+ NULL,
+#endif
0, 0, {0}, 0, 0, 0, 0
};
@@ -103,13 +112,13 @@ static qmgrStream_t qmgr_streams[MAX_AVB_STREAMS];
static bool setupHWQueue(int nClass, unsigned classBytesPerSec)
{
int err = 0;
-#if (AVB_FEATURE_IGB)
+#if (AVB_FEATURE_IGB) || (AVB_FEATURE_ATL)
U32 class_a_bytes_per_sec, class_b_bytes_per_sec;
#endif
AVB_TRACE_ENTRY(AVB_TRACE_QUEUE_MANAGER);
-#if (AVB_FEATURE_IGB)
+#if (AVB_FEATURE_IGB) || (AVB_FEATURE_ATL)
if (nClass == SR_CLASS_A) {
class_a_bytes_per_sec = classBytesPerSec;
class_b_bytes_per_sec = qmgr_classes[SR_CLASS_B].classBytesPerSec;
@@ -117,9 +126,17 @@ static bool setupHWQueue(int nClass, unsigned classBytesPerSec)
class_a_bytes_per_sec = qmgr_classes[SR_CLASS_A].classBytesPerSec;
class_b_bytes_per_sec = classBytesPerSec;
}
+#if (AVB_FEATURE_IGB)
err = igb_set_class_bandwidth2(qdisc_data.igb_dev, class_a_bytes_per_sec, class_b_bytes_per_sec);
if (err)
AVB_LOGF_ERROR("Adding stream; igb_set_class_bandwidth failed: %s", strerror(err));
+#else // (AVB_FEATURE_ATL)
+ err = atl_set_class_bandwidth(qdisc_data.atl_dev,
+ class_a_bytes_per_sec,
+ class_b_bytes_per_sec);
+ if (err)
+ AVB_LOGF_ERROR("Adding stream; atl_set_class_bandwidth failed: %s", strerror(err));
+#endif
#endif
AVB_TRACE_EXIT(AVB_TRACE_QUEUE_MANAGER);
@@ -268,6 +285,14 @@ bool openavbQmgrInitialize(int mode, int ifindex, const char* ifname, unsigned m
}
else
#endif
+#if (AVB_FEATURE_ATL)
+ if ( qdisc_data.mode != AVB_SHAPER_DISABLED
+ && (qdisc_data.atl_dev = atlAcquireDevice(ifname)) == 0)
+ {
+ AVB_LOG_ERROR("Initializing QMgr; unable to acquire atl device");
+ }
+ else
+#endif
{
// Initialize data for classes and streams
memset(qmgr_classes, 0, sizeof(qmgr_classes));
@@ -315,6 +340,10 @@ void openavbQmgrFinalize(void)
igbReleaseDevice(qdisc_data.igb_dev);
qdisc_data.igb_dev = NULL;
#endif
+#if (AVB_FEATURE_ATL)
+ atlReleaseDevice(qdisc_data.atl_dev);
+ qdisc_data.atl_dev = NULL;
+#endif
}
UNLOCK();
diff --git a/lib/avtp_pipeline/rawsock/CMakeLists.txt b/lib/avtp_pipeline/rawsock/CMakeLists.txt
index e3e0f49e..7fac0725 100644
--- a/lib/avtp_pipeline/rawsock/CMakeLists.txt
+++ b/lib/avtp_pipeline/rawsock/CMakeLists.txt
@@ -10,6 +10,13 @@ if (AVB_FEATURE_PCAP)
${AVB_HAL_DIR}/openavb_igb.c
)
endif ()
+ if (AVB_FEATURE_ATL)
+ message("-- Rawsock ATL enabled")
+ SET (ATL_FILES
+ ${AVB_OSAL_DIR}/rawsock/atl_rawsock.c
+ ${AVB_HAL_DIR}/openavb_atl.c
+ )
+ endif ()
endif ()
SET (SRC_FILES ${SRC_FILES}
${AVB_SRC_DIR}/rawsock/rawsock_impl.c
@@ -19,5 +26,6 @@ SET (SRC_FILES ${SRC_FILES}
${AVB_OSAL_DIR}/rawsock/sendmmsg_rawsock.c
${PCAP_FILES}
${IGB_FILES}
+ ${ATL_FILES}
PARENT_SCOPE
)
diff --git a/lib/avtp_pipeline/tl/openavb_talker.c b/lib/avtp_pipeline/tl/openavb_talker.c
index 24fdacdd..6794e167 100644
--- a/lib/avtp_pipeline/tl/openavb_talker.c
+++ b/lib/avtp_pipeline/tl/openavb_talker.c
@@ -238,7 +238,7 @@ static inline bool talkerDoStream(tl_state_t *pTLState)
// sleep until the next interval
SLEEP_UNTIL_NSEC(pTalkerData->nextCycleNS);
} else {
-#if !IGB_LAUNCHTIME_ENABLED
+#if !IGB_LAUNCHTIME_ENABLED && !ATL_LAUNCHTIME_ENABLED
SPIN_UNTIL_NSEC(pTalkerData->nextCycleNS);
#endif
}
diff --git a/lib/avtp_pipeline/util/openavb_log.c b/lib/avtp_pipeline/util/openavb_log.c
index 5d32843f..46d827cf 100644
--- a/lib/avtp_pipeline/util/openavb_log.c
+++ b/lib/avtp_pipeline/util/openavb_log.c
@@ -243,7 +243,7 @@ extern void DLL_EXPORT avbLogExit()
logOutputFd = NULL;
}
-extern void DLL_EXPORT avbLogFn(
+void __avbLogFn(
int level,
const char *tag,
const char *company,
@@ -251,12 +251,9 @@ extern void DLL_EXPORT avbLogFn(
const char *path,
int line,
const char *fmt,
- ...)
+ va_list args)
{
if (level <= AVB_LOG_LEVEL) {
- va_list args;
- va_start(args, fmt);
-
LOG_LOCK();
vsprintf(msg, fmt, args);
@@ -313,12 +310,26 @@ extern void DLL_EXPORT avbLogFn(
}
}
- va_end(args);
-
LOG_UNLOCK();
}
}
+extern void DLL_EXPORT avbLogFn(
+ int level,
+ const char *tag,
+ const char *company,
+ const char *component,
+ const char *path,
+ int line,
+ const char *fmt,
+ ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ __avbLogFn(level, tag, company, component, path, line, fmt, args);
+ va_end(args);
+}
+
extern void DLL_EXPORT avbLogRT(int level, bool bBegin, bool bItem, bool bEnd, char *pFormat, log_rt_datatype_t dataType, void *pVar)
{
if (logRTQueue) {
diff --git a/lib/common/Makefile b/lib/common/Makefile
index 7685056a..085f77e0 100644
--- a/lib/common/Makefile
+++ b/lib/common/Makefile
@@ -1,15 +1,21 @@
AVB_FEATURE_IGB ?= 1
+AVB_FEATURE_ATL ?= 1
ifeq ($(AVB_FEATURE_IGB),1)
AVB_IGB_OBJ = avb_igb.o
EXTRA_CFLAGS = -DAVB_FEATURE_IGB=1
endif
+ifeq ($(AVB_FEATURE_ATL),1)
+AVB_ATL_OBJ = avb_atl.o
+EXTRA_CFLAGS = -DAVB_FEATURE_ATL=1
+endif
+
CC ?= gcc
OPT = -O2 -g
WARN = -Wall -Wextra -Wno-parentheses
CFLAGS = $(OPT) $(WARN) $(EXTRA_CFLAGS)
-ALL_OBJS = avb_avtp.o avb_gptp.o $(AVB_IGB_OBJ)
+ALL_OBJS = avb_avtp.o avb_gptp.o $(AVB_IGB_OBJ) $(AVB_ATL_OBJ)
.PHONY: all clean
@@ -17,6 +23,8 @@ all: $(ALL_OBJS)
avb_igb.o: CPPFLAGS = -I../igb_avb/lib
avb_igb.o: avb_igb.c avb_igb.h
+avb_atl.o: CPPFLAGS = -I../atl_avb/lib
+avb_atl.o: avb_atl.c avb_atl.h
avb_avtp.o: avb_avtp.c avb_avtp.h
avb_gptp.o: avb_gptp.c avb_gptp.h
diff --git a/lib/common/avb.h b/lib/common/avb.h
index 0cf2d4e7..b70bf26a 100644
--- a/lib/common/avb.h
+++ b/lib/common/avb.h
@@ -27,5 +27,10 @@
#define AVB_FEATURE_IGB 1
#include "avb_igb.h"
#endif
+#ifndef AVB_FEATURE_ATL
+/* IGB has not been disabled, so assume it is enabled. */
+#define AVB_FEATURE_ATL 1
+#include "avb_atl.h"
+#endif
#endif /* __AVB_H__ */
diff --git a/lib/common/avb_atl.c b/lib/common/avb_atl.c
new file mode 100644
index 00000000..8257250e
--- /dev/null
+++ b/lib/common/avb_atl.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019, Aquantia 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 "avb_atl.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+/**
+ * @brief Connect to the network card
+ * @param atl_dev [inout] Device handle
+ * @return 0 for success, ENXIO for failure
+ */
+
+int pci_connect(device_t *atl_dev)
+{
+ int err;
+ err = atl_probe(atl_dev);
+ if (err) {
+ logprint(LOG_LVL_ERROR, "atl_probe failed! (%s)", strerror(err));
+ return ENXIO;
+ }
+ logprint(LOG_LVL_VERBOSE, "attaching to %s", atl_dev->ifname);
+ err = atl_attach(atl_dev);
+ if (err) {
+ logprint(LOG_LVL_ERROR, "attach failed! (%s)", strerror(err));
+ }
+ else {
+ logprint(LOG_LVL_VERBOSE, "attaching to %s tx", atl_dev->ifname);
+ err = atl_attach_tx(atl_dev);
+ }
+ if (err) {
+ logprint(LOG_LVL_ERROR, "attach_tx failed! (%s)", strerror(err));
+ atl_detach(atl_dev);
+ }
+ else {
+ logprint(LOG_LVL_VERBOSE, "attaching to %s rx", atl_dev->ifname);
+ err = atl_attach_rx(atl_dev);
+ }
+ if (err) {
+ logprint(LOG_LVL_ERROR, "attach_rx failed! (%s)", strerror(err));
+ atl_detach(atl_dev);
+ }
+ logprint(LOG_LVL_INFO, "Attaching to %s complete", atl_dev->ifname);
+ return 0;
+}
diff --git a/lib/common/avb_atl.h b/lib/common/avb_atl.h
new file mode 100644
index 00000000..050662e4
--- /dev/null
+++ b/lib/common/avb_atl.h
@@ -0,0 +1,7 @@
+#ifndef __AVB_ATL_H__
+#define __AVB_ATL_H__
+
+#include <atl.h>
+int pci_connect(device_t * atl_dev);
+
+#endif
diff --git a/run_atl.sh b/run_atl.sh
new file mode 100755
index 00000000..7331f0b2
--- /dev/null
+++ b/run_atl.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Simple script to run atl_avb
+
+if [ "$#" -eq "0" ]; then
+ echo "please enter network interface name as parameter. For example:"
+ echo "sudo ./run_atl.sh eth1"
+ exit -1
+fi
+
+rmmod atlantic
+rmmod aqdiag
+rmmod atl_tsn
+modprobe crc_itu_t
+modprobe ptp
+insmod lib/atl_avb/kmod/atl_tsn.ko
+
+ethtool -i $1