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