diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/atl_simple_talker/Makefile | 39 | ||||
-rw-r--r-- | examples/atl_simple_talker/README | 29 | ||||
-rw-r--r-- | examples/atl_simple_talker/simple_talker.c | 828 | ||||
-rw-r--r-- | examples/common/Makefile | 6 | ||||
-rw-r--r-- | examples/common/async_pcap_storing.c | 314 | ||||
-rw-r--r-- | examples/common/async_pcap_storing.h | 13 | ||||
-rw-r--r-- | examples/jackd-talker/Makefile | 2 | ||||
-rw-r--r-- | examples/live_stream/Makefile | 2 | ||||
-rw-r--r-- | examples/osx/avb_viewer/AVB Viewer.xcodeproj/project.pbxproj | 34 | ||||
-rw-r--r-- | examples/osx/avb_viewer/AVB Viewer/AVB Viewer-Info.plist | 16 | ||||
-rw-r--r-- | examples/osx/avb_viewer/AVB Viewer/OutlineViewController.m | 12 | ||||
-rw-r--r-- | examples/osx/avb_viewer/AVB ViewerTests/AVB ViewerTests-Info.plist | 2 | ||||
-rw-r--r-- | examples/send_packet_precisely/Makefile | 36 | ||||
-rw-r--r-- | examples/send_packet_precisely/README | 25 | ||||
-rw-r--r-- | examples/send_packet_precisely/send_packet_precisely.c | 1278 | ||||
-rw-r--r-- | examples/simple_talker/Makefile | 2 |
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) |