summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-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/osx/avb_viewer/AVB Viewer.xcodeproj/project.pbxproj34
-rw-r--r--examples/osx/avb_viewer/AVB Viewer/AVB Viewer-Info.plist16
-rw-r--r--examples/osx/avb_viewer/AVB Viewer/OutlineViewController.m12
-rw-r--r--examples/osx/avb_viewer/AVB ViewerTests/AVB ViewerTests-Info.plist2
-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
16 files changed, 2617 insertions, 21 deletions
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/osx/avb_viewer/AVB Viewer.xcodeproj/project.pbxproj b/examples/osx/avb_viewer/AVB Viewer.xcodeproj/project.pbxproj
index d3cf8070..8692488a 100644
--- a/examples/osx/avb_viewer/AVB Viewer.xcodeproj/project.pbxproj
+++ b/examples/osx/avb_viewer/AVB Viewer.xcodeproj/project.pbxproj
@@ -22,7 +22,6 @@
674AB0081820126E00234149 /* AvbDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 674AB0071820126E00234149 /* AvbDevice.m */; };
674AB00A1820162600234149 /* AudioVideoBridging.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 674AB0091820162600234149 /* AudioVideoBridging.framework */; };
674AB00D1820436500234149 /* Brick-Icons.icns in Resources */ = {isa = PBXBuildFile; fileRef = 674AB00C1820436500234149 /* Brick-Icons.icns */; };
- 674AB00F1820824300234149 /* Brick-Icons.icns in Resources */ = {isa = PBXBuildFile; fileRef = 674AB00E1820824300234149 /* Brick-Icons.icns */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -220,7 +219,7 @@
674AAFC4182011BB00234149 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0500;
+ LastUpgradeCheck = 1010;
ORGANIZATIONNAME = AVB.io;
TargetAttributes = {
674AAFCB182011BB00234149 = {
@@ -351,18 +350,32 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@@ -388,19 +401,32 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -421,6 +447,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "AVB Viewer/AVB Viewer-Prefix.pch";
INFOPLIST_FILE = "AVB Viewer/AVB Viewer-Info.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = "avbio.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -435,6 +462,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "AVB Viewer/AVB Viewer-Prefix.pch";
INFOPLIST_FILE = "AVB Viewer/AVB Viewer-Info.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = "avbio.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -456,6 +484,7 @@
"$(inherited)",
);
INFOPLIST_FILE = "AVB ViewerTests/AVB ViewerTests-Info.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = "avbio.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
@@ -474,6 +503,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "AVB Viewer/AVB Viewer-Prefix.pch";
INFOPLIST_FILE = "AVB ViewerTests/AVB ViewerTests-Info.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = "avbio.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
diff --git a/examples/osx/avb_viewer/AVB Viewer/AVB Viewer-Info.plist b/examples/osx/avb_viewer/AVB Viewer/AVB Viewer-Info.plist
index c37d80d6..ceb1006e 100644
--- a/examples/osx/avb_viewer/AVB Viewer/AVB Viewer-Info.plist
+++ b/examples/osx/avb_viewer/AVB Viewer/AVB Viewer-Info.plist
@@ -2,13 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
- <key>resourceUsage</key>
- <dict>
- <key>iokit.user-client</key>
- <array>
- <string>IOAVB17221EntityDiscoveryUserClient</string>
- </array>
- </dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
@@ -16,7 +9,7 @@
<key>CFBundleIconFile</key>
<string>Brick-Icons</string>
<key>CFBundleIdentifier</key>
- <string>avbio.${PRODUCT_NAME:rfc1034identifier}</string>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
@@ -39,5 +32,12 @@
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
+ <key>resourceUsage</key>
+ <dict>
+ <key>iokit.user-client</key>
+ <array>
+ <string>IOAVB17221EntityDiscoveryUserClient</string>
+ </array>
+ </dict>
</dict>
</plist>
diff --git a/examples/osx/avb_viewer/AVB Viewer/OutlineViewController.m b/examples/osx/avb_viewer/AVB Viewer/OutlineViewController.m
index 6dd0d227..ab1b26a1 100644
--- a/examples/osx/avb_viewer/AVB Viewer/OutlineViewController.m
+++ b/examples/osx/avb_viewer/AVB Viewer/OutlineViewController.m
@@ -10,10 +10,12 @@
#define kIconImageSize 16.0
-@implementation OutlineViewController
+static NSString *avbDevicesChanged = @"AVBCHANGED";
-NSString *avbDevicesChanged = @"AVBCHANGED";
-NSString *interfaceName;
+@implementation OutlineViewController
+{
+ NSString *interfaceName;
+}
- (id)init {
self = [super init];
@@ -25,7 +27,7 @@ NSString *interfaceName;
//[newDevice addChild:[[AvbDevice alloc] initWithId: @"Test Talker"]];
//[self.avbDevices addObject:newDevice];
- [self getLocalInterface];
+ interfaceName = [self getLocalInterface];
//[self createLocalEntity]; Not used yet
[self resetDiscovery];
}
@@ -105,7 +107,7 @@ NSString *interfaceName;
[entity setTalkerStreamSources:1];
[entity setEntityID:666666];
[entity setLocalEntity:YES];
- AVB17221ACMPInterface *acmp = [[AVB17221ACMPInterface alloc] init];
+ AVB17221ACMPInterface *acmp = [AVB17221ACMPInterface new];
return acmp;
}
diff --git a/examples/osx/avb_viewer/AVB ViewerTests/AVB ViewerTests-Info.plist b/examples/osx/avb_viewer/AVB ViewerTests/AVB ViewerTests-Info.plist
index cad13dc2..169b6f71 100644
--- a/examples/osx/avb_viewer/AVB ViewerTests/AVB ViewerTests-Info.plist
+++ b/examples/osx/avb_viewer/AVB ViewerTests/AVB ViewerTests-Info.plist
@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
- <string>avbio.${PRODUCT_NAME:rfc1034identifier}</string>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
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)