summaryrefslogtreecommitdiff
path: root/examples/atl_simple_talker
diff options
context:
space:
mode:
Diffstat (limited to 'examples/atl_simple_talker')
-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
3 files changed, 896 insertions, 0 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;
+}