moving targets/ARCH to sdr/
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
ETHERNET_OBJ += $(OPENAIR_DIR)/sdr/ETHERNET/USERSPACE/LIB/ethernet_lib.o
|
||||
ETHERNET_FILE_OBJ += $(OPENAIR_DIR)/sdr/ETHERNET/USERSPACE/LIB/ethernet_lib.c
|
||||
ETHERNET_CFLAGS += -O2 -I$(OPENAIR_DIR)/sdr/COMMON -I$(OPENAIR_DIR)/sdr/ETHERNET/USERSPACE/LIB/ -I$(OPENAIR_TARGETS)/COMMON
|
||||
|
||||
@@ -0,0 +1,540 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file ethernet_lib.c
|
||||
* \brief API to stream I/Q samples over standard ethernet
|
||||
* \author add alcatel Katerina Trilyraki, Navid Nikaein, Pedro Dinis, Lucio Ferreira, Raymond Knopp, Tien-Thinh Nguyen
|
||||
* \date 2015
|
||||
* \version 0.2
|
||||
* \company Eurecom
|
||||
* \maintainer: navid.nikaein@eurecom.fr
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/ether.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "common_lib.h"
|
||||
#include "ethernet_lib.h"
|
||||
|
||||
//#define DEBUG 0
|
||||
|
||||
//struct sockaddr_ll dest_addr[MAX_INST];
|
||||
//struct sockaddr_ll local_addr[MAX_INST];
|
||||
//int addr_len[MAX_INST];
|
||||
//struct ifreq if_index[MAX_INST];
|
||||
|
||||
int eth_socket_init_raw(openair0_device *device) {
|
||||
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
const char *local_mac, *remote_mac;
|
||||
int sock_dom=0;
|
||||
int sock_type=0;
|
||||
int sock_proto=0;
|
||||
|
||||
if (device->host_type == RRU_HOST ) { /* RRU doesn't know remote MAC(will be retrieved from first packet send from RAU) and remote port(don't care) */
|
||||
local_mac = device->eth_params->my_addr;
|
||||
remote_mac = malloc(ETH_ALEN);
|
||||
memset((void*)remote_mac,0,ETH_ALEN);
|
||||
printf("[%s] local MAC addr (user) %s remote MAC addr (user) %s\n","RRU", local_mac,remote_mac);
|
||||
} else {
|
||||
local_mac = device->eth_params->my_addr;
|
||||
remote_mac = device->eth_params->remote_addr;
|
||||
printf("[%s] local MAC addr (user) %s remote MAC addr (user) %s\n","RAU", local_mac,remote_mac);
|
||||
}
|
||||
|
||||
|
||||
/* Open a RAW socket to send on */
|
||||
sock_dom=AF_PACKET;
|
||||
sock_type=SOCK_RAW;
|
||||
sock_proto=IPPROTO_RAW;
|
||||
if ((eth->sockfdc = socket(sock_dom, sock_type, sock_proto)) == -1) {
|
||||
perror("ETHERNET: Error opening RAW socket (control)");
|
||||
exit(0);
|
||||
}
|
||||
for (int i=0;i<eth->num_fd;i++)
|
||||
if ((eth->sockfdd[i] = socket(sock_dom, sock_type, sock_proto)) == -1) {
|
||||
perror("ETHERNET: Error opening RAW socket (user)");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* initialize destination address */
|
||||
bzero((void *)&(eth->local_addrc_ll), sizeof(struct sockaddr_ll));
|
||||
bzero((void *)&(eth->local_addrd_ll), sizeof(struct sockaddr_ll));
|
||||
bzero((void *)&(eth->if_index), sizeof(struct ifreq));
|
||||
|
||||
/* Get the index of the interface to send on */
|
||||
strcpy(eth->if_index.ifr_name,eth->if_name);
|
||||
if (ioctl(eth->sockfdc, SIOCGIFINDEX, &(eth->if_index)) < 0)
|
||||
perror("SIOCGIFINDEX");
|
||||
for (int i=0;i<eth->num_fd;i++)
|
||||
if (ioctl(eth->sockfdd[i], SIOCGIFINDEX, &(eth->if_index)) < 0)
|
||||
perror("SIOCGIFINDEX");
|
||||
|
||||
eth->local_addrc_ll.sll_family = AF_PACKET;
|
||||
eth->local_addrc_ll.sll_ifindex = eth->if_index.ifr_ifindex;
|
||||
eth->local_addrd_ll.sll_family = AF_PACKET;
|
||||
eth->local_addrd_ll.sll_ifindex = eth->if_index.ifr_ifindex;
|
||||
/* hear traffic from specific protocol*/
|
||||
eth->local_addrc_ll.sll_protocol = htons((short)device->eth_params->my_portc);
|
||||
eth->local_addrd_ll.sll_protocol = htons((short)device->eth_params->my_portd);
|
||||
|
||||
eth->local_addrc_ll.sll_halen = ETH_ALEN;
|
||||
eth->local_addrc_ll.sll_pkttype = PACKET_OTHERHOST;
|
||||
eth->local_addrd_ll.sll_halen = ETH_ALEN;
|
||||
eth->local_addrd_ll.sll_pkttype = PACKET_OTHERHOST;
|
||||
eth->addr_len = sizeof(struct sockaddr_ll);
|
||||
|
||||
for (int i=0;i<eth->num_fd;i++)
|
||||
if (bind(eth->sockfdd[i],(struct sockaddr *)ð->local_addrd_ll,eth->addr_len)<0) {
|
||||
perror("ETHERNET: Cannot bind to socket (user)");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* Construct the Ethernet header */
|
||||
ether_aton_r(local_mac, (struct ether_addr *)(&(eth->ehd.ether_shost)));
|
||||
ether_aton_r(remote_mac, (struct ether_addr *)(&(eth->ehd.ether_dhost)));
|
||||
eth->ehc.ether_type = htons((short)device->eth_params->my_portc);
|
||||
eth->ehd.ether_type = htons((short)device->eth_params->my_portd);
|
||||
|
||||
printf("[%s] binding to hardware address %x:%x:%x:%x:%x:%x\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"),eth->ehd.ether_shost[0],eth->ehd.ether_shost[1],eth->ehd.ether_shost[2],eth->ehd.ether_shost[3],eth->ehd.ether_shost[4],eth->ehd.ether_shost[5]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 09/03/2019: fix obvious inconsistencies, but this code hasn't be tested for sure */
|
||||
int trx_eth_write_raw(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int cc, int flags) {
|
||||
|
||||
int bytes_sent=0;
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
int sendto_flag =0;
|
||||
int i=0;
|
||||
//sendto_flag|=flags;
|
||||
|
||||
eth->tx_nsamps=nsamps;
|
||||
int pktsize;
|
||||
if (eth->compression == ALAW_COMPRESS) {
|
||||
pktsize = RAW_PACKET_SIZE_BYTES_ALAW(nsamps);
|
||||
} else {
|
||||
pktsize = RAW_PACKET_SIZE_BYTES(nsamps);
|
||||
}
|
||||
for (i=0;i<cc;i++) {
|
||||
/* buff[i] points to the position in tx buffer where the payload to be sent is
|
||||
buff2 points to the position in tx buffer where the packet header will be placed */
|
||||
void *buff2 = (void*)(buff[i]-APP_HEADER_SIZE_BYTES-MAC_HEADER_SIZE_BYTES);
|
||||
|
||||
/* we don't want to ovewrite with the header info the previous tx buffer data so we store it*/
|
||||
struct ether_header temp = *(struct ether_header *)buff2;
|
||||
int32_t temp0 = *(int32_t *)(buff2 + MAC_HEADER_SIZE_BYTES);
|
||||
openair0_timestamp temp1 = *(openair0_timestamp *)(buff2 + MAC_HEADER_SIZE_BYTES + sizeof(int32_t));
|
||||
|
||||
bytes_sent = 0;
|
||||
memcpy(buff2,(void*)ð->ehd,MAC_HEADER_SIZE_BYTES);
|
||||
*(int16_t *)(buff2 + MAC_HEADER_SIZE_BYTES + sizeof(int16_t))=1+(i<<1);
|
||||
*(openair0_timestamp *)(buff2 + MAC_HEADER_SIZE_BYTES + sizeof(int32_t)) = timestamp;
|
||||
|
||||
/*printf("[RRU]write mod_%d %d , len %d, buff %p \n",
|
||||
Mod_id,eth->sockfd[Mod_id],RAW_PACKET_SIZE_BYTES(nsamps), buff2);*/
|
||||
|
||||
while(bytes_sent < pktsize) {
|
||||
|
||||
/* Send packet */
|
||||
|
||||
bytes_sent += send(eth->sockfdd[cc % eth->num_fd],
|
||||
buff2,
|
||||
pktsize,
|
||||
sendto_flag);
|
||||
if ( bytes_sent == -1) {
|
||||
eth->num_tx_errors++;
|
||||
perror("ETHERNET WRITE: ");
|
||||
exit(-1);
|
||||
} else {
|
||||
|
||||
eth->tx_actual_nsamps=bytes_sent>>2;
|
||||
eth->tx_count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* tx buffer values restored */
|
||||
*(struct ether_header *)buff2 = temp;
|
||||
*(int32_t *)(buff2 + MAC_HEADER_SIZE_BYTES) = temp0;
|
||||
*(openair0_timestamp *)(buff2 + MAC_HEADER_SIZE_BYTES + sizeof(int32_t)) = temp1;
|
||||
}
|
||||
return (bytes_sent-APP_HEADER_SIZE_BYTES-MAC_HEADER_SIZE_BYTES)>>2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int trx_eth_write_raw_IF4p5(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) {
|
||||
|
||||
int nblocks = nsamps;
|
||||
int bytes_sent = 0;
|
||||
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
|
||||
ssize_t packet_size;
|
||||
|
||||
if (flags == IF4p5_PDLFFT) {
|
||||
packet_size = RAW_IF4p5_PDLFFT_SIZE_BYTES(nblocks);
|
||||
} else if (flags == IF4p5_PULFFT) {
|
||||
packet_size = RAW_IF4p5_PULFFT_SIZE_BYTES(nblocks);
|
||||
} else if (flags == IF4p5_PULTICK) {
|
||||
packet_size = RAW_IF4p5_PULTICK_SIZE_BYTES;
|
||||
} else {
|
||||
packet_size = RAW_IF4p5_PRACH_SIZE_BYTES;
|
||||
}
|
||||
|
||||
eth->tx_nsamps = nblocks;
|
||||
|
||||
memcpy(buff[0], (void*)ð->ehd, MAC_HEADER_SIZE_BYTES);
|
||||
|
||||
|
||||
bytes_sent = send(eth->sockfdd[cc % eth->num_fd],
|
||||
buff[0],
|
||||
packet_size,
|
||||
0);
|
||||
|
||||
if (bytes_sent == -1) {
|
||||
eth->num_tx_errors++;
|
||||
perror("ETHERNET WRITE: ");
|
||||
exit(-1);
|
||||
} else {
|
||||
eth->tx_actual_nsamps = bytes_sent>>1;
|
||||
eth->tx_count++;
|
||||
}
|
||||
|
||||
return (bytes_sent-MAC_HEADER_SIZE_BYTES);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int trx_eth_read_raw(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) {
|
||||
|
||||
int ret;
|
||||
int bytes_received=0;
|
||||
int i=0;
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
int rcvfrom_flag =0;
|
||||
|
||||
int block_cnt=0;
|
||||
int again_cnt=0;
|
||||
|
||||
eth->rx_nsamps=nsamps;
|
||||
|
||||
for (i=0;i<cc;i++) {
|
||||
/* buff[i] points to the position in rx buffer where the payload to be received will be placed
|
||||
buff2 points to the position in rx buffer where the packet header will be placed */
|
||||
void *buff2 = (void*)(buff[i]-APP_HEADER_SIZE_BYTES-MAC_HEADER_SIZE_BYTES);
|
||||
|
||||
/* we don't want to ovewrite with the header info the previous rx buffer data so we store it*/
|
||||
struct ether_header temp = *(struct ether_header *)buff2;
|
||||
int32_t temp0 = *(int32_t *)(buff2 + MAC_HEADER_SIZE_BYTES);
|
||||
openair0_timestamp temp1 = *(openair0_timestamp *)(buff2 + MAC_HEADER_SIZE_BYTES + sizeof(int32_t));
|
||||
|
||||
bytes_received=0;
|
||||
int receive_bytes;
|
||||
if (eth->compression == ALAW_COMPRESS) {
|
||||
receive_bytes = RAW_PACKET_SIZE_BYTES_ALAW(nsamps);
|
||||
} else {
|
||||
receive_bytes = RAW_PACKET_SIZE_BYTES(nsamps);
|
||||
}
|
||||
|
||||
while(bytes_received < receive_bytes) {
|
||||
again:
|
||||
ret = recv(eth->sockfdd[cc % eth->num_fd],
|
||||
buff2,
|
||||
receive_bytes,
|
||||
rcvfrom_flag);
|
||||
|
||||
if (ret ==-1) {
|
||||
eth->num_rx_errors++;
|
||||
if (errno == EAGAIN) {
|
||||
again_cnt++;
|
||||
usleep(10);
|
||||
if (again_cnt == 1000) {
|
||||
perror("ETHERNET IF5 READ (EAGAIN): ");
|
||||
exit(-1);
|
||||
} else {
|
||||
printf("AGAIN AGAIN AGAIN AGAIN AGAIN AGAIN AGAIN AGAIN AGAIN AGAIN AGAIN AGAIN \n");
|
||||
goto again;
|
||||
}
|
||||
} else if (errno == EWOULDBLOCK) {
|
||||
block_cnt++;
|
||||
usleep(10);
|
||||
if (block_cnt == 1000) {
|
||||
perror("ETHERNET IF5 READ (EWOULDBLOCK): ");
|
||||
exit(-1);
|
||||
} else {
|
||||
printf("BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK \n");
|
||||
goto again;
|
||||
}
|
||||
} else {
|
||||
perror("ETHERNET IF5 READ");
|
||||
printf("(%s):\n", strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
} else {
|
||||
bytes_received+=ret;
|
||||
/* store the timestamp value from packet's header */
|
||||
*timestamp = *(openair0_timestamp *)(buff2 + MAC_HEADER_SIZE_BYTES + sizeof(int32_t));
|
||||
eth->rx_actual_nsamps=bytes_received>>2;
|
||||
eth->rx_count++;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
printf("------- RX------: nu=%x an_id=%d ts%d bytes_recv=%d \n",
|
||||
*(uint8_t *)(buff2+ETH_ALEN),
|
||||
*(int16_t *)(buff2 + MAC_HEADER_SIZE_BYTES + sizeof(int16_t)),
|
||||
*(openair0_timestamp *)(buff2 + MAC_HEADER_SIZE_BYTES + sizeof(int32_t)),
|
||||
bytes_received);
|
||||
|
||||
dump_packet((device->host_type == RAU_HOST)? "RAU":"RRU", buff2, RAW_PACKET_SIZE_BYTES(nsamps),RX_FLAG);
|
||||
|
||||
#endif
|
||||
|
||||
/* tx buffer values restored */
|
||||
*(struct ether_header *)buff2 = temp;
|
||||
*(int32_t *)(buff2 + MAC_HEADER_SIZE_BYTES) = temp0;
|
||||
*(openair0_timestamp *)(buff2 + MAC_HEADER_SIZE_BYTES + sizeof(int32_t)) = temp1;
|
||||
}
|
||||
return (bytes_received-APP_HEADER_SIZE_BYTES-MAC_HEADER_SIZE_BYTES)>>2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int trx_eth_read_raw_IF4p5(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) {
|
||||
|
||||
int ret;
|
||||
// Read nblocks info from packet itself
|
||||
int nblocks = nsamps;
|
||||
int bytes_received=0;
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
|
||||
ssize_t packet_size = MAC_HEADER_SIZE_BYTES + sizeof_IF4p5_header_t;
|
||||
IF4p5_header_t *test_header = (IF4p5_header_t*)(buff[0] + MAC_HEADER_SIZE_BYTES);
|
||||
|
||||
int block_cnt=0;
|
||||
int again_cnt=0;
|
||||
|
||||
while (bytes_received < packet_size) {
|
||||
again:
|
||||
ret = recv(eth->sockfdd[cc % eth->num_fd],
|
||||
buff[0],
|
||||
packet_size,
|
||||
MSG_PEEK);
|
||||
if (ret ==-1) {
|
||||
eth->num_rx_errors++;
|
||||
if (errno == EAGAIN) {
|
||||
again_cnt++;
|
||||
usleep(10);
|
||||
if (again_cnt == 1000) {
|
||||
perror("ETHERNET IF4p5 READ (EAGAIN): ");
|
||||
exit(-1);
|
||||
} else {
|
||||
printf("AGAIN AGAIN AGAIN AGAIN AGAIN AGAIN AGAIN AGAIN AGAIN AGAIN AGAIN AGAIN \n");
|
||||
goto again;
|
||||
}
|
||||
} else if (errno == EWOULDBLOCK) {
|
||||
block_cnt++;
|
||||
usleep(10);
|
||||
if (block_cnt == 1000) {
|
||||
perror("ETHERNET IF4p5 READ (EWOULDBLOCK): ");
|
||||
exit(-1);
|
||||
} else {
|
||||
printf("BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK \n");
|
||||
goto again;
|
||||
}
|
||||
} else {
|
||||
perror("ETHERNET IF4p5 READ");
|
||||
printf("(%s):\n", strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
else bytes_received+=ret;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
for (int i=0;i<packet_size;i++)
|
||||
printf("%2x.",((uint8_t*)buff[0])[i]);
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
*timestamp = test_header->sub_type;
|
||||
|
||||
if (test_header->sub_type == IF4p5_PDLFFT) {
|
||||
packet_size = RAW_IF4p5_PDLFFT_SIZE_BYTES(nblocks);
|
||||
} else if (test_header->sub_type == IF4p5_PULFFT) {
|
||||
packet_size = RAW_IF4p5_PULFFT_SIZE_BYTES(nblocks);
|
||||
} else {
|
||||
packet_size = RAW_IF4p5_PRACH_SIZE_BYTES;
|
||||
}
|
||||
|
||||
while(bytes_received < packet_size) {
|
||||
ret = recv(eth->sockfdd[cc % eth->num_fd],
|
||||
buff[0],
|
||||
packet_size,
|
||||
0);
|
||||
if (ret ==-1) {
|
||||
eth->num_rx_errors++;
|
||||
perror("ETHERNET IF4p5 READ (payload): ");
|
||||
return(-1);
|
||||
} else {
|
||||
bytes_received+=ret;
|
||||
eth->rx_actual_nsamps = bytes_received>>1;
|
||||
eth->rx_count++;
|
||||
}
|
||||
}
|
||||
|
||||
eth->rx_nsamps = nsamps;
|
||||
return(bytes_received);
|
||||
}
|
||||
|
||||
|
||||
int eth_set_dev_conf_raw(openair0_device *device) {
|
||||
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
void *msg;
|
||||
ssize_t msg_len;
|
||||
|
||||
/* a RAU client sends to RRU a set of configuration parameters (openair0_config_t)
|
||||
so that RF front end is configured appropriately and
|
||||
frame/packet size etc. can be set */
|
||||
|
||||
msg = malloc(MAC_HEADER_SIZE_BYTES + sizeof(openair0_config_t));
|
||||
msg_len = MAC_HEADER_SIZE_BYTES + sizeof(openair0_config_t);
|
||||
|
||||
|
||||
memcpy(msg,(void*)ð->ehc,MAC_HEADER_SIZE_BYTES);
|
||||
memcpy((msg+MAC_HEADER_SIZE_BYTES),(void*)device->openair0_cfg,sizeof(openair0_config_t));
|
||||
|
||||
if (send(eth->sockfdc,
|
||||
msg,
|
||||
msg_len,
|
||||
0)==-1) {
|
||||
perror("ETHERNET: ");
|
||||
exit(0);
|
||||
}
|
||||
free(msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int eth_set_dev_conf_raw_IF4p5(openair0_device *device) {
|
||||
// use for cc_id info
|
||||
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
void *msg;
|
||||
ssize_t msg_len;
|
||||
|
||||
/* a RAU client sends to RRU a set of configuration parameters (openair0_config_t)
|
||||
so that RF front end is configured appropriately and
|
||||
frame/packet size etc. can be set */
|
||||
|
||||
msg = malloc(MAC_HEADER_SIZE_BYTES + sizeof(openair0_config_t));
|
||||
msg_len = MAC_HEADER_SIZE_BYTES + sizeof(openair0_config_t);
|
||||
|
||||
|
||||
memcpy(msg,(void*)ð->ehc,MAC_HEADER_SIZE_BYTES);
|
||||
memcpy((msg+MAC_HEADER_SIZE_BYTES),(void*)device->openair0_cfg,sizeof(openair0_config_t));
|
||||
|
||||
if (send(eth->sockfdc,
|
||||
msg,
|
||||
msg_len,
|
||||
0)==-1) {
|
||||
perror("ETHERNET: ");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int eth_get_dev_conf_raw(openair0_device *device) {
|
||||
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
void *msg;
|
||||
ssize_t msg_len;
|
||||
|
||||
msg = malloc(MAC_HEADER_SIZE_BYTES + sizeof(openair0_config_t));
|
||||
msg_len = MAC_HEADER_SIZE_BYTES + sizeof(openair0_config_t);
|
||||
|
||||
/* RRU receives from RAU openair0_config_t */
|
||||
if (recv(eth->sockfdc,
|
||||
msg,
|
||||
msg_len,
|
||||
0)==-1) {
|
||||
perror("ETHERNET: ");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* RRU stores the remote MAC address */
|
||||
memcpy(eth->ehd.ether_dhost,(msg+ETH_ALEN),ETH_ALEN);
|
||||
//memcpy((void*)&device->openair0_cfg,(msg + MAC_HEADER_SIZE_BYTES), sizeof(openair0_config_t));
|
||||
device->openair0_cfg=(openair0_config_t *)(msg + MAC_HEADER_SIZE_BYTES);
|
||||
printf("[%s] binding mod to hardware address %x:%x:%x:%x:%x:%x hardware address %x:%x:%x:%x:%x:%x\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"),eth->ehd.ether_shost[0],eth->ehd.ether_shost[1],eth->ehd.ether_shost[2],eth->ehd.ether_shost[3],eth->ehd.ether_shost[4],eth->ehd.ether_shost[5],eth->ehd.ether_dhost[0],eth->ehd.ether_dhost[1],eth->ehd.ether_dhost[2],eth->ehd.ether_dhost[3],eth->ehd.ether_dhost[4],eth->ehd.ether_dhost[5]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int eth_get_dev_conf_raw_IF4p5(openair0_device *device) {
|
||||
// use for cc_id info
|
||||
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
void *msg;
|
||||
ssize_t msg_len;
|
||||
|
||||
msg = malloc(MAC_HEADER_SIZE_BYTES + sizeof(openair0_config_t));
|
||||
msg_len = MAC_HEADER_SIZE_BYTES + sizeof(openair0_config_t);
|
||||
|
||||
/* RRU receives from RAU openair0_config_t */
|
||||
if (recv(eth->sockfdc,
|
||||
msg,
|
||||
msg_len,
|
||||
0)==-1) {
|
||||
perror("ETHERNET: ");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* RRU stores the remote MAC address */
|
||||
memcpy(eth->ehd.ether_dhost,(msg+ETH_ALEN),ETH_ALEN);
|
||||
//memcpy((void*)&device->openair0_cfg,(msg + MAC_HEADER_SIZE_BYTES), sizeof(openair0_config_t));
|
||||
//device->openair0_cfg=(openair0_config_t *)(msg + MAC_HEADER_SIZE_BYTES);
|
||||
printf("[%s] binding mod to hardware address %x:%x:%x:%x:%x:%x hardware address %x:%x:%x:%x:%x:%x\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"),eth->ehd.ether_shost[0],eth->ehd.ether_shost[1],eth->ehd.ether_shost[2],eth->ehd.ether_shost[3],eth->ehd.ether_shost[4],eth->ehd.ether_shost[5],eth->ehd.ether_dhost[0],eth->ehd.ether_dhost[1],eth->ehd.ether_dhost[2],eth->ehd.ether_dhost[3],eth->ehd.ether_dhost[4],eth->ehd.ether_dhost[5]);
|
||||
|
||||
free(msg);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,547 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file ethernet_lib.c
|
||||
* \brief API to stream I/Q samples over standard ethernet
|
||||
* \author add alcatel Katerina Trilyraki, Navid Nikaein, Pedro Dinis, Lucio Ferreira, Raymond Knopp
|
||||
* \date 2015
|
||||
* \version 0.2
|
||||
* \company Eurecom
|
||||
* \maintainer: navid.nikaein@eurecom.fr
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
|
||||
// _GNU_SOURCE needed to have sched_getcpu() from sched.h
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/filter.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/ether.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "common/utils/LOG/vcd_signal_dumper.h"
|
||||
#include <sched.h>
|
||||
|
||||
#include "common_lib.h"
|
||||
#include "ethernet_lib.h"
|
||||
#include "common/utils/threadPool/thread-pool.h"
|
||||
|
||||
//#define DEBUG 1
|
||||
|
||||
// These are for IF5 and must be put into the device structure if multiple RUs in the same RAU !!!!!!!!!!!!!!!!!
|
||||
uint16_t pck_seq_num = 1;
|
||||
uint16_t pck_seq_num_cur=0;
|
||||
uint16_t pck_seq_num_prev=0;
|
||||
|
||||
int eth_socket_init_udp(openair0_device *device) {
|
||||
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
eth_params_t *eth_params = device->eth_params;
|
||||
|
||||
char str_local[INET_ADDRSTRLEN];
|
||||
char str_remote[INET_ADDRSTRLEN];
|
||||
|
||||
const char *local_ip, *remote_ipc,*remote_ipd;
|
||||
int local_portc=0,local_portd=0, remote_portc=0,remote_portd=0;
|
||||
int sock_dom=0;
|
||||
int sock_type=0;
|
||||
int sock_proto=0;
|
||||
int enable=1;
|
||||
const char str[2][4] = {"RRU\0","RAU\0"};
|
||||
int hostind = 0;
|
||||
|
||||
local_ip = eth_params->my_addr;
|
||||
local_portc = eth_params->my_portc;
|
||||
local_portd = eth_params->my_portd;
|
||||
|
||||
|
||||
if (device->host_type == RRU_HOST ) {
|
||||
|
||||
remote_ipc = "0.0.0.0";
|
||||
remote_ipd = eth_params->remote_addr;
|
||||
remote_portc = 0;
|
||||
remote_portd = eth_params->remote_portd;;
|
||||
printf("[%s] local ip addr %s portc %d portd %d\n", "RRU", local_ip, local_portc, local_portd);
|
||||
} else {
|
||||
remote_ipc = eth_params->remote_addr;
|
||||
remote_ipd = "0.0.0.0";
|
||||
remote_portc = eth_params->remote_portc;
|
||||
remote_portd = 0;
|
||||
hostind = 1;
|
||||
printf("[%s] local ip addr %s portc %d portd %d\n","RAU", local_ip, local_portc, local_portd);
|
||||
}
|
||||
|
||||
/* Open socket to send on */
|
||||
sock_dom=AF_INET;
|
||||
sock_type=SOCK_DGRAM;
|
||||
sock_proto=IPPROTO_UDP;
|
||||
|
||||
if ((eth->sockfdc = socket(sock_dom, sock_type, sock_proto)) == -1) {
|
||||
perror("ETHERNET: Error opening socket (control)");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
for (int i = 0;i<eth->num_fd;i++)
|
||||
if ((eth->sockfdd[i] = socket(sock_dom, sock_type, sock_proto)) == -1) {
|
||||
printf("ETHERNET: Error opening socket (user %d)",i);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* initialize addresses */
|
||||
bzero((void *)&(eth->dest_addrc), sizeof(eth->dest_addrc));
|
||||
bzero((void *)&(eth->local_addrc), sizeof(eth->local_addrc));
|
||||
bzero((void *)&(eth->dest_addrd), sizeof(eth->dest_addrd));
|
||||
bzero((void *)&(eth->local_addrd), sizeof(eth->local_addrd));
|
||||
|
||||
|
||||
eth->addr_len = sizeof(struct sockaddr_in);
|
||||
|
||||
eth->dest_addrc.sin_family = AF_INET;
|
||||
inet_pton(AF_INET,remote_ipc,&(eth->dest_addrc.sin_addr.s_addr));
|
||||
eth->dest_addrc.sin_port=htons(remote_portc);
|
||||
inet_ntop(AF_INET, &(eth->dest_addrc.sin_addr), str_remote, INET_ADDRSTRLEN);
|
||||
|
||||
eth->dest_addrd.sin_family = AF_INET;
|
||||
inet_pton(AF_INET,remote_ipd,&(eth->dest_addrd.sin_addr.s_addr));
|
||||
eth->dest_addrd.sin_port=htons(remote_portd);
|
||||
|
||||
eth->local_addrc.sin_family = AF_INET;
|
||||
inet_pton(AF_INET,local_ip,&(eth->local_addrc.sin_addr.s_addr));
|
||||
eth->local_addrc.sin_port=htons(local_portc);
|
||||
inet_ntop(AF_INET, &(eth->local_addrc.sin_addr), str_local, INET_ADDRSTRLEN);
|
||||
|
||||
eth->local_addrd.sin_family = AF_INET;
|
||||
inet_pton(AF_INET,local_ip,&(eth->local_addrd.sin_addr.s_addr));
|
||||
eth->local_addrd.sin_port=htons(local_portd);
|
||||
|
||||
|
||||
|
||||
|
||||
/* set reuse address flag */
|
||||
if (setsockopt(eth->sockfdc, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int))) {
|
||||
perror("ETHERNET: Cannot set SO_REUSEADDR option on socket (control)");
|
||||
exit(0);
|
||||
}
|
||||
for (int i=0;i<eth->num_fd;i++) {
|
||||
if (setsockopt(eth->sockfdd[i], SOL_SOCKET, SO_NO_CHECK, &enable, sizeof(int))) {
|
||||
printf("ETHERNET: Cannot set SO_NO_CHECK option on socket (user %d)",i);
|
||||
exit(0);
|
||||
}
|
||||
#if 0 /*def SO_ATTACH_REUSEPORT_EBPF*/
|
||||
if (setsockopt(eth->sockfdd[i], SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int))) {
|
||||
printf("ETHERNET: Cannot set SO_REUSEPORT option on socket (user %d)",i);
|
||||
exit(0);
|
||||
}
|
||||
struct sock_filter code[]={
|
||||
{ BPF_LD | BPF_W | BPF_ABS, 0, 0, SKF_AD_OFF + SKF_AD_CPU }, // A = #cpu
|
||||
{ BPF_RET | BPF_A, 0, 0, 0 }, // return A
|
||||
};
|
||||
struct sock_fprog bpf = {
|
||||
.len = sizeof(code)/sizeof(struct sock_filter),
|
||||
.filter = code,
|
||||
};
|
||||
if (i==0 && setsockopt(eth->sockfdd[i], SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &bpf, sizeof(bpf))) {
|
||||
printf("ETHERNET: Cannot set SO_ATTACH_REUSEPORT_EBPF option on socket (user %d)",i);
|
||||
exit(0);
|
||||
}
|
||||
else printf("ETHERNET: set SO_ATTACH_REUSEPORT_EBPF option on socket (user %d)\n",i);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/* want to receive -> so bind */
|
||||
if (bind(eth->sockfdc,(struct sockaddr *)ð->local_addrc,eth->addr_len)<0) {
|
||||
perror("ETHERNET: Cannot bind to socket (control)");
|
||||
exit(0);
|
||||
} else {
|
||||
printf("[%s] binding to %s:%d (control)\n",str[hostind],str_local,ntohs(eth->local_addrc.sin_port));
|
||||
}
|
||||
for (int i=0;i<eth->num_fd;i++)
|
||||
if (bind(eth->sockfdd[i],(struct sockaddr *)ð->local_addrd,eth->addr_len)<0) {
|
||||
printf("ETHERNET: Cannot bind to socket (user %d)",i);
|
||||
exit(0);
|
||||
} else {
|
||||
printf("[%s] binding to %s:%d (user %d)\n",str[hostind],str_local,ntohs(eth->local_addrd.sin_port),i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trx_eth_read_udp_IF4p5(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) {
|
||||
|
||||
// Read nblocks info from packet itself
|
||||
int nblocks = nsamps;
|
||||
int bytes_received=-1;
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
|
||||
ssize_t packet_size = sizeof_IF4p5_header_t;
|
||||
IF4p5_header_t *test_header = (IF4p5_header_t*)(buff[0]);
|
||||
|
||||
int block_cnt=0;
|
||||
|
||||
// *2 because of 2 antennas PUL/DLFFT are controlled by nsamps, PRACH is not
|
||||
packet_size = max(UDP_IF4p5_PRACH_SIZE_BYTES*2, max(UDP_IF4p5_PULFFT_SIZE_BYTES(nblocks), UDP_IF4p5_PDLFFT_SIZE_BYTES(nblocks)));
|
||||
|
||||
while(bytes_received == -1) {
|
||||
again:
|
||||
bytes_received = recvfrom(eth->sockfdd[0/*cc%eth->num_fd*/],
|
||||
buff[0],
|
||||
packet_size,
|
||||
0,
|
||||
(struct sockaddr *)ð->dest_addrd,
|
||||
(socklen_t *)ð->addr_len);
|
||||
if (bytes_received ==-1) {
|
||||
eth->num_rx_errors++;
|
||||
if (errno == EAGAIN) {
|
||||
printf("Lost IF4p5 connection with %s\n", inet_ntoa(eth->dest_addrd.sin_addr));
|
||||
return -1;
|
||||
} else if (errno == EWOULDBLOCK) {
|
||||
block_cnt++;
|
||||
usleep(10);
|
||||
if (block_cnt == 1000) {
|
||||
perror("ETHERNET IF4p5 READ (EWOULDBLOCK): ");
|
||||
} else {
|
||||
printf("BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK BLOCK \n");
|
||||
goto again;
|
||||
}
|
||||
} else {
|
||||
return(-1);
|
||||
//perror("ETHERNET IF4p5 READ");
|
||||
//printf("(%s):\n", strerror(errno));
|
||||
}
|
||||
} else {
|
||||
*timestamp = test_header->sub_type;
|
||||
eth->rx_actual_nsamps = bytes_received>>1;
|
||||
eth->rx_count++;
|
||||
}
|
||||
}
|
||||
eth->rx_nsamps = nsamps;
|
||||
return(bytes_received);
|
||||
}
|
||||
|
||||
int trx_eth_write_udp_IF4p5(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) {
|
||||
|
||||
int nblocks = nsamps;
|
||||
int bytes_sent = 0;
|
||||
|
||||
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
|
||||
ssize_t packet_size;
|
||||
|
||||
char str[INET_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET, &(eth->dest_addrd.sin_addr), str, INET_ADDRSTRLEN);
|
||||
|
||||
if (flags == IF4p5_PDLFFT) {
|
||||
packet_size = UDP_IF4p5_PDLFFT_SIZE_BYTES(nblocks);
|
||||
} else if (flags == IF4p5_PULFFT) {
|
||||
packet_size = UDP_IF4p5_PULFFT_SIZE_BYTES(nblocks);
|
||||
} else if (flags == IF4p5_PULTICK) {
|
||||
packet_size = UDP_IF4p5_PULTICK_SIZE_BYTES;
|
||||
} else if ((flags >= IF4p5_PRACH)&&
|
||||
(flags <= (IF4p5_PRACH+4))) {
|
||||
packet_size = UDP_HEADER_SIZE_BYTES + IPV4_HEADER_SIZE_BYTES + sizeof_IF4p5_header_t + (nsamps<<1);
|
||||
} else {
|
||||
printf("trx_eth_write_udp_IF4p5: unknown flags %d\n",flags);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
eth->tx_nsamps = nblocks;
|
||||
|
||||
bytes_sent = sendto(eth->sockfdd[0/*cc%eth->num_fd*/],
|
||||
buff[0],
|
||||
packet_size,
|
||||
0,
|
||||
(struct sockaddr*)ð->dest_addrd,
|
||||
eth->addr_len);
|
||||
|
||||
if (bytes_sent == -1) {
|
||||
eth->num_tx_errors++;
|
||||
perror("error writing to remote unit (user) : ");
|
||||
exit(-1);
|
||||
} else {
|
||||
eth->tx_actual_nsamps = bytes_sent>>1;
|
||||
eth->tx_count++;
|
||||
}
|
||||
return (bytes_sent);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void *trx_eth_write_udp_cmd(udpTXelem_t *udpTXelem) {
|
||||
|
||||
openair0_device *device=udpTXelem->device;
|
||||
openair0_timestamp timestamp = udpTXelem->timestamp;
|
||||
void **buff = udpTXelem->buff;
|
||||
int nsamps = udpTXelem->nsamps;
|
||||
int nant = udpTXelem->nant;
|
||||
int bytes_sent=0;
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
int sendto_flag =0;
|
||||
fhstate_t *fhstate = &device->fhstate;
|
||||
|
||||
//sendto_flag|=flags;
|
||||
eth->tx_nsamps=nsamps;
|
||||
|
||||
uint64_t last_rxTS = fhstate->TS[0]-fhstate->TS0;
|
||||
uint64_t TS_advance=0;
|
||||
if (timestamp > last_rxTS) TS_advance = timestamp - last_rxTS;
|
||||
|
||||
if (TS_advance < (nsamps/2)) LOG_W(PHY,"Starting TX FH for TS %llu absslot %llu(%llu) last_rxTS %llu TS_advance %llu samples\n",(unsigned long long)timestamp,(unsigned long long)timestamp/nsamps,((unsigned long long)timestamp/nsamps)%20,(unsigned long long)last_rxTS,(unsigned long long)TS_advance);
|
||||
void *buff2;
|
||||
#if defined(__x86_64) || defined(__i386__)
|
||||
#ifdef __AVX2__
|
||||
int nsamps2 = 256>>3;
|
||||
__m256i buff_tx[nsamps2+1];
|
||||
buff2=(void*)&buff_tx[1] - APP_HEADER_SIZE_BYTES;
|
||||
#else
|
||||
int nsamps2 = 256>>2;
|
||||
__m128i buff_tx[nsamps2+2];
|
||||
buff2=(void*)&buff_tx[2] - APP_HEADER_SIZE_BYTES;
|
||||
#endif
|
||||
#elif defined(__arm__) || defined(__aarch64__)
|
||||
int nsamps2 = 256>>2;
|
||||
int16x8_t buff_tx[nsamps2+2];
|
||||
buff2=(void*)&buff_tx[2] - APP_HEADER_SIZE_BYTES;
|
||||
#else
|
||||
#error Unsupported CPU architecture, ethernet device cannot be built
|
||||
#endif
|
||||
|
||||
/* construct application header */
|
||||
// ECPRI Protocol revision + reserved bits (1 byte)
|
||||
*(uint8_t *)buff2 = ECPRIREV;
|
||||
// ECPRI Message type (1 byte)
|
||||
*(uint8_t *)(buff2 + 1) = 64;
|
||||
|
||||
openair0_timestamp TS = timestamp + fhstate->TS0;
|
||||
TS = (6*device->sampling_rate_ratio_d*TS)/device->sampling_rate_ratio_n;
|
||||
TS -= device->txrx_offset;
|
||||
int TSinc = (6*256*device->sampling_rate_ratio_d)/device->sampling_rate_ratio_n;
|
||||
int len=256;
|
||||
LOG_D(PHY,"TS %llu (%llu),txrx_offset %d,d %d, n %d, buff[0] %p buff[1] %p\n",
|
||||
(unsigned long long)TS,(unsigned long long)timestamp,device->txrx_offset,device->sampling_rate_ratio_d,device->sampling_rate_ratio_n,
|
||||
buff[0],buff[1]);
|
||||
for (int offset=0;offset<nsamps;offset+=256,TS+=TSinc) {
|
||||
// OAI modified SEQ_ID (4 bytes)
|
||||
*(uint64_t *)(buff2 + 6) = TS;
|
||||
if ((offset + 256) <= nsamps) len=1024;
|
||||
else len = (nsamps-offset)<<2;
|
||||
// ECPRI Payload Size (2 bytes)
|
||||
*(uint8_t *)(buff2 + 2) = len>>8;
|
||||
*(uint8_t *)(buff2 + 3) = len&0xff;
|
||||
for (int aid = 0; aid<nant; aid++) {
|
||||
LOG_D(PHY,"TS %llu (TS0 %llu) aa %d : offset %d, len %d\n",(unsigned long long)TS,(unsigned long long)fhstate->TS0,aid,offset,len);
|
||||
// ECPRI PC_ID (2 bytes)
|
||||
*(uint16_t *)(buff2 + 4) = aid;
|
||||
// bring TX data into 12 MSBs
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
#ifdef __AVX2__
|
||||
__m256i *buff256 = (__m256i *)&(((int32_t*)buff[aid])[offset]);
|
||||
for (int j=0; j<32; j+=8) {
|
||||
buff_tx[1+j] = _mm256_slli_epi16(buff256[j],4);
|
||||
buff_tx[2+j] = _mm256_slli_epi16(buff256[j+1],4);
|
||||
buff_tx[3+j] = _mm256_slli_epi16(buff256[j+2],4);
|
||||
buff_tx[4+j] = _mm256_slli_epi16(buff256[j+3],4);
|
||||
buff_tx[5+j] = _mm256_slli_epi16(buff256[j+4],4);
|
||||
buff_tx[6+j] = _mm256_slli_epi16(buff256[j+5],4);
|
||||
buff_tx[7+j] = _mm256_slli_epi16(buff256[j+6],4);
|
||||
buff_tx[8+j] = _mm256_slli_epi16(buff256[j+7],4);
|
||||
}
|
||||
#else
|
||||
__m128i *buff128 = (__m128i *)&buff[aid][offset];
|
||||
for (int j=0; j<64; j++) buff_tx[2+j] = _mm_slli_epi16(buff128[j],4);
|
||||
#endif
|
||||
#elif defined(__arm__)
|
||||
int16x8_t *buff128 = (__int16x8_t*)&buff[aid][offset];
|
||||
for (int j=0; j<64; j++) buff_tx[2+j] = vshlq_n_s16(((int16x8_t *)buff128)[j],4);
|
||||
#endif
|
||||
|
||||
/* Send packet */
|
||||
bytes_sent = sendto(eth->sockfdd[0],
|
||||
buff2,
|
||||
UDP_PACKET_SIZE_BYTES(len>>2),
|
||||
sendto_flag,
|
||||
(struct sockaddr*)ð->dest_addrd,
|
||||
eth->addr_len);
|
||||
if ( bytes_sent == -1) {
|
||||
eth->num_tx_errors++;
|
||||
perror("ETHERNET WRITE: ");
|
||||
exit(-1);
|
||||
} else {
|
||||
eth->tx_actual_nsamps=bytes_sent>>2;
|
||||
eth->tx_count++;
|
||||
}
|
||||
} // aid
|
||||
} // offset
|
||||
free(buff);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
int trx_eth_write_udp(openair0_device *device, openair0_timestamp timestamp, void **buff, int fd_ind, int nsamps, int flags, int nant) {
|
||||
|
||||
union udpTXReqUnion id = {.s={(uint64_t)timestamp,nsamps,0}};
|
||||
notifiedFIFO_elt_t *req=newNotifiedFIFO_elt(sizeof(udpTXelem_t), id.p, device->utx[fd_ind]->resp,NULL);
|
||||
udpTXelem_t * udptxelem=(udpTXelem_t *) NotifiedFifoData(req);
|
||||
udptxelem->device = device;
|
||||
udptxelem->timestamp = timestamp;
|
||||
udptxelem->buff = calloc(nant,sizeof(void*));
|
||||
memcpy(udptxelem->buff,buff,nant*sizeof(void*));
|
||||
udptxelem->fd_ind = fd_ind;
|
||||
udptxelem->nsamps = nsamps;
|
||||
udptxelem->flags = flags;
|
||||
udptxelem->nant = nant;
|
||||
pushNotifiedFIFO(device->utx[fd_ind]->resp, req);
|
||||
LOG_D(PHY,"Pushed to TX FH FIFO, TS %llu, nsamps %d, nant %d buffs[0] %p buffs[1] %p\n",
|
||||
(unsigned long long)timestamp,nsamps,nant,udptxelem->buff[0],udptxelem->buff[1]);
|
||||
return(0);
|
||||
}
|
||||
extern int oai_exit;
|
||||
|
||||
void *udp_write_thread(void *arg) {
|
||||
|
||||
udp_ctx_t *utx = (udp_ctx_t *)arg;
|
||||
utx->resp = (notifiedFIFO_t*) malloc(sizeof(notifiedFIFO_elt_t));
|
||||
initNotifiedFIFO(utx->resp);
|
||||
LOG_D(PHY,"UDP write thread started on core %d\n",sched_getcpu());
|
||||
reset_meas(&utx->device->tx_fhaul);
|
||||
while (oai_exit == 0) {
|
||||
notifiedFIFO_elt_t *res = pullNotifiedFIFO(utx->resp);
|
||||
udpTXelem_t *udptxelem = (udpTXelem_t *)NotifiedFifoData(res);
|
||||
LOG_D(PHY,"Pulled from TX FH FIFO, TS %llu, nsamps %d, nant %d\n",(unsigned long long)udptxelem->timestamp,udptxelem->nsamps,udptxelem->nant);
|
||||
start_meas(&utx->device->tx_fhaul);
|
||||
trx_eth_write_udp_cmd(udptxelem);
|
||||
stop_meas(&utx->device->tx_fhaul);
|
||||
// send data to RU
|
||||
delNotifiedFIFO_elt(res);
|
||||
}
|
||||
free(utx->resp);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
void *udp_read_thread(void *arg) {
|
||||
openair0_timestamp TS;
|
||||
|
||||
int aid;
|
||||
udp_ctx_t *u = (udp_ctx_t *)arg;
|
||||
openair0_device *device=u->device;
|
||||
fhstate_t *fhstate = &device->fhstate;
|
||||
char buffer[UDP_PACKET_SIZE_BYTES(256)];
|
||||
int first_read=0;
|
||||
while (oai_exit == 0) {
|
||||
LOG_I(PHY,"UDP read thread %d, waiting for start sampling_rate_d %d, sampling_rate_n %d\n",u->thread_id,device->sampling_rate_ratio_n,device->sampling_rate_ratio_d);
|
||||
while (fhstate->active > 0) {
|
||||
size_t count = recvfrom(((eth_state_t*)device->priv)->sockfdd[0],
|
||||
buffer,sizeof(buffer),0,
|
||||
(struct sockaddr *)&((eth_state_t*)device->priv)->dest_addrd,
|
||||
(socklen_t *)&((eth_state_t*)device->priv)->addr_len);
|
||||
aid = *(uint16_t*)(&buffer[ECPRICOMMON_BYTES]);
|
||||
TS = *(openair0_timestamp *)(&buffer[ECPRICOMMON_BYTES+ECPRIPCID_BYTES]);
|
||||
// convert TS to samples, /6 for AW2S @ 30.72 Ms/s, this is converted for other sample rates in OAI application
|
||||
TS = (device->sampling_rate_ratio_n*TS)/(device->sampling_rate_ratio_d*6);
|
||||
if ((int)count <= 0) continue;
|
||||
AssertFatal(aid < 8,"Cannot handle more than 8 antennas, got aid %d\n",aid);
|
||||
fhstate->r[aid]=1;
|
||||
if (aid==0 && first_read == 0) fhstate->TS0 = TS;
|
||||
first_read = 1;
|
||||
/* store the timestamp value from packet's header */
|
||||
fhstate->TS[aid] = TS;
|
||||
int64_t offset = TS - fhstate->TS0;
|
||||
if (offset > 0) offset = offset % device->openair0_cfg->rxsize;
|
||||
else offset = TS % device->openair0_cfg->rxsize + ((((uint64_t)1)<<63)-(fhstate->TS0-1)) % device->openair0_cfg->rxsize;
|
||||
// need to do memcpy since there is no guarantee that aid is the same each time, otherwise we could have used
|
||||
// zero-copy and corrected the header component.
|
||||
|
||||
memcpy((void*)(device->openair0_cfg->rxbase[aid]+offset),
|
||||
(void*)&buffer[APP_HEADER_SIZE_BYTES],
|
||||
count-APP_HEADER_SIZE_BYTES);
|
||||
LOG_D(PHY,"UDP read thread_id %d (%d), aid %d, TS %llu, TS0 %llu, offset %ld\n",(int)u->thread_id,(int)sched_getcpu(),aid,(unsigned long long)TS,(unsigned long long)fhstate->TS0,offset);
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
int trx_eth_read_udp(openair0_device *device, openair0_timestamp *timestamp, uint32_t **buff, int nsamps) {
|
||||
|
||||
fhstate_t *fhstate = &device->fhstate;
|
||||
openair0_timestamp prev_read_TS= fhstate->TS_read, min_TS;
|
||||
// block until FH is ready
|
||||
while (fhstate->r[0] == 0 || fhstate->r[1] == 0 || fhstate->r[2] == 0 || fhstate->r[3] == 0 ||
|
||||
fhstate->r[4] == 0 || fhstate->r[5] == 0 || fhstate->r[6] == 0 || fhstate->r[7] == 0) usleep(100);
|
||||
|
||||
// get minimum TS over all antennas
|
||||
min_TS = fhstate->TS[0];
|
||||
for (int i=1;i<device->openair0_cfg->rx_num_channels;i++) min_TS = min(min_TS,fhstate->TS[i]);
|
||||
// poll/sleep until we accumulated enough samples on each antenna port
|
||||
int count=0;
|
||||
while (fhstate->first_read == 1 && min_TS < (fhstate->TS0+prev_read_TS + nsamps)) {
|
||||
usleep(50);
|
||||
min_TS = fhstate->TS[0];
|
||||
for (int i=1;i<device->openair0_cfg->rx_num_channels;i++) min_TS = min(min_TS,fhstate->TS[i]);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (fhstate->first_read == 0) {
|
||||
*timestamp = 0;
|
||||
fhstate->TS_read = *timestamp+nsamps;
|
||||
LOG_D(PHY,"first read : TS_read %llu, TS %llu state (%d,%d,%d,%d,%d,%d,%d,%d)\n",(unsigned long long)fhstate->TS_read,(unsigned long long)*timestamp,
|
||||
fhstate->r[0],fhstate->r[1],fhstate->r[2],fhstate->r[3],fhstate->r[4],fhstate->r[5],fhstate->r[6],fhstate->r[7]);
|
||||
}
|
||||
else {
|
||||
*timestamp = fhstate->TS_read;
|
||||
fhstate->TS_read = prev_read_TS + nsamps;
|
||||
LOG_D(PHY,"TS_read %llu (%llu, %llu), min_TS %llu, prev_read_TS %llu, nsamps %d, fhstate->TS0+prev_TS+nsamps %llu, wait count %d x 100us\n",(unsigned long long)fhstate->TS_read,(unsigned long long)*timestamp/nsamps,((unsigned long long)*timestamp/nsamps)%20,(unsigned long long)min_TS,(unsigned long long)prev_read_TS,nsamps,(unsigned long long)(fhstate->TS0+prev_read_TS+nsamps),count);
|
||||
}
|
||||
fhstate->first_read = 1;
|
||||
return (nsamps);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int trx_eth_ctlsend_udp(openair0_device *device, void *msg, ssize_t msg_len) {
|
||||
|
||||
return(sendto(((eth_state_t*)device->priv)->sockfdc,
|
||||
msg,
|
||||
msg_len,
|
||||
0,
|
||||
(struct sockaddr *)&((eth_state_t*)device->priv)->dest_addrc,
|
||||
((eth_state_t*)device->priv)->addr_len));
|
||||
}
|
||||
|
||||
|
||||
int trx_eth_ctlrecv_udp(openair0_device *device, void *msg, ssize_t msg_len) {
|
||||
|
||||
return (recvfrom(((eth_state_t*)device->priv)->sockfdc,
|
||||
msg,
|
||||
msg_len,
|
||||
0,
|
||||
(struct sockaddr *)&((eth_state_t*)device->priv)->dest_addrc,
|
||||
(socklen_t *)&((eth_state_t*)device->priv)->addr_len));
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
# Ethernet drivers
|
||||
|
||||
This directory contains ethernet-based drivers for fronthaul. The only functional versions today are ECPRI time-domain (split 8) over UDP for AW2S and IF4.5 (split 7) for OAI RU over UDP. The RAW ethernet implementation is currently not tested and quite possibly obsolete.
|
||||
|
||||
|
||||
## license
|
||||
Author:
|
||||
Raymond Knopp, EURECOM
|
||||
OAI License V1.1
|
||||
# Top-level
|
||||
|
||||
The files implement the OAI IF device interface which provides the transmit/receive (trx) functions for generic ethernet-based devices. Some minimal control-plane socket handling for IF4p5 is also provided. It comprises the following top-level functions (ethernet_lib.c) which are mapped to the OAI device structure:
|
||||
|
||||
* trx_eth_start : create sockets and threads to handle I/O
|
||||
* trx_eth_end : close sockets
|
||||
* trx_eth_stop : stops 3rd party (AW2S) device
|
||||
* trx_eth_set_freq : empty
|
||||
* trx_eth_set_gains : empty
|
||||
* trx_eth_get_stats : empty
|
||||
* trx_eth_reset_stats : empty
|
||||
* trx_eth_write_init : empty
|
||||
* ethernet_tune : write certain performance-related parameters to ethernet device
|
||||
* transport_init : entry routine to fill device data structure with function pointers
|
||||
|
||||
|
||||
# IF4p5 U-plane implementation
|
||||
|
||||
It is contained in the eth_udp.c and eth_raw.c files. The two basic routines are
|
||||
|
||||
* trx_eth_read_udp_IF4p5() : implements a blocking read for three particular IF4p5 packets, IF4p5_PULFFT (for OAI RCC/DU), IF4p5_PRACH and IF4p5_PDLFFT (for OAI RU). The packets are parsed and mapped to the appropriate physical channels by the OAI physical layer
|
||||
* trx_eth_write_udp_IF4p5 : implements a write for the three IF4p5 packets.
|
||||
|
||||
* trx_eth_ctlsend_udp : implements the sending component for the control socket
|
||||
|
||||
* trx_eth_ctlrecv_udp : implements the receive component for the control socket (blocking read)
|
||||
|
||||
# ECPRI U-plane implementation
|
||||
|
||||
It is contained in the eth_udp.c file and only implements ECPRI/UDP. The implementation has 2 top-level functions:
|
||||
|
||||
* trx_eth_write_udp : This sends a stream of packets containing 16-bit I/ 16-bit Q samples to the ECPRI RU. It uses the proprietary AW2S format (user-defined). Each packet carries 256 samples and some header information (timestamp, antenna index) and fits inslightly more than 1024 bytes. Each packet with Ethernet, IP and UDP headers fits into a regular Ethernet frame. The transmit function does not block and pushes the data to a worker thread (udp_write_thread) which runs in the background. The worker thread scales the IQ samples to fit in 16-bit units and forms the ECPRI packets for each antenna. The signals are split into 256-sample chunks and antennas are handled in sequence for each chunk. This ensures that all antennas receive their signals more or less at the same time.
|
||||
|
||||
* trx_eth_read_udp : This is a blocking read which waits for a worker thread (udp_read_thread) listening on the U-plane socket to have acquired enough samples satisfying the request (nsamps). The requests are usually a slot (NR) or subframe (LTE) worth of samples. The worker thread identifies the antenna index (aid) and timestamp of each packet and copies the received chunk into the destination memory location according to the timestamp modulo the length of the receive buffer. The memory location for read is identifited during initialization of the interface and is written circularly. Typically the buffer would contain a frame's worth of samples, but this is not a requirement. There is only one memcpy in the driver and the end OAI application can use the data in its normal local buffer (RU.common.rxdata[aid]). The memcpy is needed since the destination address depends on the antenna index which is carried in the received packet itself. Kernel filtering and redirection (C/EBPF,XDP) doesn't seem to be possible with the AW2S packet format.
|
||||
|
||||
The two threads should be pinned to isolated CPUs to maximize performance. Their CPU id's can be passed to the driver from the OAI application. tx_fh_core and rx_fh_core in the RU section of the OAI .conf file.
|
||||
|
||||
# Obsolete code
|
||||
|
||||
Non ECPRI split 8 and IF4p5 with RAW sockets is not functional anymore (eth_raw.c). split-8 will be resusitated if an RU implementing ECPRI is integrated with OAI. IF4p5 with raw sockets is replaced with the FHI Split 7.2 interface.
|
||||
|
||||
|
||||
@@ -0,0 +1,685 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file ethernet_lib.c
|
||||
* \brief API to stream I/Q samples over standard ethernet
|
||||
* \author add alcatel Katerina Trilyraki, Navid Nikaein, Pedro Dinis, Lucio Ferreira, Raymond Knopp
|
||||
* \date 2015
|
||||
* \version 0.2
|
||||
* \company Eurecom
|
||||
* \maintainer: navid.nikaein@eurecom.fr
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/ether.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <linux/sysctl.h>
|
||||
|
||||
#include "common_lib.h"
|
||||
#include "ethernet_lib.h"
|
||||
|
||||
|
||||
int num_devices_eth = 0;
|
||||
struct sockaddr_in dest_addr[MAX_INST];
|
||||
int dest_addr_len[MAX_INST];
|
||||
|
||||
int load_lib(openair0_device *device,
|
||||
openair0_config_t *openair0_cfg,
|
||||
eth_params_t *cfg,
|
||||
uint8_t flag);
|
||||
|
||||
int trx_eth_start(openair0_device *device)
|
||||
{
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
|
||||
if (eth->flags == ETH_UDP_IF5_ECPRI_MODE) {
|
||||
AssertFatal(device->thirdparty_init != NULL, "device->thirdparty_init is null\n");
|
||||
AssertFatal(device->thirdparty_init(device) == 0, "third-party init failed\n");
|
||||
device->openair0_cfg->samples_per_packet = 256;
|
||||
eth->num_fd = 1; //max(device->openair0_cfg->rx_num_channels,device->openair0_cfg->tx_num_channels);
|
||||
udp_ctx_t *u[1+eth->num_fd];
|
||||
device->utx = (udp_ctx_t**)malloc(sizeof(device->utx));
|
||||
for (int i=0;i<eth->num_fd;i++) {
|
||||
u[i] = malloc(sizeof(udp_ctx_t));
|
||||
u[i]->thread_id=i;
|
||||
u[i]->device = device;
|
||||
printf("UDP Read Thread %d on core %d\n",i,device->openair0_cfg->rxfh_cores[i]);
|
||||
threadCreate(&u[i]->pthread,udp_read_thread,u[i],"udp read thread",device->openair0_cfg->rxfh_cores[i],OAI_PRIORITY_RT_MAX);
|
||||
device->utx[i] = malloc(sizeof(udp_ctx_t));
|
||||
device->utx[i]->thread_id=i;
|
||||
device->utx[i]->device = device;
|
||||
printf("UDP Write Thread %d on core %d\n",i,device->openair0_cfg->txfh_cores[i]);
|
||||
threadCreate(&device->utx[i]->pthread,udp_write_thread,device->utx[i],"udp write thread",device->openair0_cfg->txfh_cores[i],OAI_PRIORITY_RT_MAX);
|
||||
}
|
||||
device->sampling_rate_ratio_n=1;
|
||||
device->sampling_rate_ratio_d=1;
|
||||
if (device->openair0_cfg->nr_flag==1) { // This check if RRU knows about NR numerologies
|
||||
if (device->openair0_cfg->num_rb_dl <= 162 && device->openair0_cfg->num_rb_dl > 106) {
|
||||
device->sampling_rate_ratio_n = 2;
|
||||
device->txrx_offset=5500;
|
||||
}
|
||||
else if (device->openair0_cfg->num_rb_dl <= 106 && device->openair0_cfg->num_rb_dl > 51) {
|
||||
device->sampling_rate_ratio_d=3;
|
||||
device->sampling_rate_ratio_n=4;
|
||||
device->txrx_offset=3750;
|
||||
}
|
||||
else if (device->openair0_cfg->num_rb_dl == 51) {
|
||||
device->sampling_rate_ratio_n=1;
|
||||
device->sampling_rate_ratio_d=1;
|
||||
device->txrx_offset=7500;
|
||||
}
|
||||
else AssertFatal(1==0,"num_rb_dl %d not ok with ECPRI\n",device->openair0_cfg->num_rb_dl);
|
||||
}
|
||||
else {
|
||||
if (device->openair0_cfg->num_rb_dl == 100 || device->openair0_cfg->num_rb_dl == 51) {
|
||||
device->sampling_rate_ratio_d = 1;
|
||||
device->txrx_offset=7500;
|
||||
}
|
||||
else if (device->openair0_cfg->num_rb_dl == 75) {
|
||||
device->sampling_rate_ratio_d = 4;
|
||||
device->sampling_rate_ratio_n=3;
|
||||
device->txrx_offset=7500;
|
||||
}
|
||||
else if (device->openair0_cfg->num_rb_dl == 50) {
|
||||
device->sampling_rate_ratio_d = 2;
|
||||
device->txrx_offset=7500;
|
||||
}
|
||||
else if (device->openair0_cfg->num_rb_dl == 25) {
|
||||
device->sampling_rate_ratio_d = 4;
|
||||
device->txrx_offset=7500;
|
||||
}
|
||||
else AssertFatal(1==0,"num_rb_dl %d not ok with ECPRI\n",device->openair0_cfg->num_rb_dl);
|
||||
}
|
||||
#ifdef USE_TX_TPOOL
|
||||
int threadCnt = device->openair0_cfg->tx_num_channels;
|
||||
if (threadCnt < 2) LOG_E(PHY,"Number of threads for gNB should be more than 1. Allocated only %d\n",threadCnt);
|
||||
char pool[80];
|
||||
sprintf(pool,"-1");
|
||||
int s_offset = 0;
|
||||
for (int icpu=1; icpu<threadCnt; icpu++) {
|
||||
sprintf(pool+2+s_offset,",-1");
|
||||
s_offset += 3;
|
||||
}
|
||||
device->threadPool = (tpool_t*)malloc(sizeof(tpool_t));
|
||||
initTpool(pool, device->threadPool, cpumeas(CPUMEAS_GETSTATE));
|
||||
// ULSCH decoder result FIFO
|
||||
device->respudpTX = (notifiedFIFO_elt_t*) malloc(sizeof(notifiedFIFO_elt_t));
|
||||
initNotifiedFIFO(device->respudpTX);
|
||||
#endif
|
||||
}
|
||||
/* initialize socket */
|
||||
if (eth->flags == ETH_RAW_MODE) {
|
||||
printf("Setting ETHERNET to ETH_RAW_IF5_MODE\n");
|
||||
if (eth_socket_init_raw(device)!=0) return -1;
|
||||
/* RRU gets device configuration - RAU sets device configuration*/
|
||||
|
||||
printf("Setting Timenout to 999999 usecs\n");
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
if(eth_set_dev_conf_raw(device)!=0) return -1;
|
||||
} else {
|
||||
if(eth_get_dev_conf_raw(device)!=0) return -1;
|
||||
}*/
|
||||
|
||||
/* adjust MTU wrt number of samples per packet */
|
||||
if(eth->compression == ALAW_COMPRESS) {
|
||||
if(ethernet_tune (device,MTU_SIZE,RAW_PACKET_SIZE_BYTES_ALAW(device->openair0_cfg->samples_per_packet))!=0) return -1;
|
||||
} else {
|
||||
if(ethernet_tune (device,MTU_SIZE,RAW_PACKET_SIZE_BYTES(device->openair0_cfg->samples_per_packet))!=0) return -1;
|
||||
}
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
} else if (eth->flags == ETH_RAW_IF4p5_MODE) {
|
||||
|
||||
printf("Setting ETHERNET to ETH_RAW_IF4p5_MODE\n");
|
||||
if (eth_socket_init_raw(device)!=0) return -1;
|
||||
|
||||
printf("Setting Timenout to 999999 usecs\n");
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
if(eth_set_dev_conf_raw_IF4p5(device)!=0) return -1;
|
||||
} else {
|
||||
if(eth_get_dev_conf_raw_IF4p5(device)!=0) return -1;
|
||||
}
|
||||
*/
|
||||
|
||||
/* adjust MTU wrt number of samples per packet */
|
||||
if(ethernet_tune (device,MTU_SIZE,RAW_IF4p5_PRACH_SIZE_BYTES)!=0) return -1;
|
||||
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
} else if (eth->flags == ETH_UDP_IF4p5_MODE) {
|
||||
printf("Setting ETHERNET to UDP_IF4p5_MODE\n");
|
||||
if (eth_socket_init_udp(device)!=0) return -1;
|
||||
|
||||
printf("Setting Timeout to 999999 usecs\n");
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
if(eth_set_dev_conf_udp(device)!=0) return -1;
|
||||
} else {
|
||||
if(eth_get_dev_conf_udp(device)!=0) return -1;
|
||||
}
|
||||
*/
|
||||
|
||||
/* adjust MTU wrt number of samples per packet */
|
||||
// if(ethernet_tune (device,MTU_SIZE,UDP_IF4p5_PRACH_SIZE_BYTES)!=0) return -1;
|
||||
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
printf("Setting ETHERNET to UDP_IF5_MODE\n");
|
||||
if (eth_socket_init_udp(device)!=0) return -1;
|
||||
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
if(eth_set_dev_conf_udp(device)!=0) return -1;
|
||||
} else {
|
||||
if(eth_get_dev_conf_udp(device)!=0) return -1;
|
||||
}*/
|
||||
|
||||
//if(ethernet_tune (device,MTU_SIZE,UDP_IF4p5_PRACH_SIZE_BYTES)!=0) return -1;
|
||||
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
}
|
||||
/* apply additional configuration */
|
||||
if(ethernet_tune (device, SND_BUF_SIZE,67108864)!=0) return -1;
|
||||
if(ethernet_tune (device, RCV_BUF_SIZE,67108864)!=0) return -1;
|
||||
if(ethernet_tune (device, KERNEL_SND_BUF_MAX_SIZE, 67108864)!=0) return -1;
|
||||
if(ethernet_tune (device, KERNEL_RCV_BUF_MAX_SIZE, 67108864)!=0) return -1;
|
||||
if(ethernet_tune (device, TX_Q_LEN, 10000)!=0) return -1;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void trx_eth_end(openair0_device *device)
|
||||
{
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
/* destroys socket only for the processes that call the eth_end fuction-- shutdown() for beaking the pipe */
|
||||
for (int i=0;i<eth->num_fd;i++)
|
||||
if ( close(eth->sockfdd[i]) <0 ) {
|
||||
perror("ETHERNET: Failed to close socket");
|
||||
exit(0);
|
||||
} else {
|
||||
printf("[%s] socket has been successfully closed.\n",(device->host_type == RAU_HOST)? "RAU":"RRU");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int trx_eth_stop(openair0_device *device) {
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
|
||||
if (eth->flags == ETH_UDP_IF5_ECPRI_MODE) {
|
||||
AssertFatal(device->thirdparty_cleanup != NULL, "device->thirdparty_cleanup is null\n");
|
||||
AssertFatal(device->thirdparty_cleanup(device) == 0, "third-party cleanup failed\n");
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int trx_eth_set_freq(openair0_device* device,
|
||||
openair0_config_t *openair0_cfg,
|
||||
int exmimo_dump_config)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int trx_eth_set_gains(openair0_device* device, openair0_config_t *openair0_cfg)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int trx_eth_get_stats(openair0_device* device)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int trx_eth_reset_stats(openair0_device* device)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int trx_eth_write_init(openair0_device *device)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ethernet_tune(openair0_device *device,
|
||||
unsigned int option,
|
||||
int value)
|
||||
{
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
struct timeval timeout;
|
||||
struct ifreq ifr;
|
||||
char system_cmd[256];
|
||||
int ret=0;
|
||||
// int i=0;
|
||||
|
||||
/****************** socket level options ************************/
|
||||
switch(option) {
|
||||
case SND_BUF_SIZE: /* transmit socket buffer size */
|
||||
for (int i=0;i<eth->num_fd;i++)
|
||||
if (setsockopt(eth->sockfdd[i],
|
||||
SOL_SOCKET,
|
||||
SO_SNDBUF,
|
||||
&value,sizeof(value))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf("send buffer size= %d bytes\n",value);
|
||||
}
|
||||
break;
|
||||
|
||||
case RCV_BUF_SIZE: /* receive socket buffer size */
|
||||
for (int i=0;i<eth->num_fd;i++) {
|
||||
if (setsockopt(eth->sockfdd[i],
|
||||
SOL_SOCKET,
|
||||
SO_RCVBUF,
|
||||
&value,sizeof(value))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf("receive bufffer size= %d bytes\n",value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RCV_TIMEOUT:
|
||||
timeout.tv_sec = value/1000000;
|
||||
timeout.tv_usec = value%1000000;//less than rt_period?
|
||||
if (setsockopt(eth->sockfdc,
|
||||
SOL_SOCKET,
|
||||
SO_RCVTIMEO,
|
||||
(char *)&timeout,sizeof(timeout))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf( "receive timeout= %u usec\n",(unsigned int)timeout.tv_usec);
|
||||
}
|
||||
for (int i=0;i<eth->num_fd;i++) {
|
||||
if (setsockopt(eth->sockfdd[i],
|
||||
SOL_SOCKET,
|
||||
SO_RCVTIMEO,
|
||||
(char *)&timeout,sizeof(timeout))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf( "receive timeout= %u usec\n",(unsigned int)timeout.tv_usec);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_TIMEOUT:
|
||||
timeout.tv_sec = value/1000000000;
|
||||
timeout.tv_usec = value%1000000000;//less than rt_period?
|
||||
if (setsockopt(eth->sockfdc,
|
||||
SOL_SOCKET,
|
||||
SO_SNDTIMEO,
|
||||
(char *)&timeout,sizeof(timeout))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf( "send timeout= %d,%d sec\n",(int)timeout.tv_sec,(int)timeout.tv_usec);
|
||||
}
|
||||
for (int i=0;i<eth->num_fd;i++) {
|
||||
if (setsockopt(eth->sockfdd[i],
|
||||
SOL_SOCKET,
|
||||
SO_SNDTIMEO,
|
||||
(char *)&timeout,sizeof(timeout))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf( "send timeout= %d,%d sec\n",(int)timeout.tv_sec,(int)timeout.tv_usec);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
/******************* interface level options *************************/
|
||||
case MTU_SIZE: /* change MTU of the eth interface */
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
strncpy(ifr.ifr_name,eth->if_name, sizeof(ifr.ifr_name)-1);
|
||||
ifr.ifr_mtu =value;
|
||||
for (int i=0;i<eth->num_fd;i++) {
|
||||
if (ioctl(eth->sockfdd[i],SIOCSIFMTU,(caddr_t)&ifr) < 0 )
|
||||
perror ("[ETHERNET] Can't set the MTU");
|
||||
else
|
||||
printf("[ETHERNET] %s MTU size has changed to %d\n",eth->if_name,ifr.ifr_mtu);
|
||||
}
|
||||
break;
|
||||
|
||||
case TX_Q_LEN: /* change TX queue length of eth interface */
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
strncpy(ifr.ifr_name,eth->if_name, sizeof(ifr.ifr_name)-1);
|
||||
ifr.ifr_qlen =value;
|
||||
for (int i=0;i<eth->num_fd;i++) {
|
||||
if (ioctl(eth->sockfdd[i],SIOCSIFTXQLEN,(caddr_t)&ifr) < 0 )
|
||||
perror ("[ETHERNET] Can't set the txqueuelen");
|
||||
else
|
||||
printf("[ETHERNET] %s txqueuelen size has changed to %d\n",eth->if_name,ifr.ifr_qlen);
|
||||
}
|
||||
break;
|
||||
|
||||
/******************* device level options *************************/
|
||||
case COALESCE_PAR:
|
||||
ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -C %s rx-usecs %d",eth->if_name,value);
|
||||
if (ret > 0) {
|
||||
ret=system(system_cmd);
|
||||
if (ret == -1) {
|
||||
fprintf (stderr,"[ETHERNET] Can't start shell to execute %s %s",system_cmd, strerror(errno));
|
||||
} else {
|
||||
printf ("[ETHERNET] status of %s is %d\n", system_cmd, WEXITSTATUS(ret));
|
||||
}
|
||||
printf("[ETHERNET] Coalesce parameters %s\n",system_cmd);
|
||||
} else {
|
||||
perror("[ETHERNET] Can't set coalesce parameters\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case PAUSE_PAR:
|
||||
if (value==1) ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -A %s autoneg off rx off tx off",eth->if_name);
|
||||
else if (value==0) ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -A %s autoneg on rx on tx on",eth->if_name);
|
||||
else break;
|
||||
if (ret > 0) {
|
||||
ret=system(system_cmd);
|
||||
if (ret == -1) {
|
||||
fprintf (stderr,"[ETHERNET] Can't start shell to execute %s %s",system_cmd, strerror(errno));
|
||||
} else {
|
||||
printf ("[ETHERNET] status of %s is %d\n", system_cmd, WEXITSTATUS(ret));
|
||||
}
|
||||
printf("[ETHERNET] Pause parameters %s\n",system_cmd);
|
||||
} else {
|
||||
perror("[ETHERNET] Can't set pause parameters\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case RING_PAR:
|
||||
ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -G %s val %d",eth->if_name,value);
|
||||
if (ret > 0) {
|
||||
ret=system(system_cmd);
|
||||
if (ret == -1) {
|
||||
fprintf (stderr,"[ETHERNET] Can't start shell to execute %s %s",system_cmd, strerror(errno));
|
||||
} else {
|
||||
printf ("[ETHERNET] status of %s is %d\n", system_cmd, WEXITSTATUS(ret));
|
||||
}
|
||||
printf("[ETHERNET] Ring parameters %s\n",system_cmd);
|
||||
} else {
|
||||
perror("[ETHERNET] Can't set ring parameters\n");
|
||||
}
|
||||
break;
|
||||
case KERNEL_RCV_BUF_MAX_SIZE:
|
||||
ret=snprintf(system_cmd,sizeof(system_cmd),"sysctl -w net.core.rmem_max=%d;sysctl -w net.core.rmem_default=%d",value,value);
|
||||
if (ret > 0) {
|
||||
ret=system(system_cmd);
|
||||
if (ret == -1) {
|
||||
fprintf (stderr,"[ETHERNET] Can't start shell to execute %s %s",system_cmd, strerror(errno));
|
||||
} else {
|
||||
printf ("[ETHERNET] status of %s is %d\n", system_cmd, WEXITSTATUS(ret));
|
||||
}
|
||||
printf("[ETHERNET] net core rmem %s\n",system_cmd);
|
||||
} else {
|
||||
perror("[ETHERNET] Can't set net core rmem\n");
|
||||
}
|
||||
break;
|
||||
case KERNEL_SND_BUF_MAX_SIZE:
|
||||
ret=snprintf(system_cmd,sizeof(system_cmd),"sysctl -w net.core.wmem_max=%d;sysctl -w net.core.wmem_default=%d;sysctl -w net.core.netdev_max_backlog=5000;sysctl -w net.core.optmem_max=524288;",value,value);
|
||||
if (ret > 0) {
|
||||
ret=system(system_cmd);
|
||||
if (ret == -1) {
|
||||
fprintf (stderr,"[ETHERNET] Can't start shell to execute %s %s",system_cmd, strerror(errno));
|
||||
} else {
|
||||
printf ("[ETHERNET] status of %s is %d\n", system_cmd, WEXITSTATUS(ret));
|
||||
}
|
||||
printf("[ETHERNET] net core wmem %s\n",system_cmd);
|
||||
} else {
|
||||
perror("[ETHERNET] Can't set net core wmem\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int transport_init(openair0_device *device,
|
||||
openair0_config_t *openair0_cfg,
|
||||
eth_params_t * eth_params )
|
||||
{
|
||||
eth_state_t *eth = (eth_state_t*)malloc(sizeof(eth_state_t));
|
||||
memset(eth, 0, sizeof(eth_state_t));
|
||||
|
||||
eth->flags = eth_params->transp_preference;
|
||||
|
||||
// load third-party driver
|
||||
if (eth->flags == ETH_UDP_IF5_ECPRI_MODE) load_lib(device,openair0_cfg,eth_params,RAU_REMOTE_THIRDPARTY_RADIO_HEAD);
|
||||
|
||||
|
||||
if (eth_params->if_compress == 0) {
|
||||
eth->compression = NO_COMPRESS;
|
||||
} else if (eth_params->if_compress == 1) {
|
||||
eth->compression = ALAW_COMPRESS;
|
||||
} else {
|
||||
printf("transport_init: Unknown compression scheme %d - default to ALAW", eth_params->if_compress);
|
||||
eth->compression = ALAW_COMPRESS;
|
||||
}
|
||||
|
||||
eth->num_fd = 1;
|
||||
|
||||
printf("[ETHERNET]: Initializing openair0_device for %s ...\n", ((device->host_type == RAU_HOST) ? "RAU": "RRU"));
|
||||
printf("[ETHERNET]: num_fd %d\n",eth->num_fd);
|
||||
device->Mod_id = 0;//num_devices_eth++;
|
||||
device->transp_type = ETHERNET_TP;
|
||||
device->trx_start_func = trx_eth_start;
|
||||
device->trx_get_stats_func = trx_eth_get_stats;
|
||||
device->trx_reset_stats_func = trx_eth_reset_stats;
|
||||
device->trx_end_func = trx_eth_end;
|
||||
device->trx_stop_func = trx_eth_stop;
|
||||
device->trx_set_freq_func = trx_eth_set_freq;
|
||||
device->trx_set_gains_func = trx_eth_set_gains;
|
||||
device->trx_write_init = trx_eth_write_init;
|
||||
|
||||
device->trx_read_func2 = NULL;
|
||||
device->trx_read_func = NULL;
|
||||
device->trx_write_func2 = NULL;
|
||||
device->trx_write_func = NULL;
|
||||
|
||||
if (eth->flags == ETH_RAW_MODE) {
|
||||
device->trx_write_func = trx_eth_write_raw;
|
||||
device->trx_read_func = trx_eth_read_raw;
|
||||
} else if (eth->flags == ETH_UDP_MODE || eth->flags == ETH_UDP_IF5_ECPRI_MODE) {
|
||||
device->trx_write_func2 = trx_eth_write_udp;
|
||||
device->trx_read_func2 = trx_eth_read_udp;
|
||||
device->trx_ctlsend_func = trx_eth_ctlsend_udp;
|
||||
device->trx_ctlrecv_func = trx_eth_ctlrecv_udp;
|
||||
memset((void*)&device->fhstate,0,sizeof(device->fhstate));
|
||||
printf("Setting %d RX channels to waiting\n",openair0_cfg->rx_num_channels);
|
||||
for (int i=openair0_cfg->rx_num_channels;i<8;i++) device->fhstate.r[i] = 1;
|
||||
} else if (eth->flags == ETH_RAW_IF4p5_MODE) {
|
||||
device->trx_write_func = trx_eth_write_raw_IF4p5;
|
||||
device->trx_read_func = trx_eth_read_raw_IF4p5;
|
||||
} else if (eth->flags == ETH_UDP_IF4p5_MODE) {
|
||||
device->trx_write_func = trx_eth_write_udp_IF4p5;
|
||||
device->trx_read_func = trx_eth_read_udp_IF4p5;
|
||||
device->trx_ctlsend_func = trx_eth_ctlsend_udp;
|
||||
device->trx_ctlrecv_func = trx_eth_ctlrecv_udp;
|
||||
} else {
|
||||
//device->trx_write_func = trx_eth_write_udp_IF4p5;
|
||||
//device->trx_read_func = trx_eth_read_udp_IF4p5;
|
||||
}
|
||||
|
||||
eth->if_name = eth_params->local_if_name;
|
||||
device->priv = eth;
|
||||
|
||||
|
||||
/* device specific */
|
||||
// openair0_cfg[0].iq_rxrescale = 15;//rescale iqs
|
||||
// openair0_cfg[0].iq_txshift = eth_params->iq_txshift;// shift
|
||||
// openair0_cfg[0].tx_sample_advance = eth_params->tx_sample_advance;
|
||||
|
||||
/* RRU does not have any information to make this configuration atm */
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
|
||||
switch ((int)openair0_cfg[0].sample_rate) {
|
||||
case 30720000:
|
||||
openair0_cfg[0].samples_per_packet = 3840;
|
||||
break;
|
||||
case 23040000:
|
||||
openair0_cfg[0].samples_per_packet = 2880;
|
||||
break;
|
||||
case 15360000:
|
||||
openair0_cfg[0].samples_per_packet = 1920;
|
||||
break;
|
||||
case 7680000:
|
||||
openair0_cfg[0].samples_per_packet = 960;
|
||||
break;
|
||||
case 1920000:
|
||||
openair0_cfg[0].samples_per_packet = 240;
|
||||
break;
|
||||
default:
|
||||
printf("Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
|
||||
exit(-1);
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
|
||||
device->openair0_cfg=&openair0_cfg[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************************************************************
|
||||
* DEBUGING-RELATED FUNCTIONS *
|
||||
**************************************************************************************************************************/
|
||||
void dump_packet(char *title,
|
||||
unsigned char* pkt,
|
||||
int bytes,
|
||||
unsigned int tx_rx_flag)
|
||||
{
|
||||
static int numSend = 1;
|
||||
static int numRecv = 1;
|
||||
int num, k;
|
||||
char tmp[48];
|
||||
unsigned short int cksum;
|
||||
|
||||
num = (tx_rx_flag)? numSend++:numRecv++;
|
||||
for (k = 0; k < 24; k++) sprintf(tmp+k, "%02X", pkt[k]);
|
||||
cksum = calc_csum((unsigned short *)pkt, bytes>>2);
|
||||
printf("%s-%s (%06d): %s 0x%04X\n", title,(tx_rx_flag)? "TX":"RX", num, tmp, cksum);
|
||||
}
|
||||
|
||||
|
||||
unsigned short calc_csum (unsigned short *buf,
|
||||
int nwords)
|
||||
{
|
||||
unsigned long sum;
|
||||
for (sum = 0; nwords > 0; nwords--)
|
||||
sum += *buf++;
|
||||
sum = (sum >> 16) + (sum & 0xffff);
|
||||
sum += (sum >> 16);
|
||||
return ~sum;
|
||||
}
|
||||
|
||||
|
||||
void dump_dev(openair0_device *device)
|
||||
{
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
|
||||
printf("Ethernet device interface %i configuration:\n" ,device->openair0_cfg->Mod_id);
|
||||
printf(" Log level is %i :\n" ,device->openair0_cfg->log_level);
|
||||
printf(" RB number: %i, sample rate: %lf \n" ,
|
||||
device->openair0_cfg->num_rb_dl, device->openair0_cfg->sample_rate);
|
||||
printf(" RAU configured for %i tx/%i rx channels)\n",
|
||||
device->openair0_cfg->tx_num_channels,device->openair0_cfg->rx_num_channels);
|
||||
printf(" Running flags: %s %s (\n",
|
||||
((eth->flags & ETH_RAW_MODE) ? "RAW socket mode - ":""),
|
||||
((eth->flags & ETH_UDP_MODE) ? "UDP socket mode - ":""));
|
||||
printf(" Number of iqs dumped when displaying packets: %i\n\n",eth->iqdumpcnt);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void inline dump_txcounters(openair0_device *device)
|
||||
{
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
printf(" Ethernet device interface %i, tx counters:\n" ,device->openair0_cfg->Mod_id);
|
||||
printf(" Sent packets: %llu send errors: %i\n", (long long unsigned int)eth->tx_count, eth->num_tx_errors);
|
||||
}
|
||||
|
||||
|
||||
void inline dump_rxcounters(openair0_device *device)
|
||||
{
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
printf(" Ethernet device interface %i rx counters:\n" ,device->openair0_cfg->Mod_id);
|
||||
printf(" Received packets: %llu missed packets errors: %i\n", (long long unsigned int)eth->rx_count, eth->num_underflows);
|
||||
}
|
||||
|
||||
|
||||
void inline dump_buff(openair0_device *device,
|
||||
char *buff,
|
||||
unsigned int tx_rx_flag,
|
||||
int nsamps)
|
||||
{
|
||||
char *strptr;
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
/*need to add ts number of iqs in printf need to fix dump iqs call */
|
||||
strptr = (( tx_rx_flag == TX_FLAG) ? "TX" : "RX");
|
||||
printf("\n %s, nsamps=%i \n" ,strptr,nsamps);
|
||||
|
||||
if (tx_rx_flag == 1) {
|
||||
dump_txcounters(device);
|
||||
printf(" First %i iqs of TX buffer\n",eth->iqdumpcnt);
|
||||
dump_iqs(buff,eth->iqdumpcnt);
|
||||
} else {
|
||||
dump_rxcounters(device);
|
||||
printf(" First %i iqs of RX buffer\n",eth->iqdumpcnt);
|
||||
dump_iqs(buff,eth->iqdumpcnt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void dump_iqs(char * buff,
|
||||
int iq_cnt)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<iq_cnt; i++) {
|
||||
printf("s%02i: Q=%+ij I=%+i%s",i,
|
||||
((iqoai_t *)(buff))[i].q,
|
||||
((iqoai_t *)(buff))[i].i,
|
||||
((i+1)%3 == 0) ? "\n" : " ");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file ethernet_lib.h
|
||||
* \brief API to stream I/Q samples over standard ethernet
|
||||
* \author Katerina Trilyraki, Navid Nikaein
|
||||
* \date 2015
|
||||
* \version 0.2
|
||||
* \company Eurecom
|
||||
* \maintainer: navid.nikaein@eurecom.fr
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
#ifndef ETHERNET_LIB_H
|
||||
#define ETHERNET_LIB_H
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/ether.h>
|
||||
#include <common/utils/threadPool/thread-pool.h>
|
||||
|
||||
#define MAX_INST 4
|
||||
#define DEFAULT_IF "lo"
|
||||
|
||||
#define TX_FLAG 1
|
||||
#define RX_FLAG 0
|
||||
|
||||
#include "if_defs.h"
|
||||
#define ECPRICOMMON_BYTES 4
|
||||
#define ECPRIPCID_BYTES 2
|
||||
#define APP_HEADER_SIZE_BYTES (ECPRICOMMON_BYTES + ECPRIPCID_BYTES + sizeof(openair0_timestamp))
|
||||
#define ECPRIREV 1 // ECPRI Version 1, C=0 - single ECPRI message per OAI TX packet
|
||||
|
||||
/*!\brief opaque ethernet data structure */
|
||||
typedef struct {
|
||||
|
||||
/*!\brief socket file desc (control)*/
|
||||
int sockfdc;
|
||||
/*!\brief number of sockets for user-plane*/
|
||||
int num_fd;
|
||||
/*!\brief socket file desc (user)*/
|
||||
int sockfdd[8];
|
||||
/*!\brief interface name */
|
||||
char *if_name;
|
||||
/*!\brief buffer size */
|
||||
unsigned int buffer_size;
|
||||
/*!\brief Fronthaul state */
|
||||
fhstate_t *fhstate;
|
||||
/*!\brief destination address (control) for UDP socket*/
|
||||
struct sockaddr_in dest_addrc;
|
||||
/*!\brief local address (control) for UDP socket*/
|
||||
struct sockaddr_in local_addrc;
|
||||
/*!\brief destination address (user) for UDP socket*/
|
||||
struct sockaddr_in dest_addrd;
|
||||
/*!\brief local address (user) for UDP socket*/
|
||||
struct sockaddr_in local_addrd;
|
||||
/*!\brief address length for both UDP and RAW socket*/
|
||||
int addr_len;
|
||||
/*!\brief destination address (control) for RAW socket*/
|
||||
struct sockaddr_ll dest_addrc_ll;
|
||||
/*!\brief local address (control) for RAW socket*/
|
||||
struct sockaddr_ll local_addrc_ll;
|
||||
/*!\brief destination address (user) for RAW socket*/
|
||||
struct sockaddr_ll dest_addrd_ll;
|
||||
/*!\brief local address (user) for RAW socket*/
|
||||
struct sockaddr_ll local_addrd_ll;
|
||||
/*!\brief inteface index for RAW socket*/
|
||||
struct ifreq if_index;
|
||||
/*!\brief timeout ms */
|
||||
unsigned int rx_timeout_ms;
|
||||
/*!\brief timeout ms */
|
||||
unsigned int tx_timeout_ms;
|
||||
/*!\brief runtime flags */
|
||||
uint32_t flags;
|
||||
/*!\compression enalbe */
|
||||
uint32_t compression;
|
||||
/*!\ time offset between transmiter timestamp and receiver timestamp */
|
||||
double tdiff;
|
||||
/*!\ calibration */
|
||||
int tx_forward_nsamps;
|
||||
|
||||
// --------------------------------
|
||||
// Debug and output control
|
||||
// --------------------------------
|
||||
|
||||
/*!\brief number of I/Q samples to be printed */
|
||||
int iqdumpcnt;
|
||||
|
||||
/*!\brief number of underflows in interface */
|
||||
int num_underflows;
|
||||
/*!\brief number of overflows in interface */
|
||||
int num_overflows;
|
||||
/*!\brief number of concesutive errors in interface */
|
||||
int num_seq_errors;
|
||||
/*!\brief number of errors in interface's receiver */
|
||||
int num_rx_errors;
|
||||
/*!\brief number of errors in interface's transmitter */
|
||||
int num_tx_errors;
|
||||
|
||||
/*!\brief current TX timestamp */
|
||||
openair0_timestamp tx_current_ts;
|
||||
/*!\brief socket file desc */
|
||||
openair0_timestamp rx_current_ts;
|
||||
/*!\brief actual number of samples transmitted */
|
||||
uint64_t tx_actual_nsamps;
|
||||
/*!\brief actual number of samples received */
|
||||
uint64_t rx_actual_nsamps;
|
||||
/*!\brief number of samples to be transmitted */
|
||||
uint64_t tx_nsamps;
|
||||
/*!\brief number of samples to be received */
|
||||
uint64_t rx_nsamps;
|
||||
/*!\brief number of packets transmitted */
|
||||
uint64_t tx_count;
|
||||
/*!\brief number of packets received */
|
||||
uint64_t rx_count;
|
||||
/*!\brief TX sequence number*/
|
||||
uint16_t pck_seq_num;
|
||||
/*!\brief Current RX sequence number*/
|
||||
uint16_t pck_seq_num_cur;
|
||||
/*!\brief Previous RX sequence number */
|
||||
uint16_t pck_seq_num_prev;
|
||||
/*!\brief precomputed ethernet header (control) */
|
||||
struct ether_header ehc;
|
||||
/*!\brief precomputed ethernet header (data) */
|
||||
struct ether_header ehd;
|
||||
} eth_state_t;
|
||||
|
||||
|
||||
|
||||
/*!\brief packet header */
|
||||
typedef struct {
|
||||
/*!\brief packet sequence number max value=packets per frame*/
|
||||
uint16_t seq_num ;
|
||||
/*!\brief antenna port used to resynchronize */
|
||||
uint16_t antenna_id;
|
||||
/*!\brief packet's timestamp */
|
||||
openair0_timestamp timestamp;
|
||||
} header_t;
|
||||
|
||||
/*!\brief different options for ethernet tuning in socket and driver level */
|
||||
typedef enum {
|
||||
MIN_OPT = 0,
|
||||
/*!\brief socket send buffer size in bytes */
|
||||
SND_BUF_SIZE,
|
||||
/*!\brief socket receive buffer size in bytes */
|
||||
RCV_BUF_SIZE,
|
||||
/*!\brief receiving timeout */
|
||||
RCV_TIMEOUT,
|
||||
/*!\brief sending timeout */
|
||||
SND_TIMEOUT,
|
||||
/*!\brief maximun transmission unit size in bytes */
|
||||
MTU_SIZE,
|
||||
/*!\brief TX queue length */
|
||||
TX_Q_LEN,
|
||||
/*!\brief RX/TX ring parameters of ethernet device */
|
||||
RING_PAR,
|
||||
/*!\brief interruptions coalesence mechanism of ethernet device */
|
||||
COALESCE_PAR,
|
||||
/*!\brief pause parameters of ethernet device */
|
||||
PAUSE_PAR,
|
||||
/*!\brief kernel network receive buffer maximun size */
|
||||
KERNEL_RCV_BUF_MAX_SIZE,
|
||||
/*!\brief kernel network send buffer maximun size */
|
||||
KERNEL_SND_BUF_MAX_SIZE,
|
||||
MAX_OPT
|
||||
} eth_opt_t;
|
||||
|
||||
|
||||
/*
|
||||
#define SND_BUF_SIZE 1
|
||||
#define RCV_BUF_SIZE 1<<1
|
||||
#define SND_TIMEOUT 1<<2
|
||||
#define RCV_TIMEOUT 1<<3
|
||||
#define MTU_SIZE 1<<4
|
||||
#define TX_Q_LEN 1<<5
|
||||
#define RING_PAR 1<<5
|
||||
#define COALESCE_PAR 1<<6
|
||||
#define PAUSE_PAR 1<<7
|
||||
*/
|
||||
|
||||
/*!\brief I/Q samples */
|
||||
typedef struct {
|
||||
/*!\brief phase */
|
||||
short i;
|
||||
/*!\brief quadrature */
|
||||
short q;
|
||||
} iqoai_t ;
|
||||
|
||||
typedef struct udpTXelem_s {
|
||||
openair0_device *device;
|
||||
openair0_timestamp timestamp;
|
||||
void **buff;
|
||||
int fd_ind;
|
||||
int nant;
|
||||
int nsamps;
|
||||
int flags;
|
||||
} udpTXelem_t;
|
||||
|
||||
struct udpTXReqId {
|
||||
uint64_t TS;
|
||||
int aid;
|
||||
int length;
|
||||
uint16_t spare;
|
||||
} __attribute__((packed));
|
||||
|
||||
union udpTXReqUnion {
|
||||
struct udpTXReqId s;
|
||||
uint64_t p;
|
||||
};
|
||||
|
||||
void dump_packet(char *title, unsigned char* pkt, int bytes, unsigned int tx_rx_flag);
|
||||
unsigned short calc_csum (unsigned short *buf, int nwords);
|
||||
void dump_dev(openair0_device *device);
|
||||
/*void inline dump_buff(openair0_device *device, char *buff,unsigned int tx_rx_flag,int nsamps);
|
||||
void inline dump_rxcounters(openair0_device *device);
|
||||
void inline dump_txcounters(openair0_device *device);
|
||||
*/
|
||||
void dump_iqs(char * buff, int iq_cnt);
|
||||
|
||||
void *udp_read_thread(void *arg);
|
||||
void *udp_write_thread(void *arg);
|
||||
|
||||
/*! \fn int ethernet_tune (openair0_device *device, unsigned int option, int value);
|
||||
* \brief this function allows you to configure certain ethernet parameters in socket or device level
|
||||
* \param[in] openair0 device which bears the socket
|
||||
* \param[in] name of parameter to configure
|
||||
* \return 0 on success, otherwise -1
|
||||
* \note
|
||||
* @ingroup _oai
|
||||
*/
|
||||
int ethernet_tune(openair0_device *device, unsigned int option, int value);
|
||||
|
||||
|
||||
|
||||
/*! \fn int eth_socket_init_udp(openair0_device *device)
|
||||
* \brief initialization of UDP Socket to communicate with one destination
|
||||
* \param[in] *device openair device for which the socket will be created
|
||||
* \param[out]
|
||||
* \return 0 on success, otherwise -1
|
||||
* \note
|
||||
* @ingroup _oai
|
||||
*/
|
||||
int eth_socket_init_udp(openair0_device *device);
|
||||
int trx_eth_write_udp(openair0_device *device, openair0_timestamp timestamp, void **buf, int fd_ind, int nsamps, int flags,int nant);
|
||||
int trx_eth_read_udp(openair0_device *device, openair0_timestamp *timestamp, uint32_t **buff, int nsamps);
|
||||
|
||||
|
||||
int eth_socket_init_raw(openair0_device *device);
|
||||
int trx_eth_write_raw(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int cc, int flags);
|
||||
int trx_eth_read_raw(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc);
|
||||
int trx_eth_write_raw_IF4p5(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int cc, int flags);
|
||||
int trx_eth_read_raw_IF4p5(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc);
|
||||
int trx_eth_read_raw_IF5_mobipass(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc);
|
||||
int trx_eth_write_udp_IF4p5(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int cc, int flags);
|
||||
int trx_eth_read_udp_IF4p5(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc);
|
||||
int trx_eth_ctlsend_udp(openair0_device *device, void *msg, ssize_t msg_len);
|
||||
int trx_eth_ctlrecv_udp(openair0_device *device, void *msg, ssize_t msg_len);
|
||||
|
||||
int eth_get_dev_conf_raw(openair0_device *device);
|
||||
int eth_set_dev_conf_raw(openair0_device *device);
|
||||
int eth_get_dev_conf_raw_IF4p5(openair0_device *device);
|
||||
int eth_set_dev_conf_raw_IF4p5(openair0_device *device);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file sdr/ETHERNET/USERSPACE/LIB/if_defs.h
|
||||
* \brief
|
||||
* \author S. Sandeep Kumar, Raymond Knopp
|
||||
* \date 2016
|
||||
* \version 0.1
|
||||
* \company Eurecom
|
||||
* \email: ee13b1025@iith.ac.in, knopp@eurecom.fr
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
|
||||
#ifndef __ETHERNET_USERSPACE_LIB_IF_DEFS__H__
|
||||
#define __ETHERNET_USERSPACE_LIB_IF_DEFS__H__
|
||||
|
||||
#include <netinet/ether.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef LITE_COMPILATION
|
||||
#include "PHY/LTE_TRANSPORT/if4_tools.h"
|
||||
#endif
|
||||
|
||||
// ETH transport preference modes
|
||||
#define ETH_UDP_MODE 0
|
||||
#define ETH_RAW_MODE 1
|
||||
#define ETH_UDP_IF4p5_MODE 2
|
||||
#define ETH_RAW_IF4p5_MODE 3
|
||||
#define ETH_UDP_IF5_ECPRI_MODE 4
|
||||
|
||||
// COMMOM HEADER LENGTHS
|
||||
|
||||
#define NO_COMPRESS 0
|
||||
#define ALAW_COMPRESS 1
|
||||
|
||||
#define UDP_HEADER_SIZE_BYTES 8
|
||||
#define IPV4_HEADER_SIZE_BYTES 60 // This is the maximum IPv4 header length
|
||||
|
||||
// Time domain RRH packet sizes
|
||||
#define MAC_HEADER_SIZE_BYTES (sizeof(struct ether_header))
|
||||
#define MAX_PACKET_SEQ_NUM(spp,spf) (spf/spp)
|
||||
#define PAYLOAD_SIZE_BYTES(nsamps) (nsamps<<2)
|
||||
#define UDP_PACKET_SIZE_BYTES(nsamps) (APP_HEADER_SIZE_BYTES + PAYLOAD_SIZE_BYTES(nsamps))
|
||||
#define RAW_PACKET_SIZE_BYTES(nsamps) (APP_HEADER_SIZE_BYTES + MAC_HEADER_SIZE_BYTES + PAYLOAD_SIZE_BYTES(nsamps))
|
||||
|
||||
#define PAYLOAD_SIZE_BYTES_ALAW(nsamps) (nsamps<<1)
|
||||
#define UDP_PACKET_SIZE_BYTES_ALAW(nsamps) (APP_HEADER_SIZE_BYTES + PAYLOAD_SIZE_BYTES_ALAW(nsamps))
|
||||
#define RAW_PACKET_SIZE_BYTES_ALAW(nsamps) (APP_HEADER_SIZE_BYTES + MAC_HEADER_SIZE_BYTES + PAYLOAD_SIZE_BYTES_ALAW(nsamps))
|
||||
|
||||
// Packet sizes for IF4p5 interface format
|
||||
#define DATA_BLOCK_SIZE_BYTES(scaled_nblocks) (sizeof(uint16_t)*scaled_nblocks)
|
||||
#define PRACH_NUM_SAMPLES (839*2)
|
||||
#define PRACH_BLOCK_SIZE_BYTES (sizeof(int16_t)*PRACH_NUM_SAMPLES)
|
||||
|
||||
#define RAW_IF4p5_PDLFFT_SIZE_BYTES(nblocks) (MAC_HEADER_SIZE_BYTES + sizeof_IF4p5_header_t + DATA_BLOCK_SIZE_BYTES(nblocks))
|
||||
#define RAW_IF4p5_PULFFT_SIZE_BYTES(nblocks) (MAC_HEADER_SIZE_BYTES + sizeof_IF4p5_header_t + DATA_BLOCK_SIZE_BYTES(nblocks))
|
||||
#define RAW_IF4p5_PULTICK_SIZE_BYTES (MAC_HEADER_SIZE_BYTES + sizeof_IF4p5_header_t)
|
||||
#define RAW_IF4p5_PRACH_SIZE_BYTES (MAC_HEADER_SIZE_BYTES + sizeof_IF4p5_header_t + PRACH_BLOCK_SIZE_BYTES)
|
||||
#define UDP_IF4p5_PDLFFT_SIZE_BYTES(nblocks) (UDP_HEADER_SIZE_BYTES + IPV4_HEADER_SIZE_BYTES + sizeof_IF4p5_header_t + DATA_BLOCK_SIZE_BYTES(nblocks))
|
||||
#define UDP_IF4p5_PULFFT_SIZE_BYTES(nblocks) (UDP_HEADER_SIZE_BYTES + IPV4_HEADER_SIZE_BYTES + sizeof_IF4p5_header_t + DATA_BLOCK_SIZE_BYTES(nblocks))
|
||||
#define UDP_IF4p5_PULTICK_SIZE_BYTES (UDP_HEADER_SIZE_BYTES + IPV4_HEADER_SIZE_BYTES + sizeof_IF4p5_header_t)
|
||||
#define UDP_IF4p5_PRACH_SIZE_BYTES (UDP_HEADER_SIZE_BYTES + IPV4_HEADER_SIZE_BYTES + sizeof_IF4p5_header_t + PRACH_BLOCK_SIZE_BYTES)
|
||||
|
||||
// Mobipass packet sizes
|
||||
#define RAW_IF5_MOBIPASS_BLOCK_SIZE_BYTES 1280
|
||||
#define RAW_IF5_MOBIPASS_SIZE_BYTES (MAC_HEADER_SIZE_BYTES + sizeof_IF5_mobipass_header_t + RAW_IF5_MOBIPASS_BLOCK_SIZE_BYTES)
|
||||
#define PAYLOAD_MOBIPASS_NUM_SAMPLES 640
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common_lib.h"
|
||||
#include "ethernet_lib.h"
|
||||
#include "shared_buffers.h"
|
||||
#include "low.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "common/utils/LOG/vcd_signal_dumper.h"
|
||||
|
||||
typedef struct {
|
||||
eth_state_t e;
|
||||
shared_buffers buffers;
|
||||
rru_config_msg_type_t last_msg;
|
||||
int capabilities_sent;
|
||||
void *benetel_priv;
|
||||
char *dpdk_main_command_line;
|
||||
} benetel_eth_state_t;
|
||||
|
||||
int trx_benetel_start(openair0_device *device)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void trx_benetel_end(openair0_device *device)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
|
||||
int trx_benetel_stop(openair0_device *device)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int trx_benetel_set_freq(openair0_device* device,
|
||||
openair0_config_t *openair0_cfg,
|
||||
int exmimo_dump_config)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int trx_benetel_set_gains(openair0_device* device,
|
||||
openair0_config_t *openair0_cfg)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int trx_benetel_get_stats(openair0_device* device)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int trx_benetel_reset_stats(openair0_device* device)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int ethernet_tune(openair0_device *device,
|
||||
unsigned int option,
|
||||
int value)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trx_benetel_write_raw(openair0_device *device,
|
||||
openair0_timestamp timestamp,
|
||||
void **buff, int nsamps, int cc, int flags)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return nsamps*4;
|
||||
}
|
||||
|
||||
int trx_benetel_read_raw(openair0_device *device,
|
||||
openair0_timestamp *timestamp,
|
||||
void **buff, int nsamps, int cc)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return nsamps*4;
|
||||
}
|
||||
|
||||
char *msg_type(int t)
|
||||
{
|
||||
static char *s[12] = {
|
||||
"RAU_tick",
|
||||
"RRU_capabilities",
|
||||
"RRU_config",
|
||||
"RRU_config_ok",
|
||||
"RRU_start",
|
||||
"RRU_stop",
|
||||
"RRU_sync_ok",
|
||||
"RRU_frame_resynch",
|
||||
"RRU_MSG_max_num",
|
||||
"RRU_check_sync",
|
||||
"RRU_config_update",
|
||||
"RRU_config_update_ok",
|
||||
};
|
||||
|
||||
if (t < 0 || t > 11) return "UNKNOWN";
|
||||
return s[t];
|
||||
}
|
||||
|
||||
int trx_benetel_ctlsend(openair0_device *device, void *msg, ssize_t msg_len)
|
||||
{
|
||||
RRU_CONFIG_msg_t *rru_config_msg = msg;
|
||||
benetel_eth_state_t *s = device->priv;
|
||||
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
|
||||
printf(" rru_config_msg->type %d [%s]\n", rru_config_msg->type,
|
||||
msg_type(rru_config_msg->type));
|
||||
|
||||
s->last_msg = rru_config_msg->type;
|
||||
|
||||
return msg_len;
|
||||
}
|
||||
|
||||
int trx_benetel_ctlrecv(openair0_device *device, void *msg, ssize_t msg_len)
|
||||
{
|
||||
RRU_CONFIG_msg_t *rru_config_msg = msg;
|
||||
benetel_eth_state_t *s = device->priv;
|
||||
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
|
||||
if (s->last_msg == RAU_tick && s->capabilities_sent == 0) {
|
||||
RRU_capabilities_t *cap;
|
||||
rru_config_msg->type = RRU_capabilities;
|
||||
rru_config_msg->len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_capabilities_t);
|
||||
cap = (RRU_capabilities_t*)&rru_config_msg->msg[0];
|
||||
cap->FH_fmt = OAI_IF4p5_only;
|
||||
cap->num_bands = 1;
|
||||
cap->band_list[0] = 7;
|
||||
cap->nb_rx[0] = device->openair0_cfg->rx_num_channels;
|
||||
cap->nb_tx[0] = device->openair0_cfg->tx_num_channels;
|
||||
cap->max_pdschReferenceSignalPower[0] = -27;
|
||||
cap->max_rxgain[0] = 90;
|
||||
|
||||
s->capabilities_sent = 1;
|
||||
|
||||
return rru_config_msg->len;
|
||||
}
|
||||
if (s->last_msg == RRU_config) {
|
||||
rru_config_msg->type = RRU_config_ok;
|
||||
return 0;
|
||||
}
|
||||
if (s->last_msg == RRU_start) {
|
||||
s->benetel_priv = benetel_start(s->e.if_name, &s->buffers, s->dpdk_main_command_line);
|
||||
}
|
||||
/* correct? */
|
||||
while (1) pause();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void benetel_fh_if4p5_south_in(RU_t *ru,
|
||||
int *frame,
|
||||
int *subframe)
|
||||
{
|
||||
benetel_eth_state_t *s = ru->ifdevice.priv;
|
||||
PHY_VARS_eNB **eNB_list = ru->eNB_list, *eNB;
|
||||
LTE_DL_FRAME_PARMS *fp;
|
||||
int symbol;
|
||||
int32_t *rxdata;
|
||||
int antenna;
|
||||
|
||||
lock_ul_buffer(&s->buffers, *subframe);
|
||||
next:
|
||||
while (!((s->buffers.ul_busy[0][*subframe] == 0x3fff &&
|
||||
s->buffers.ul_busy[1][*subframe] == 0x3fff) ||
|
||||
s->buffers.prach_busy[*subframe] == 1))
|
||||
wait_ul_buffer(&s->buffers, *subframe);
|
||||
if (s->buffers.prach_busy[*subframe] == 1) {
|
||||
int i;
|
||||
int antenna = 0;
|
||||
uint16_t *in;
|
||||
uint16_t *out;
|
||||
in = (uint16_t *)s->buffers.prach[*subframe];
|
||||
out = (uint16_t *)ru->prach_rxsigF[0][antenna];
|
||||
for (i = 0; i < 840*2; i++)
|
||||
out[i] = ntohs(in[i]);
|
||||
s->buffers.prach_busy[*subframe] = 0;
|
||||
ru->wakeup_prach_eNB(ru->eNB_list[0], ru, *frame, *subframe);
|
||||
goto next;
|
||||
}
|
||||
|
||||
eNB = eNB_list[0];
|
||||
fp = &eNB->frame_parms;
|
||||
for (antenna = 0; antenna < ru->nb_rx; antenna++) {
|
||||
for (symbol = 0; symbol < 14; symbol++) {
|
||||
int i;
|
||||
uint16_t *p = (uint16_t *)(&s->buffers.ul[antenna][*subframe][symbol*1200*4]);
|
||||
for (i = 0; i < 1200*2; i++) {
|
||||
p[i] = htons(p[i]);
|
||||
}
|
||||
rxdata = &ru->common.rxdataF[antenna][symbol * fp->ofdm_symbol_size];
|
||||
#if 1
|
||||
memcpy(rxdata + 2048 - 600,
|
||||
&s->buffers.ul[antenna][*subframe][symbol*1200*4],
|
||||
600 * 4);
|
||||
memcpy(rxdata,
|
||||
&s->buffers.ul[antenna][*subframe][symbol*1200*4] + 600*4,
|
||||
600 * 4);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
s->buffers.ul_busy[0][*subframe] = 0;
|
||||
s->buffers.ul_busy[1][*subframe] = 0;
|
||||
signal_ul_buffer(&s->buffers, *subframe);
|
||||
unlock_ul_buffer(&s->buffers, *subframe);
|
||||
|
||||
//printf("BENETEL: %s (f.sf %d.%d)\n", __FUNCTION__, *frame, *subframe);
|
||||
|
||||
RU_proc_t *proc = &ru->proc;
|
||||
extern uint16_t sf_ahead;
|
||||
int f = *frame;
|
||||
int sf = *subframe;
|
||||
|
||||
//calculate timestamp_rx, timestamp_tx based on frame and subframe
|
||||
proc->tti_rx = sf;
|
||||
proc->frame_rx = f;
|
||||
proc->timestamp_rx = ((proc->frame_rx * 10) + proc->tti_rx ) * fp->samples_per_tti ;
|
||||
|
||||
if (get_nprocs()<=4) {
|
||||
proc->tti_tx = (sf+sf_ahead)%10;
|
||||
proc->frame_tx = (sf>(9-sf_ahead)) ? (f+1)&1023 : f;
|
||||
}
|
||||
|
||||
VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_IF4P5_SOUTH_IN_RU+ru->idx,f);
|
||||
VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_SUBFRAME_NUMBER_IF4P5_SOUTH_IN_RU+ru->idx,sf);
|
||||
proc->symbol_mask[sf] = 0;
|
||||
VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, proc->timestamp_rx&0xffffffff);
|
||||
|
||||
}
|
||||
|
||||
void benetel_fh_if4p5_south_out(RU_t *ru,
|
||||
int frame,
|
||||
int subframe,
|
||||
uint64_t timestamp)
|
||||
{
|
||||
benetel_eth_state_t *s = ru->ifdevice.priv;
|
||||
PHY_VARS_eNB **eNB_list = ru->eNB_list, *eNB;
|
||||
LTE_DL_FRAME_PARMS *fp;
|
||||
int symbol;
|
||||
int32_t *txdata;
|
||||
int aa;
|
||||
|
||||
//printf("BENETEL: %s (f.sf %d.%d ts %ld)\n", __FUNCTION__, frame, subframe, timestamp);
|
||||
|
||||
VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx&0xffffffff );
|
||||
|
||||
lock_dl_buffer(&s->buffers, subframe);
|
||||
if (s->buffers.dl_busy[0][subframe] ||
|
||||
s->buffers.dl_busy[1][subframe]) {
|
||||
printf("%s: fatal: DL buffer busy for subframe %d\n", __FUNCTION__, subframe);
|
||||
unlock_dl_buffer(&s->buffers, subframe);
|
||||
return;
|
||||
}
|
||||
eNB = eNB_list[0];
|
||||
fp = &eNB->frame_parms;
|
||||
if (ru->num_eNB != 1 || fp->ofdm_symbol_size != 2048 ||
|
||||
fp->Ncp != NORMAL || fp->symbols_per_tti != 14) {
|
||||
printf("%s:%d:%s: unsupported configuration\n",
|
||||
__FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (aa = 0; aa < ru->nb_tx; aa++) {
|
||||
for (symbol = 0; symbol < 14; symbol++) {
|
||||
txdata = &ru->common.txdataF_BF[aa][symbol * fp->ofdm_symbol_size];
|
||||
#if 1
|
||||
memcpy(&s->buffers.dl[aa][subframe][symbol*1200*4],
|
||||
txdata + 2048 - 600,
|
||||
600 * 4);
|
||||
memcpy(&s->buffers.dl[aa][subframe][symbol*1200*4] + 600*4,
|
||||
txdata + 1,
|
||||
600 * 4);
|
||||
#endif
|
||||
int i;
|
||||
uint16_t *p = (uint16_t *)(&s->buffers.dl[aa][subframe][symbol*1200*4]);
|
||||
for (i = 0; i < 1200*2; i++) {
|
||||
p[i] = htons(p[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s->buffers.dl_busy[0][subframe] = 0x3fff;
|
||||
s->buffers.dl_busy[1][subframe] = 0x3fff;
|
||||
unlock_dl_buffer(&s->buffers, subframe);
|
||||
|
||||
VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_IF4P5_SOUTH_OUT_RU+ru->idx, ru->proc.frame_tx);
|
||||
VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_SUBFRAME_NUMBER_IF4P5_SOUTH_OUT_RU+ru->idx, ru->proc.tti_tx);
|
||||
|
||||
}
|
||||
|
||||
void *get_internal_parameter(char *name)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
|
||||
if (!strcmp(name, "fh_if4p5_south_in"))
|
||||
return benetel_fh_if4p5_south_in;
|
||||
if (!strcmp(name, "fh_if4p5_south_out"))
|
||||
return benetel_fh_if4p5_south_out;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__attribute__((__visibility__("default")))
|
||||
int transport_init(openair0_device *device,
|
||||
openair0_config_t *openair0_cfg,
|
||||
eth_params_t * eth_params )
|
||||
{
|
||||
benetel_eth_state_t *eth;
|
||||
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
|
||||
device->Mod_id = 0;
|
||||
device->transp_type = ETHERNET_TP;
|
||||
device->trx_start_func = trx_benetel_start;
|
||||
device->trx_get_stats_func = trx_benetel_get_stats;
|
||||
device->trx_reset_stats_func = trx_benetel_reset_stats;
|
||||
device->trx_end_func = trx_benetel_end;
|
||||
device->trx_stop_func = trx_benetel_stop;
|
||||
device->trx_set_freq_func = trx_benetel_set_freq;
|
||||
device->trx_set_gains_func = trx_benetel_set_gains;
|
||||
|
||||
device->trx_write_func = trx_benetel_write_raw;
|
||||
device->trx_read_func = trx_benetel_read_raw;
|
||||
|
||||
device->trx_ctlsend_func = trx_benetel_ctlsend;
|
||||
device->trx_ctlrecv_func = trx_benetel_ctlrecv;
|
||||
|
||||
device->get_internal_parameter = get_internal_parameter;
|
||||
|
||||
eth = calloc(1, sizeof(benetel_eth_state_t));
|
||||
if (eth == NULL) {
|
||||
AssertFatal(0==1, "out of memory\n");
|
||||
}
|
||||
|
||||
eth->e.flags = ETH_RAW_IF4p5_MODE;
|
||||
eth->e.compression = NO_COMPRESS;
|
||||
eth->e.if_name = eth_params->local_if_name;
|
||||
device->priv = eth;
|
||||
device->openair0_cfg=&openair0_cfg[0];
|
||||
|
||||
if (openair0_cfg->sdr_addrs == NULL) {
|
||||
printf("benetel: fatal: sdr_addrs not set in configuration file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
eth->dpdk_main_command_line = strdup(openair0_cfg->sdr_addrs);
|
||||
if (eth->dpdk_main_command_line == NULL) {
|
||||
AssertFatal(0==1, "out of memory\n");
|
||||
}
|
||||
|
||||
eth->last_msg = -1;
|
||||
|
||||
init_buffers(ð->buffers);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,774 @@
|
||||
/* license: to be defined */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/queue.h>
|
||||
#include <netinet/in.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <rte_common.h>
|
||||
#include <rte_log.h>
|
||||
#include <rte_malloc.h>
|
||||
#include <rte_memory.h>
|
||||
#include <rte_memcpy.h>
|
||||
#include <rte_eal.h>
|
||||
#include <rte_launch.h>
|
||||
#include <rte_atomic.h>
|
||||
#include <rte_cycles.h>
|
||||
#include <rte_prefetch.h>
|
||||
#include <rte_lcore.h>
|
||||
#include <rte_per_lcore.h>
|
||||
#include <rte_branch_prediction.h>
|
||||
#include <rte_interrupts.h>
|
||||
#include <rte_random.h>
|
||||
#include <rte_debug.h>
|
||||
#include <rte_ether.h>
|
||||
#include <rte_ethdev.h>
|
||||
#include <rte_mempool.h>
|
||||
#include <rte_mbuf.h>
|
||||
|
||||
#include "low.h"
|
||||
|
||||
// HEADER DEFINITION
|
||||
#define ANT_NUM buf[23]
|
||||
#define PAYLOAD_1 buf[20]
|
||||
#define PAYLOAD_2 buf[21]
|
||||
#define ETH_TYPE buf[17]
|
||||
#define SYMBOL buf[29]
|
||||
#define SUBFRAME buf[28]
|
||||
#define FRAME buf[27]
|
||||
|
||||
static volatile bool force_quit;
|
||||
|
||||
unsigned short iq[33600];
|
||||
unsigned char iq_swap[67200];
|
||||
|
||||
unsigned short *iq_ptr[14];
|
||||
unsigned int dl_start = 0;
|
||||
|
||||
/* MAC updating enabled by default */
|
||||
static int mac_updating = 1;
|
||||
#define RTE_LOGTYPE_L2FWD RTE_LOGTYPE_USER1
|
||||
#define MAX_PKT_BURST 32
|
||||
#define BURST_TX_DRAIN_US 5 /* TX drain every ~100us */
|
||||
#define MEMPOOL_CACHE_SIZE 256
|
||||
/*
|
||||
* Configurable number of RX/TX ring descriptors
|
||||
*/
|
||||
#define RTE_TEST_RX_DESC_DEFAULT 1024
|
||||
#define RTE_TEST_TX_DESC_DEFAULT 1024
|
||||
static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
|
||||
static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
|
||||
/* ethernet addresses of ports */
|
||||
static struct ether_addr l2fwd_ports_eth_addr[RTE_MAX_ETHPORTS];
|
||||
/* mask of enabled ports */
|
||||
static uint32_t l2fwd_enabled_port_mask = 0;
|
||||
/* list of enabled ports */
|
||||
static uint32_t l2fwd_dst_ports[RTE_MAX_ETHPORTS];
|
||||
static unsigned int l2fwd_rx_queue_per_lcore = 1;
|
||||
#define MAX_RX_QUEUE_PER_LCORE 16
|
||||
#define MAX_TX_QUEUE_PER_PORT 16
|
||||
struct lcore_queue_conf {
|
||||
unsigned n_rx_port;
|
||||
unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE];
|
||||
} __rte_cache_aligned;
|
||||
struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE];
|
||||
static struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS];
|
||||
static struct rte_eth_conf port_conf = {
|
||||
.rxmode = {
|
||||
.split_hdr_size = 0,
|
||||
.offloads = DEV_RX_OFFLOAD_JUMBO_FRAME,
|
||||
.split_hdr_size = 0,
|
||||
.max_rx_pkt_len = 9500,
|
||||
},
|
||||
.txmode = {
|
||||
.mq_mode = ETH_MQ_TX_NONE,
|
||||
},
|
||||
};
|
||||
struct rte_mempool * l2fwd_pktmbuf_pool = NULL;
|
||||
/* Per-port statistics struct */
|
||||
struct l2fwd_port_statistics {
|
||||
uint64_t tx;
|
||||
uint64_t rx;
|
||||
uint64_t dropped;
|
||||
} __rte_cache_aligned;
|
||||
struct l2fwd_port_statistics port_statistics[RTE_MAX_ETHPORTS];
|
||||
#define MAX_TIMER_PERIOD 86400 /* 1 day max */
|
||||
/* A tsc-based timer responsible for triggering statistics printout */
|
||||
static uint64_t timer_period = 10; /* default period is 10 seconds */
|
||||
/* Print out statistics on packets dropped */
|
||||
static void
|
||||
print_stats(void)
|
||||
{
|
||||
uint64_t total_packets_dropped, total_packets_tx, total_packets_rx;
|
||||
unsigned portid;
|
||||
total_packets_dropped = 0;
|
||||
total_packets_tx = 0;
|
||||
total_packets_rx = 0;
|
||||
const char clr[] = { 27, '[', '2', 'J', '\0' };
|
||||
const char topLeft[] = { 27, '[', '1', ';', '1', 'H','\0' };
|
||||
/* Clear screen and move to top left */
|
||||
printf("%s%s", clr, topLeft);
|
||||
printf("\nPort statistics ====================================");
|
||||
for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
|
||||
/* skip disabled ports */
|
||||
if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
|
||||
continue;
|
||||
printf("\nStatistics for port %u ------------------------------"
|
||||
"\nPackets sent: %24"PRIu64
|
||||
"\nPackets received: %20"PRIu64
|
||||
"\nPackets dropped: %21"PRIu64,
|
||||
portid,
|
||||
port_statistics[portid].tx,
|
||||
port_statistics[portid].rx,
|
||||
port_statistics[portid].dropped);
|
||||
total_packets_dropped += port_statistics[portid].dropped;
|
||||
total_packets_tx += port_statistics[portid].tx;
|
||||
total_packets_rx += port_statistics[portid].rx;
|
||||
}
|
||||
printf("\nAggregate statistics ==============================="
|
||||
"\nTotal packets sent: %18"PRIu64
|
||||
"\nTotal packets received: %14"PRIu64
|
||||
"\nTotal packets dropped: %15"PRIu64,
|
||||
total_packets_tx,
|
||||
total_packets_rx,
|
||||
total_packets_dropped);
|
||||
printf("\n====================================================\n");
|
||||
}
|
||||
static void
|
||||
l2fwd_simple_forward(struct rte_mbuf *m, unsigned portid, benetel_t *bs)
|
||||
{
|
||||
unsigned char *buf, *buf_tx;
|
||||
unsigned int len;
|
||||
struct ether_hdr *eth;
|
||||
|
||||
unsigned dst_port;
|
||||
int sent, prach_ctrl = 0;
|
||||
struct rte_eth_dev_tx_buffer *buffer;
|
||||
|
||||
unsigned char * IQ_ptr;
|
||||
|
||||
ul_packet_t p;
|
||||
|
||||
buf = rte_pktmbuf_mtod(m, unsigned char *);
|
||||
//rte_memdump(stdout, "Rx Packet",buf ,34);
|
||||
eth = rte_pktmbuf_mtod(m, struct ether_hdr *);
|
||||
len = rte_pktmbuf_data_len(m);
|
||||
|
||||
IQ_ptr = &buf[34];
|
||||
|
||||
//destination mac address (RRU MAC)
|
||||
eth->d_addr.addr_bytes[0] = 0x02;
|
||||
eth->d_addr.addr_bytes[1] = 0x00;
|
||||
eth->d_addr.addr_bytes[2] = 0x5e;
|
||||
eth->d_addr.addr_bytes[3] = 0x01;
|
||||
eth->d_addr.addr_bytes[4] = 0x01;
|
||||
eth->d_addr.addr_bytes[5] = 0x01;
|
||||
|
||||
//source mac address (DU MAC)
|
||||
eth->s_addr.addr_bytes[0] = 0xdd;
|
||||
eth->s_addr.addr_bytes[1] = 0xdd;
|
||||
eth->s_addr.addr_bytes[2] = 0xdd;
|
||||
eth->s_addr.addr_bytes[3] = 0xdd;
|
||||
eth->s_addr.addr_bytes[4] = 0xdd;
|
||||
eth->s_addr.addr_bytes[5] = 0xdd;
|
||||
|
||||
dst_port = l2fwd_dst_ports[portid];
|
||||
#if 1
|
||||
|
||||
if (buf[18] == 0xAA) //msg1 check
|
||||
{
|
||||
printf("\nHandshake MSG 1 Received\n");
|
||||
printf("\n====================================================\n");
|
||||
|
||||
buf[18] = 0xBB;//change to msg2 after receive msg1
|
||||
|
||||
printf("Handshake MSG 2 Sent\n");
|
||||
printf("\n====================================================\n");
|
||||
}
|
||||
|
||||
#else
|
||||
// Handshake Condition
|
||||
if (buf[18] == 0x01 || buf[18] == 0x03) // ORAN handshake msgs.
|
||||
{
|
||||
if (buf[18] == 0x01) //msg1 check
|
||||
{
|
||||
printf("\nHandshake MSG 1 Received\n");
|
||||
printf("\n====================================================\n");
|
||||
|
||||
buf[18] = 0x02;//change to msg2 after receive msg1
|
||||
|
||||
printf("Handshake MSG 2 Sent\n");
|
||||
printf("\n====================================================\n");
|
||||
}
|
||||
|
||||
else if(buf[18] == 0x03) //msg3 check
|
||||
{
|
||||
printf("Handshake MSG 3 Sent\n");
|
||||
printf("\n====================================================\n");
|
||||
|
||||
buf[18] = 0x04;
|
||||
|
||||
printf("Handshake MSG 4 Sent\n");
|
||||
printf("\n====================================================\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Trigger start send DL packets
|
||||
if(PAYLOAD_1 == 0x12 && PAYLOAD_2 == 0xce && SYMBOL == 0x09 && ANT_NUM == 0x00 && dl_start == 0){
|
||||
|
||||
printf("\nU-Plane Started\n");
|
||||
printf("\n====================================================\n");
|
||||
|
||||
dl_start = 1;
|
||||
}
|
||||
|
||||
|
||||
if(PAYLOAD_1 == 0x12 && PAYLOAD_2 == 0xce) {
|
||||
p.frame = FRAME;
|
||||
p.subframe = SUBFRAME >> 4;
|
||||
p.slot = 0; /* unused */
|
||||
p.symbol = SYMBOL;
|
||||
p.antenna = ANT_NUM;
|
||||
memcpy(p.iq, IQ_ptr, 4800);
|
||||
store_ul(bs, &p);
|
||||
}
|
||||
|
||||
// U-PLANE UL PROCESSING
|
||||
if(PAYLOAD_1 == 0x12 && PAYLOAD_2 == 0xce && dl_start == 1)
|
||||
{
|
||||
int a = ANT_NUM;
|
||||
int tx_frame = FRAME;
|
||||
int tx_subframe = SUBFRAME >> 4;
|
||||
int tx_symbol = SYMBOL + 5;
|
||||
if (tx_symbol > 13) {
|
||||
tx_symbol -= 14;
|
||||
tx_subframe++;
|
||||
if (tx_subframe == 10) {
|
||||
tx_subframe = 0;
|
||||
tx_frame = (tx_frame + 1) & 255;
|
||||
}
|
||||
}
|
||||
|
||||
// ANT_NUM = 0x00;
|
||||
// SYMBOL = (SYMBOL + 5) % 14;
|
||||
PAYLOAD_2 = 0xc8;
|
||||
|
||||
FRAME = tx_frame;
|
||||
SUBFRAME = tx_subframe << 4;
|
||||
SYMBOL = tx_symbol;
|
||||
|
||||
/* send actual DL data (if available) */
|
||||
lock_dl_buffer(bs->buffers, tx_subframe);
|
||||
if (!(bs->buffers->dl_busy[a][tx_subframe] & (1 << tx_symbol))) {
|
||||
printf("%s: warning, DL underflow (antenna %d sf.symbol %d.%d)\n",
|
||||
__FUNCTION__,
|
||||
a,
|
||||
tx_subframe, tx_symbol);
|
||||
memset(IQ_ptr, 0, 1200 * 4);
|
||||
} else {
|
||||
memcpy(IQ_ptr, bs->buffers->dl[a][tx_subframe] + tx_symbol * 1200*4,
|
||||
1200*4);
|
||||
}
|
||||
bs->buffers->dl_busy[a][tx_subframe] &= ~(1 << tx_symbol);
|
||||
unlock_dl_buffer(bs->buffers, tx_subframe);
|
||||
}
|
||||
|
||||
// U-PLANE PRACH PROCESSING
|
||||
else if(PAYLOAD_1 == 0x0d && PAYLOAD_2 == 0x28){
|
||||
if (ANT_NUM == 0) {
|
||||
store_prach(bs, FRAME, SUBFRAME>>4, IQ_ptr);
|
||||
}
|
||||
|
||||
rte_pktmbuf_free(m);
|
||||
return;
|
||||
}
|
||||
|
||||
// Send Packets
|
||||
buffer = tx_buffer[dst_port];
|
||||
|
||||
sent = rte_eth_tx_buffer(dst_port, 0, buffer, m);
|
||||
|
||||
if (sent)
|
||||
port_statistics[dst_port].tx += sent;
|
||||
|
||||
}
|
||||
/* main processing loop */
|
||||
static void
|
||||
l2fwd_main_loop(benetel_t *bs)
|
||||
{
|
||||
struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
|
||||
struct rte_mbuf *m;
|
||||
int sent;
|
||||
unsigned lcore_id;
|
||||
uint64_t prev_tsc, diff_tsc, cur_tsc, timer_tsc;
|
||||
unsigned i, j, portid, nb_rx;
|
||||
struct lcore_queue_conf *qconf;
|
||||
const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S *
|
||||
BURST_TX_DRAIN_US;
|
||||
struct rte_eth_dev_tx_buffer *buffer;
|
||||
prev_tsc = 0;
|
||||
timer_tsc = 0;
|
||||
lcore_id = rte_lcore_id();
|
||||
qconf = &lcore_queue_conf[lcore_id];
|
||||
if (qconf->n_rx_port == 0) {
|
||||
RTE_LOG(INFO, L2FWD, "lcore %u has nothing to do\n", lcore_id);
|
||||
return;
|
||||
}
|
||||
RTE_LOG(INFO, L2FWD, "entering main loop on lcore %u\n", lcore_id);
|
||||
for (i = 0; i < qconf->n_rx_port; i++) {
|
||||
portid = qconf->rx_port_list[i];
|
||||
RTE_LOG(INFO, L2FWD, " -- lcoreid=%u portid=%u\n", lcore_id,
|
||||
portid);
|
||||
}
|
||||
while (!force_quit) {
|
||||
cur_tsc = rte_rdtsc();
|
||||
/*
|
||||
* TX burst queue drain
|
||||
*/
|
||||
diff_tsc = cur_tsc - prev_tsc;
|
||||
if (unlikely(diff_tsc > drain_tsc)) {
|
||||
for (i = 0; i < qconf->n_rx_port; i++) {
|
||||
portid = l2fwd_dst_ports[qconf->rx_port_list[i]];
|
||||
buffer = tx_buffer[portid];
|
||||
sent = rte_eth_tx_buffer_flush(portid, 0, buffer);
|
||||
if (sent)
|
||||
port_statistics[portid].tx += sent;
|
||||
}
|
||||
/* if timer is enabled */
|
||||
if (timer_period > 0) {
|
||||
/* advance the timer */
|
||||
timer_tsc += diff_tsc;
|
||||
/* if timer has reached its timeout */
|
||||
if (unlikely(timer_tsc >= timer_period)) {
|
||||
/* do this only on master core */
|
||||
if (lcore_id == rte_get_master_lcore()) {
|
||||
//print_stats();
|
||||
/* reset the timer */
|
||||
timer_tsc = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev_tsc = cur_tsc;
|
||||
}
|
||||
/*
|
||||
* Read packet from RX queues
|
||||
*/
|
||||
for (i = 0; i < qconf->n_rx_port; i++) {
|
||||
portid = qconf->rx_port_list[i];
|
||||
nb_rx = rte_eth_rx_burst(portid, 0,
|
||||
pkts_burst, MAX_PKT_BURST);
|
||||
port_statistics[portid].rx += nb_rx;
|
||||
for (j = 0; j < nb_rx; j++) {
|
||||
m = pkts_burst[j];
|
||||
rte_prefetch0(rte_pktmbuf_mtod(m, void *));
|
||||
l2fwd_simple_forward(m, portid, bs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static int
|
||||
l2fwd_launch_one_lcore(void *bs)
|
||||
{
|
||||
l2fwd_main_loop(bs);
|
||||
return 0;
|
||||
}
|
||||
/* display usage */
|
||||
static void
|
||||
l2fwd_usage(const char *prgname)
|
||||
{
|
||||
printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n"
|
||||
" -p PORTMASK: hexadecimal bitmask of ports to configure\n"
|
||||
" -q NQ: number of queue (=ports) per lcore (default is 1)\n"
|
||||
" -T PERIOD: statistics will be refreshed each PERIOD seconds (0 to disable, 10 default, 86400 maximum)\n"
|
||||
" --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n"
|
||||
" When enabled:\n"
|
||||
" - The source MAC address is replaced by the TX port MAC address\n"
|
||||
" - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n",
|
||||
prgname);
|
||||
}
|
||||
static int
|
||||
l2fwd_parse_portmask(const char *portmask)
|
||||
{
|
||||
char *end = NULL;
|
||||
unsigned long pm;
|
||||
/* parse hexadecimal string */
|
||||
pm = strtoul(portmask, &end, 16);
|
||||
if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
|
||||
return -1;
|
||||
if (pm == 0)
|
||||
return -1;
|
||||
return pm;
|
||||
}
|
||||
static unsigned int
|
||||
l2fwd_parse_nqueue(const char *q_arg)
|
||||
{
|
||||
char *end = NULL;
|
||||
unsigned long n;
|
||||
/* parse hexadecimal string */
|
||||
n = strtoul(q_arg, &end, 10);
|
||||
if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
|
||||
return 0;
|
||||
if (n == 0)
|
||||
return 0;
|
||||
if (n >= MAX_RX_QUEUE_PER_LCORE)
|
||||
return 0;
|
||||
return n;
|
||||
}
|
||||
static int
|
||||
l2fwd_parse_timer_period(const char *q_arg)
|
||||
{
|
||||
char *end = NULL;
|
||||
int n;
|
||||
/* parse number string */
|
||||
n = strtol(q_arg, &end, 10);
|
||||
if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
|
||||
return -1;
|
||||
if (n >= MAX_TIMER_PERIOD)
|
||||
return -1;
|
||||
return n;
|
||||
}
|
||||
static const char short_options[] =
|
||||
"p:" /* portmask */
|
||||
"q:" /* number of queues */
|
||||
"T:" /* timer period */
|
||||
;
|
||||
#define CMD_LINE_OPT_MAC_UPDATING "mac-updating"
|
||||
#define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating"
|
||||
enum {
|
||||
/* long options mapped to a short option */
|
||||
/* first long only option value must be >= 256, so that we won't
|
||||
* conflict with short options */
|
||||
CMD_LINE_OPT_MIN_NUM = 256,
|
||||
};
|
||||
static const struct option lgopts[] = {
|
||||
{ CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1},
|
||||
{ CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
/* Parse the argument given in the command line of the application */
|
||||
static int
|
||||
l2fwd_parse_args(int argc, char **argv)
|
||||
{
|
||||
int opt, ret, timer_secs;
|
||||
char **argvopt;
|
||||
int option_index;
|
||||
char *prgname = argv[0];
|
||||
argvopt = argv;
|
||||
while ((opt = getopt_long(argc, argvopt, short_options,
|
||||
lgopts, &option_index)) != EOF) {
|
||||
switch (opt) {
|
||||
/* portmask */
|
||||
case 'p':
|
||||
l2fwd_enabled_port_mask = l2fwd_parse_portmask(optarg);
|
||||
if (l2fwd_enabled_port_mask == 0) {
|
||||
printf("invalid portmask\n");
|
||||
l2fwd_usage(prgname);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
/* nqueue */
|
||||
case 'q':
|
||||
l2fwd_rx_queue_per_lcore = l2fwd_parse_nqueue(optarg);
|
||||
if (l2fwd_rx_queue_per_lcore == 0) {
|
||||
printf("invalid queue number\n");
|
||||
l2fwd_usage(prgname);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
/* timer period */
|
||||
case 'T':
|
||||
timer_secs = l2fwd_parse_timer_period(optarg);
|
||||
if (timer_secs < 0) {
|
||||
printf("invalid timer period\n");
|
||||
l2fwd_usage(prgname);
|
||||
return -1;
|
||||
}
|
||||
timer_period = timer_secs;
|
||||
break;
|
||||
/* long options */
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
l2fwd_usage(prgname);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (optind >= 0)
|
||||
argv[optind-1] = prgname;
|
||||
ret = optind-1;
|
||||
optind = 1; /* reset getopt lib */
|
||||
return ret;
|
||||
}
|
||||
/* Check the link status of all ports in up to 9s, and print them finally */
|
||||
static void
|
||||
check_all_ports_link_status(uint32_t port_mask)
|
||||
{
|
||||
#define CHECK_INTERVAL 100 /* 100ms */
|
||||
#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
|
||||
uint16_t portid;
|
||||
uint8_t count, all_ports_up, print_flag = 0;
|
||||
struct rte_eth_link link;
|
||||
printf("\nChecking link status");
|
||||
fflush(stdout);
|
||||
for (count = 0; count <= MAX_CHECK_TIME; count++) {
|
||||
if (force_quit)
|
||||
return;
|
||||
all_ports_up = 1;
|
||||
RTE_ETH_FOREACH_DEV(portid) {
|
||||
if (force_quit)
|
||||
return;
|
||||
if ((port_mask & (1 << portid)) == 0)
|
||||
continue;
|
||||
memset(&link, 0, sizeof(link));
|
||||
rte_eth_link_get_nowait(portid, &link);
|
||||
/* print link status if flag set */
|
||||
if (print_flag == 1) {
|
||||
if (link.link_status)
|
||||
printf(
|
||||
"Port%d Link Up. Speed %u Mbps - %s\n",
|
||||
portid, link.link_speed,
|
||||
(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
|
||||
("full-duplex") : ("half-duplex\n"));
|
||||
else
|
||||
printf("Port %d Link Down\n", portid);
|
||||
continue;
|
||||
}
|
||||
/* clear all_ports_up flag if any link down */
|
||||
if (link.link_status == ETH_LINK_DOWN) {
|
||||
all_ports_up = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* after finally printing all link status, get out */
|
||||
if (print_flag == 1)
|
||||
break;
|
||||
if (all_ports_up == 0) {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
rte_delay_ms(CHECK_INTERVAL);
|
||||
}
|
||||
/* set the print_flag if all ports up or timeout */
|
||||
if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
|
||||
print_flag = 1;
|
||||
printf("done\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
static void
|
||||
signal_handler(int signum)
|
||||
{
|
||||
if (signum == SIGINT || signum == SIGTERM) {
|
||||
printf("\n\nSignal %d received, preparing to exit...\n",
|
||||
signum);
|
||||
force_quit = true;
|
||||
}
|
||||
}
|
||||
int
|
||||
dpdk_main(int argc, char **argv, benetel_t *bs)
|
||||
{
|
||||
struct lcore_queue_conf *qconf;
|
||||
int ret;
|
||||
uint16_t nb_ports;
|
||||
uint16_t nb_ports_available = 0;
|
||||
uint16_t portid, last_port;
|
||||
unsigned lcore_id, rx_lcore_id;
|
||||
unsigned nb_ports_in_mask = 0;
|
||||
unsigned int nb_lcores = 0;
|
||||
unsigned int nb_mbufs;
|
||||
/* init EAL */
|
||||
ret = rte_eal_init(argc, argv);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n");
|
||||
argc -= ret;
|
||||
argv += ret;
|
||||
force_quit = false;
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
/* parse application arguments (after the EAL ones) */
|
||||
ret = l2fwd_parse_args(argc, argv);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE, "Invalid L2FWD arguments\n");
|
||||
printf("MAC updating %s\n", mac_updating ? "enabled" : "disabled");
|
||||
/* convert to number of cycles */
|
||||
timer_period *= rte_get_timer_hz();
|
||||
nb_ports = rte_eth_dev_count_avail();
|
||||
if (nb_ports == 0)
|
||||
rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
|
||||
/* check port mask to possible port mask */
|
||||
if (l2fwd_enabled_port_mask & ~((1 << nb_ports) - 1))
|
||||
rte_exit(EXIT_FAILURE, "Invalid portmask; possible (0x%x)\n",
|
||||
(1 << nb_ports) - 1);
|
||||
/* reset l2fwd_dst_ports */
|
||||
for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++)
|
||||
l2fwd_dst_ports[portid] = 0;
|
||||
last_port = 0;
|
||||
/*
|
||||
* Each logical core is assigned a dedicated TX queue on each port.
|
||||
*/
|
||||
RTE_ETH_FOREACH_DEV(portid) {
|
||||
/* skip ports that are not enabled */
|
||||
if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
|
||||
continue;
|
||||
if (nb_ports_in_mask % 2) {
|
||||
l2fwd_dst_ports[portid] = last_port;
|
||||
l2fwd_dst_ports[last_port] = portid;
|
||||
}
|
||||
else
|
||||
last_port = portid;
|
||||
nb_ports_in_mask++;
|
||||
}
|
||||
if (nb_ports_in_mask % 2) {
|
||||
printf("Notice: odd number of ports in portmask.\n");
|
||||
l2fwd_dst_ports[last_port] = last_port;
|
||||
}
|
||||
rx_lcore_id = 0;
|
||||
qconf = NULL;
|
||||
/* Initialize the port/queue configuration of each logical core */
|
||||
RTE_ETH_FOREACH_DEV(portid) {
|
||||
/* skip ports that are not enabled */
|
||||
if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
|
||||
continue;
|
||||
/* get the lcore_id for this port */
|
||||
while (rte_lcore_is_enabled(rx_lcore_id) == 0 ||
|
||||
lcore_queue_conf[rx_lcore_id].n_rx_port ==
|
||||
l2fwd_rx_queue_per_lcore) {
|
||||
rx_lcore_id++;
|
||||
if (rx_lcore_id >= RTE_MAX_LCORE)
|
||||
rte_exit(EXIT_FAILURE, "Not enough cores\n");
|
||||
}
|
||||
if (qconf != &lcore_queue_conf[rx_lcore_id]) {
|
||||
/* Assigned a new logical core in the loop above. */
|
||||
qconf = &lcore_queue_conf[rx_lcore_id];
|
||||
nb_lcores++;
|
||||
}
|
||||
qconf->rx_port_list[qconf->n_rx_port] = portid;
|
||||
qconf->n_rx_port++;
|
||||
printf("Lcore %u: RX port %u\n", rx_lcore_id, portid);
|
||||
}
|
||||
nb_mbufs = RTE_MAX(nb_ports * (nb_rxd + nb_txd + MAX_PKT_BURST +
|
||||
nb_lcores * MEMPOOL_CACHE_SIZE), 8192U);
|
||||
/* create the mbuf pool */
|
||||
l2fwd_pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", nb_mbufs,
|
||||
MEMPOOL_CACHE_SIZE, 0, 9680,
|
||||
rte_socket_id());
|
||||
if (l2fwd_pktmbuf_pool == NULL)
|
||||
rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n");
|
||||
/* Initialise each port */
|
||||
RTE_ETH_FOREACH_DEV(portid) {
|
||||
struct rte_eth_rxconf rxq_conf;
|
||||
struct rte_eth_txconf txq_conf;
|
||||
struct rte_eth_conf local_port_conf = port_conf;
|
||||
struct rte_eth_dev_info dev_info;
|
||||
/* skip ports that are not enabled */
|
||||
if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) {
|
||||
printf("Skipping disabled port %u\n", portid);
|
||||
continue;
|
||||
}
|
||||
nb_ports_available++;
|
||||
/* init port */
|
||||
printf("Initializing port %u... ", portid);
|
||||
fflush(stdout);
|
||||
rte_eth_dev_info_get(portid, &dev_info);
|
||||
if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
|
||||
local_port_conf.txmode.offloads |=
|
||||
DEV_TX_OFFLOAD_MBUF_FAST_FREE;
|
||||
ret = rte_eth_dev_configure(portid, 1, 1, &local_port_conf);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE, "Cannot configure device: err=%d, port=%u\n",
|
||||
ret, portid);
|
||||
ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd,
|
||||
&nb_txd);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE,
|
||||
"Cannot adjust number of descriptors: err=%d, port=%u\n",
|
||||
ret, portid);
|
||||
rte_eth_macaddr_get(portid,&l2fwd_ports_eth_addr[portid]);
|
||||
/* init one RX queue */
|
||||
fflush(stdout);
|
||||
rxq_conf = dev_info.default_rxconf;
|
||||
rxq_conf.offloads = local_port_conf.rxmode.offloads;
|
||||
ret = rte_eth_rx_queue_setup(portid, 0, nb_rxd,
|
||||
rte_eth_dev_socket_id(portid),
|
||||
&rxq_conf,
|
||||
l2fwd_pktmbuf_pool);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup:err=%d, port=%u\n",
|
||||
ret, portid);
|
||||
/* init one TX queue on each port */
|
||||
fflush(stdout);
|
||||
txq_conf = dev_info.default_txconf;
|
||||
txq_conf.offloads = local_port_conf.txmode.offloads;
|
||||
ret = rte_eth_tx_queue_setup(portid, 0, nb_txd,
|
||||
rte_eth_dev_socket_id(portid),
|
||||
&txq_conf);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup:err=%d, port=%u\n",
|
||||
ret, portid);
|
||||
/* Initialize TX buffers */
|
||||
tx_buffer[portid] = rte_zmalloc_socket("tx_buffer",
|
||||
RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0,
|
||||
rte_eth_dev_socket_id(portid));
|
||||
if (tx_buffer[portid] == NULL)
|
||||
rte_exit(EXIT_FAILURE, "Cannot allocate buffer for tx on port %u\n",
|
||||
portid);
|
||||
rte_eth_tx_buffer_init(tx_buffer[portid], MAX_PKT_BURST);
|
||||
ret = rte_eth_tx_buffer_set_err_callback(tx_buffer[portid],
|
||||
rte_eth_tx_buffer_count_callback,
|
||||
&port_statistics[portid].dropped);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE,
|
||||
"Cannot set error callback for tx buffer on port %u\n",
|
||||
portid);
|
||||
/* Start device */
|
||||
ret = rte_eth_dev_start(portid);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE, "rte_eth_dev_start:err=%d, port=%u\n",
|
||||
ret, portid);
|
||||
printf("done: \n");
|
||||
rte_eth_promiscuous_enable(portid);
|
||||
printf("Port %u, MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n\n",
|
||||
portid,
|
||||
l2fwd_ports_eth_addr[portid].addr_bytes[0],
|
||||
l2fwd_ports_eth_addr[portid].addr_bytes[1],
|
||||
l2fwd_ports_eth_addr[portid].addr_bytes[2],
|
||||
l2fwd_ports_eth_addr[portid].addr_bytes[3],
|
||||
l2fwd_ports_eth_addr[portid].addr_bytes[4],
|
||||
l2fwd_ports_eth_addr[portid].addr_bytes[5]);
|
||||
/* initialize port stats */
|
||||
memset(&port_statistics, 0, sizeof(port_statistics));
|
||||
}
|
||||
if (!nb_ports_available) {
|
||||
rte_exit(EXIT_FAILURE,
|
||||
"All available ports are disabled. Please set portmask.\n");
|
||||
}
|
||||
check_all_ports_link_status(l2fwd_enabled_port_mask);
|
||||
ret = 0;
|
||||
/* launch per-lcore init on every lcore */
|
||||
rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, bs, CALL_MASTER);
|
||||
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
|
||||
if (rte_eal_wait_lcore(lcore_id) < 0) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
RTE_ETH_FOREACH_DEV(portid) {
|
||||
if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
|
||||
continue;
|
||||
printf("Closing port %d...", portid);
|
||||
rte_eth_dev_stop(portid);
|
||||
rte_eth_dev_close(portid);
|
||||
printf(" Done\n");
|
||||
}
|
||||
printf("Bye...\n");
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include "low.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void store_ul(benetel_t *bs, ul_packet_t *ul)
|
||||
{
|
||||
int a = ul->antenna;
|
||||
|
||||
if (ul->subframe != bs->next_subframe[a] ||
|
||||
ul->symbol != bs->next_symbol[a]) {
|
||||
printf("%s: fatal, antenna %d expected frame.sf.symbol %d.%d.%d, got %d.%d.%d\n",
|
||||
__FUNCTION__,
|
||||
a,
|
||||
bs->expected_benetel_frame[a], bs->next_subframe[a], bs->next_symbol[a],
|
||||
ul->frame, ul->subframe, ul->symbol);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
lock_ul_buffer(bs->buffers, bs->next_subframe[a]);
|
||||
if (bs->buffers->ul_busy[a][bs->next_subframe[a]] & (1 << bs->next_symbol[a])) {
|
||||
printf("%s: warning, UL overflow (sf.symbol %d.%d)\n", __FUNCTION__,
|
||||
bs->next_subframe[a], bs->next_symbol[a]);
|
||||
}
|
||||
memcpy(bs->buffers->ul[a][bs->next_subframe[a]] + bs->next_symbol[a] * 1200*4,
|
||||
ul->iq, 1200*4);
|
||||
bs->buffers->ul_busy[a][bs->next_subframe[a]] |= (1 << bs->next_symbol[a]);
|
||||
signal_ul_buffer(bs->buffers, bs->next_subframe[a]);
|
||||
unlock_ul_buffer(bs->buffers, bs->next_subframe[a]);
|
||||
|
||||
bs->next_symbol[a]++;
|
||||
if (bs->next_symbol[a] == 14) {
|
||||
bs->next_symbol[a] = 0;
|
||||
bs->next_subframe[a] = (bs->next_subframe[a] + 1) % 10;
|
||||
if (bs->next_subframe[a] == 0) {
|
||||
bs->expected_benetel_frame[a]++;
|
||||
bs->expected_benetel_frame[a] &= 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void store_prach(benetel_t *bs, int frame, int subframe, void *data)
|
||||
{
|
||||
static int last_frame = -1;
|
||||
static int last_subframe = -1;
|
||||
/* hack: antenna number is always 0, discard second packet with same f/sf */
|
||||
if (frame == last_frame && subframe == last_subframe) return;
|
||||
last_frame = frame;
|
||||
last_subframe = subframe;
|
||||
|
||||
lock_ul_buffer(bs->buffers, subframe);
|
||||
if (bs->buffers->prach_busy[subframe]) {
|
||||
printf("store_prach: fatal: previous prach buffer not processed\n");
|
||||
unlock_ul_buffer(bs->buffers, subframe);
|
||||
return;
|
||||
}
|
||||
bs->buffers->prach_busy[subframe] = 1;
|
||||
memcpy(bs->buffers->prach[subframe], data, 849*4);
|
||||
signal_ul_buffer(bs->buffers, subframe);
|
||||
unlock_ul_buffer(bs->buffers, subframe);
|
||||
|
||||
}
|
||||
|
||||
void *benetel_start_dpdk(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line);
|
||||
|
||||
void *benetel_start(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line)
|
||||
{
|
||||
if (!strcmp(ifname, "dpdk"))
|
||||
return benetel_start_dpdk(ifname, buffers, dpdk_main_command_line);
|
||||
printf("benetel: fatal: interface %s not supported, only dpdpk is supported\n", ifname);
|
||||
exit(1);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef _BENETEL_4G_LOW_H_
|
||||
#define _BENETEL_4G_LOW_H_
|
||||
|
||||
#include "shared_buffers.h"
|
||||
|
||||
typedef struct {
|
||||
shared_buffers *buffers;
|
||||
/* [2] is for two antennas */
|
||||
int next_subframe[2];
|
||||
int next_symbol[2];
|
||||
int expected_benetel_frame[2];
|
||||
char *dpdk_main_command_line;
|
||||
} benetel_t;
|
||||
|
||||
typedef struct {
|
||||
int frame;
|
||||
int subframe;
|
||||
int slot;
|
||||
int symbol;
|
||||
int antenna;
|
||||
unsigned char iq[4800];
|
||||
} ul_packet_t;
|
||||
|
||||
void *benetel_start(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line);
|
||||
|
||||
void store_ul(benetel_t *bs, ul_packet_t *ul);
|
||||
void store_prach(benetel_t *bs, int frame, int subframe, void *data);
|
||||
|
||||
#endif /* _BENETEL_4G_LOW_H_ */
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/* those 2 lines for CPU_SET */
|
||||
#define _GNU_SOURCE
|
||||
#include <sched.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "low.h"
|
||||
|
||||
int dpdk_main(int argc, char **argv, benetel_t *);
|
||||
|
||||
void *dpdk_thread(void *_bs)
|
||||
{
|
||||
benetel_t *bs = _bs;
|
||||
int n = 0;
|
||||
char **v = NULL; // { "softmodem", "-n", "2", "-l", "8", "--", "-p", "0x2" };
|
||||
char *s = bs->dpdk_main_command_line;
|
||||
char *tok;
|
||||
|
||||
while ((tok = strtok(s, " ")) != NULL) {
|
||||
n++;
|
||||
v = realloc(v, n * sizeof(char *));
|
||||
if (v == NULL) {
|
||||
printf("%s: out of memory\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
v[n-1] = tok;
|
||||
s = NULL;
|
||||
}
|
||||
|
||||
dpdk_main(n, v, bs);
|
||||
exit(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *benetel_start_dpdk(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line)
|
||||
{
|
||||
benetel_t *bs;
|
||||
|
||||
bs = calloc(1, sizeof(benetel_t));
|
||||
if (bs == NULL) {
|
||||
printf("%s: out of memory\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bs->buffers = buffers;
|
||||
|
||||
bs->expected_benetel_frame[0] = 255;
|
||||
bs->expected_benetel_frame[1] = 255;
|
||||
|
||||
bs->dpdk_main_command_line = dpdk_main_command_line;
|
||||
|
||||
pthread_attr_t attr;
|
||||
|
||||
if (pthread_attr_init(&attr) != 0) {
|
||||
printf("pthread_attr_init failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
cpu_set_t cpuset;
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(12,&cpuset);
|
||||
if (pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset) != 0) {
|
||||
printf("pthread_attr_setaffinity_np failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) != 0) {
|
||||
printf("pthread_attr_setschedpolicy failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct sched_param params;
|
||||
params.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
||||
if (sched_get_priority_max(SCHED_FIFO) == -1) {
|
||||
printf("sched_get_priority_max failed\n");
|
||||
exit(1);
|
||||
}
|
||||
if (pthread_attr_setschedparam(&attr, ¶ms) != 0) {
|
||||
printf("pthread_setschedparam failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pthread_t t;
|
||||
if (pthread_create(&t, &attr, dpdk_thread, bs) != 0) {
|
||||
printf("%s: thread creation failed\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pthread_attr_destroy(&attr) != 0) {
|
||||
printf("pthread_attr_init failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return bs;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include "shared_buffers.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void init_buffers(shared_buffers *s)
|
||||
{
|
||||
int subframe;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
|
||||
for (subframe = 0; subframe < 10; subframe++) {
|
||||
if (pthread_mutex_init(&s->m_dl[subframe], NULL) != 0 ||
|
||||
pthread_cond_init(&s->c_dl[subframe], NULL) != 0 ||
|
||||
pthread_mutex_init(&s->m_ul[subframe], NULL) != 0 ||
|
||||
pthread_cond_init(&s->c_ul[subframe], NULL) != 0) {
|
||||
printf("%s: error initializing mutex/cond\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* in FDD the eNB's first transmitted DL subframe is 4 but the device
|
||||
* needs to have subframes 1, 2 and 3 ready. Let's pretend there are ready.
|
||||
*/
|
||||
s->dl_busy[0][1] = 0x3fff;
|
||||
s->dl_busy[0][2] = 0x3fff;
|
||||
s->dl_busy[0][3] = 0x3fff;
|
||||
s->dl_busy[1][1] = 0x3fff;
|
||||
s->dl_busy[1][2] = 0x3fff;
|
||||
s->dl_busy[1][3] = 0x3fff;
|
||||
}
|
||||
|
||||
void lock_dl_buffer(shared_buffers *s, int subframe)
|
||||
{
|
||||
if (pthread_mutex_lock(&s->m_dl[subframe]) != 0) {
|
||||
printf("%s: fatal: lock fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_dl_buffer(shared_buffers *s, int subframe)
|
||||
{
|
||||
if (pthread_mutex_unlock(&s->m_dl[subframe]) != 0) {
|
||||
printf("%s: fatal: unlock fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void wait_dl_buffer(shared_buffers *s, int subframe)
|
||||
{
|
||||
if (pthread_cond_wait(&s->c_dl[subframe], &s->m_dl[subframe]) != 0) {
|
||||
printf("%s: fatal: cond_wait fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void signal_dl_buffer(shared_buffers *s, int subframe)
|
||||
{
|
||||
if (pthread_cond_broadcast(&s->c_dl[subframe]) != 0) {
|
||||
printf("%s: fatal: cond_broadcast fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void lock_ul_buffer(shared_buffers *s, int subframe)
|
||||
{
|
||||
if (pthread_mutex_lock(&s->m_ul[subframe]) != 0) {
|
||||
printf("%s: fatal: lock fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_ul_buffer(shared_buffers *s, int subframe)
|
||||
{
|
||||
if (pthread_mutex_unlock(&s->m_ul[subframe]) != 0) {
|
||||
printf("%s: fatal: unlock fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void wait_ul_buffer(shared_buffers *s, int subframe)
|
||||
{
|
||||
if (pthread_cond_wait(&s->c_ul[subframe], &s->m_ul[subframe]) != 0) {
|
||||
printf("%s: fatal: cond_wait fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void signal_ul_buffer(shared_buffers *s, int subframe)
|
||||
{
|
||||
if (pthread_cond_broadcast(&s->c_ul[subframe]) != 0) {
|
||||
printf("%s: fatal: cond_broadcast fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef _BENETEL_4G_SHARED_BUFFERS_H_
|
||||
#define _BENETEL_4G_SHARED_BUFFERS_H_
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
/* [2] is for two antennas */
|
||||
unsigned char dl[2][10][14*1200*4];
|
||||
unsigned char ul[2][10][14*1200*4];
|
||||
uint16_t dl_busy[2][10];
|
||||
uint16_t ul_busy[2][10];
|
||||
|
||||
pthread_mutex_t m_ul[10];
|
||||
pthread_cond_t c_ul[10];
|
||||
|
||||
pthread_mutex_t m_dl[10];
|
||||
pthread_cond_t c_dl[10];
|
||||
|
||||
unsigned char prach[10][849*4];
|
||||
unsigned char prach_busy[10];
|
||||
|
||||
/* statistics/error counting */
|
||||
int ul_overflow;
|
||||
int dl_underflow;
|
||||
} shared_buffers;
|
||||
|
||||
void init_buffers(shared_buffers *s);
|
||||
|
||||
void lock_dl_buffer(shared_buffers *s, int subframe);
|
||||
void unlock_dl_buffer(shared_buffers *s, int subframe);
|
||||
void wait_dl_buffer(shared_buffers *s, int subframe);
|
||||
void signal_dl_buffer(shared_buffers *s, int subframe);
|
||||
|
||||
void lock_ul_buffer(shared_buffers *s, int subframe);
|
||||
void unlock_ul_buffer(shared_buffers *s, int subframe);
|
||||
void wait_ul_buffer(shared_buffers *s, int subframe);
|
||||
void signal_ul_buffer(shared_buffers *s, int subframe);
|
||||
|
||||
#endif /* _BENETEL_4G_SHARED_BUFFERS_H_ */
|
||||
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common_lib.h"
|
||||
#include "ethernet_lib.h"
|
||||
#include "shared_buffers.h"
|
||||
#include "low.h"
|
||||
#include "openair1/PHY/defs_gNB.h"
|
||||
|
||||
typedef struct {
|
||||
eth_state_t e;
|
||||
shared_buffers buffers;
|
||||
rru_config_msg_type_t last_msg;
|
||||
int capabilities_sent;
|
||||
void *benetel_priv;
|
||||
char *dpdk_main_command_line;
|
||||
} benetel_eth_state_t;
|
||||
|
||||
int trx_benetel_start(openair0_device *device)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void trx_benetel_end(openair0_device *device)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
|
||||
int trx_benetel_stop(openair0_device *device)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int trx_benetel_set_freq(openair0_device* device,
|
||||
openair0_config_t *openair0_cfg,
|
||||
int exmimo_dump_config)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int trx_benetel_set_gains(openair0_device* device,
|
||||
openair0_config_t *openair0_cfg)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int trx_benetel_get_stats(openair0_device* device)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int trx_benetel_reset_stats(openair0_device* device)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int ethernet_tune(openair0_device *device,
|
||||
unsigned int option,
|
||||
int value)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trx_benetel_write_raw(openair0_device *device,
|
||||
openair0_timestamp timestamp,
|
||||
void **buff, int nsamps, int cc, int flags)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return nsamps*4;
|
||||
}
|
||||
|
||||
int trx_benetel_read_raw(openair0_device *device,
|
||||
openair0_timestamp *timestamp,
|
||||
void **buff, int nsamps, int cc)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
return nsamps*4;
|
||||
}
|
||||
|
||||
char *msg_type(int t)
|
||||
{
|
||||
static char *s[12] = {
|
||||
"RAU_tick",
|
||||
"RRU_capabilities",
|
||||
"RRU_config",
|
||||
"RRU_config_ok",
|
||||
"RRU_start",
|
||||
"RRU_stop",
|
||||
"RRU_sync_ok",
|
||||
"RRU_frame_resynch",
|
||||
"RRU_MSG_max_num",
|
||||
"RRU_check_sync",
|
||||
"RRU_config_update",
|
||||
"RRU_config_update_ok",
|
||||
};
|
||||
|
||||
if (t < 0 || t > 11) return "UNKNOWN";
|
||||
return s[t];
|
||||
}
|
||||
|
||||
int trx_benetel_ctlsend(openair0_device *device, void *msg, ssize_t msg_len)
|
||||
{
|
||||
RRU_CONFIG_msg_t *rru_config_msg = msg;
|
||||
benetel_eth_state_t *s = device->priv;
|
||||
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
|
||||
printf(" rru_config_msg->type %d [%s]\n", rru_config_msg->type,
|
||||
msg_type(rru_config_msg->type));
|
||||
|
||||
s->last_msg = rru_config_msg->type;
|
||||
|
||||
return msg_len;
|
||||
}
|
||||
|
||||
int trx_benetel_ctlrecv(openair0_device *device, void *msg, ssize_t msg_len)
|
||||
{
|
||||
RRU_CONFIG_msg_t *rru_config_msg = msg;
|
||||
benetel_eth_state_t *s = device->priv;
|
||||
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
|
||||
if (s->last_msg == RAU_tick && s->capabilities_sent == 0) {
|
||||
RRU_capabilities_t *cap;
|
||||
rru_config_msg->type = RRU_capabilities;
|
||||
rru_config_msg->len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_capabilities_t);
|
||||
cap = (RRU_capabilities_t*)&rru_config_msg->msg[0];
|
||||
cap->FH_fmt = OAI_IF4p5_only;
|
||||
cap->num_bands = 1;
|
||||
cap->band_list[0] = 78;
|
||||
/* TODO: hardcoded to 1 for the moment, get the real value somehow... */
|
||||
cap->nb_rx[0] = 1; //device->openair0_cfg->rx_num_channels;
|
||||
cap->nb_tx[0] = 1; //device->openair0_cfg->tx_num_channels;
|
||||
cap->max_pdschReferenceSignalPower[0] = -27;
|
||||
cap->max_rxgain[0] = 90;
|
||||
|
||||
s->capabilities_sent = 1;
|
||||
|
||||
return rru_config_msg->len;
|
||||
}
|
||||
#if 0
|
||||
if (s->last_msg == RRU_config) {
|
||||
rru_config_msg->type = RRU_config_ok;
|
||||
return 0;
|
||||
}
|
||||
if (s->last_msg == RRU_start) {
|
||||
printf("***************** benetel start\n");
|
||||
s->benetel_priv = benetel_start(s->e.if_name, &s->buffers, s->dpdk_main_command_line);
|
||||
}
|
||||
#endif
|
||||
if (s->last_msg == RRU_config) {
|
||||
rru_config_msg->type = RRU_config_ok;
|
||||
s->benetel_priv = benetel_start(s->e.if_name, &s->buffers, s->dpdk_main_command_line);
|
||||
}
|
||||
/* correct? */
|
||||
//printf("***************** benetel pause\n");
|
||||
// while (1) pause();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void benetel_fh_if4p5_south_in(RU_t *ru,
|
||||
int *frame,
|
||||
int *slot)
|
||||
{
|
||||
//printf("XXX benetel_fh_if4p5_south_in %d %d\n", *frame, *slot);
|
||||
benetel_eth_state_t *s = ru->ifdevice.priv;
|
||||
NR_DL_FRAME_PARMS *fp;
|
||||
int symbol;
|
||||
int32_t *rxdata;
|
||||
int antenna;
|
||||
|
||||
lock_ul_buffer(&s->buffers, *slot);
|
||||
#if 1
|
||||
next:
|
||||
while (!((s->buffers.ul_busy[0][*slot] == 0x3fff &&
|
||||
s->buffers.ul_busy[1][*slot] == 0x3fff) ||
|
||||
s->buffers.prach_busy[*slot] == 1))
|
||||
wait_ul_buffer(&s->buffers, *slot);
|
||||
if (s->buffers.prach_busy[*slot] == 1) {
|
||||
int i;
|
||||
int antenna = 0;
|
||||
uint16_t *in;
|
||||
uint16_t *out;
|
||||
in = (uint16_t *)s->buffers.prach[*slot];
|
||||
out = (uint16_t *)ru->prach_rxsigF[0][antenna];
|
||||
for (i = 0; i < 839*2; i++)
|
||||
out[i] = ntohs(in[i]);
|
||||
s->buffers.prach_busy[*slot] = 0;
|
||||
//printf("prach for f.sl %d.%d\n", *frame, *slot);
|
||||
//ru->wakeup_prach_gNB(ru->gNB_list[0], ru, *frame, *slot);
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
fp = ru->nr_frame_parms;
|
||||
for (antenna = 0; antenna < ru->nb_rx; antenna++) {
|
||||
for (symbol = 0; symbol < 14; symbol++) {
|
||||
int i;
|
||||
int16_t *p = (int16_t *)(&s->buffers.ul[antenna][*slot][symbol*1272*4]);
|
||||
for (i = 0; i < 1272*2; i++) {
|
||||
p[i] = (int16_t)(ntohs(p[i])) / 16;
|
||||
}
|
||||
rxdata = &ru->common.rxdataF[antenna][symbol * fp->ofdm_symbol_size];
|
||||
#if 0
|
||||
if (*slot == 0 && symbol == 0)
|
||||
printf("rxdata in benetel_fh_if4p5_south_in %p\n", &ru->common.rxdataF[antenna][0]);
|
||||
#endif
|
||||
#if 1
|
||||
memcpy(rxdata + 2048 - 1272/2,
|
||||
&s->buffers.ul[antenna][*slot][symbol*1272*4],
|
||||
(1272/2) * 4);
|
||||
memcpy(rxdata,
|
||||
&s->buffers.ul[antenna][*slot][symbol*1272*4] + (1272/2)*4,
|
||||
(1272/2) * 4);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
s->buffers.ul_busy[0][*slot] = 0;
|
||||
s->buffers.ul_busy[1][*slot] = 0;
|
||||
signal_ul_buffer(&s->buffers, *slot);
|
||||
unlock_ul_buffer(&s->buffers, *slot);
|
||||
|
||||
//printf("BENETEL: %s (f.sf %d.%d)\n", __FUNCTION__, *frame, *slot);
|
||||
|
||||
RU_proc_t *proc = &ru->proc;
|
||||
extern uint16_t sl_ahead;
|
||||
int f = *frame;
|
||||
int sl = *slot;
|
||||
|
||||
//calculate timestamp_rx, timestamp_tx based on frame and slot
|
||||
proc->tti_rx = sl;
|
||||
proc->frame_rx = f;
|
||||
/* TODO: be sure of samples_per_slot0
|
||||
FK: should use get_samples_per_slot(slot)
|
||||
but for mu=1 its ok
|
||||
*/
|
||||
proc->timestamp_rx = ((proc->frame_rx * 20) + proc->tti_rx ) * fp->samples_per_slot0;
|
||||
|
||||
if (get_nprocs()<=4) {
|
||||
// why? what if there are more?
|
||||
proc->tti_tx = (sl+sl_ahead)%20;
|
||||
proc->frame_tx = (sl>(19-sl_ahead)) ? (f+1)&1023 : f;
|
||||
}
|
||||
}
|
||||
|
||||
void benetel_fh_if4p5_south_out(RU_t *ru,
|
||||
int frame,
|
||||
int slot,
|
||||
uint64_t timestamp)
|
||||
{
|
||||
|
||||
//printf("XXX benetel_fh_if4p5_south_out %d %d %ld\n", frame, slot, timestamp);
|
||||
benetel_eth_state_t *s = ru->ifdevice.priv;
|
||||
NR_DL_FRAME_PARMS *fp;
|
||||
int symbol;
|
||||
int32_t *txdata;
|
||||
int aa;
|
||||
|
||||
//printf("BENETEL: %s (f.sf %d.%d ts %ld)\n", __FUNCTION__, frame, slot, timestamp);
|
||||
|
||||
lock_dl_buffer(&s->buffers, slot);
|
||||
if (s->buffers.dl_busy[0][slot] ||
|
||||
s->buffers.dl_busy[1][slot]) {
|
||||
printf("%s: fatal: DL buffer busy for subframe %d\n", __FUNCTION__, slot);
|
||||
unlock_dl_buffer(&s->buffers, slot);
|
||||
return;
|
||||
}
|
||||
|
||||
fp = ru->nr_frame_parms;
|
||||
if (ru->num_gNB != 1 || fp->ofdm_symbol_size != 2048 ||
|
||||
fp->Ncp != NORMAL || fp->symbols_per_slot != 14) {
|
||||
printf("%s:%d:%s: unsupported configuration\n",
|
||||
__FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (aa = 0; aa < ru->nb_tx; aa++) {
|
||||
for (symbol = 0; symbol < 14; symbol++) {
|
||||
txdata = &ru->common.txdataF_BF[aa][symbol * fp->ofdm_symbol_size];
|
||||
#if 1
|
||||
memcpy(&s->buffers.dl[aa][slot][symbol*1272*4],
|
||||
txdata + 2048 - (1272/2),
|
||||
(1272/2) * 4);
|
||||
memcpy(&s->buffers.dl[aa][slot][symbol*1272*4] + (1272/2)*4,
|
||||
txdata,
|
||||
(1272/2) * 4);
|
||||
#endif
|
||||
int i;
|
||||
uint16_t *p = (uint16_t *)(&s->buffers.dl[aa][slot][symbol*1272*4]);
|
||||
for (i = 0; i < 1272*2; i++) {
|
||||
p[i] = htons(p[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s->buffers.dl_busy[0][slot] = 0x3fff;
|
||||
s->buffers.dl_busy[1][slot] = 0x3fff;
|
||||
unlock_dl_buffer(&s->buffers, slot);
|
||||
}
|
||||
|
||||
void *get_internal_parameter(char *name)
|
||||
{
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
|
||||
if (!strcmp(name, "fh_if4p5_south_in"))
|
||||
return benetel_fh_if4p5_south_in;
|
||||
if (!strcmp(name, "fh_if4p5_south_out"))
|
||||
return benetel_fh_if4p5_south_out;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__attribute__((__visibility__("default")))
|
||||
int transport_init(openair0_device *device,
|
||||
openair0_config_t *openair0_cfg,
|
||||
eth_params_t * eth_params )
|
||||
{
|
||||
benetel_eth_state_t *eth;
|
||||
|
||||
printf("BENETEL: %s\n", __FUNCTION__);
|
||||
|
||||
device->Mod_id = 0;
|
||||
device->transp_type = ETHERNET_TP;
|
||||
device->trx_start_func = trx_benetel_start;
|
||||
device->trx_get_stats_func = trx_benetel_get_stats;
|
||||
device->trx_reset_stats_func = trx_benetel_reset_stats;
|
||||
device->trx_end_func = trx_benetel_end;
|
||||
device->trx_stop_func = trx_benetel_stop;
|
||||
device->trx_set_freq_func = trx_benetel_set_freq;
|
||||
device->trx_set_gains_func = trx_benetel_set_gains;
|
||||
|
||||
device->trx_write_func = trx_benetel_write_raw;
|
||||
device->trx_read_func = trx_benetel_read_raw;
|
||||
|
||||
device->trx_ctlsend_func = trx_benetel_ctlsend;
|
||||
device->trx_ctlrecv_func = trx_benetel_ctlrecv;
|
||||
|
||||
device->get_internal_parameter = get_internal_parameter;
|
||||
|
||||
eth = calloc(1, sizeof(benetel_eth_state_t));
|
||||
if (eth == NULL) {
|
||||
AssertFatal(0==1, "out of memory\n");
|
||||
}
|
||||
|
||||
eth->e.flags = ETH_RAW_IF4p5_MODE;
|
||||
eth->e.compression = NO_COMPRESS;
|
||||
eth->e.if_name = eth_params->local_if_name;
|
||||
device->priv = eth;
|
||||
device->openair0_cfg=&openair0_cfg[0];
|
||||
|
||||
if (openair0_cfg->sdr_addrs == NULL) {
|
||||
printf("benetel: fatal: sdr_addrs not set in configuration file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
eth->dpdk_main_command_line = strdup(openair0_cfg->sdr_addrs);
|
||||
if (eth->dpdk_main_command_line == NULL) {
|
||||
AssertFatal(0==1, "out of memory\n");
|
||||
}
|
||||
|
||||
eth->last_msg = -1;
|
||||
|
||||
init_buffers(ð->buffers);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,835 @@
|
||||
/* license: to be defined */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/queue.h>
|
||||
#include <netinet/in.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <rte_common.h>
|
||||
#include <rte_log.h>
|
||||
#include <rte_malloc.h>
|
||||
#include <rte_memory.h>
|
||||
#include <rte_memcpy.h>
|
||||
#include <rte_eal.h>
|
||||
#include <rte_launch.h>
|
||||
#include <rte_atomic.h>
|
||||
#include <rte_cycles.h>
|
||||
#include <rte_prefetch.h>
|
||||
#include <rte_lcore.h>
|
||||
#include <rte_per_lcore.h>
|
||||
#include <rte_branch_prediction.h>
|
||||
#include <rte_interrupts.h>
|
||||
#include <rte_random.h>
|
||||
#include <rte_debug.h>
|
||||
#include <rte_ether.h>
|
||||
#include <rte_ethdev.h>
|
||||
#include <rte_mempool.h>
|
||||
#include <rte_mbuf.h>
|
||||
|
||||
#include "low.h"
|
||||
|
||||
// HEADER DEFINITION
|
||||
#define ANT_NUM buf[23]
|
||||
#define PAYLOAD_1 buf[20]
|
||||
#define PAYLOAD_2 buf[21]
|
||||
#define ETH_TYPE buf[17]
|
||||
#define SYMBOL buf[29]
|
||||
#define SUBFRAME buf[28]
|
||||
#define FRAME buf[27]
|
||||
|
||||
static volatile bool force_quit;
|
||||
|
||||
unsigned short iq[712320];
|
||||
unsigned char iq_swap[1424640];
|
||||
|
||||
unsigned short *iq_ptr[14];
|
||||
unsigned int dl_start = 0, slot_id_ctrl = 0, count_symbol = 0, sf = 0x10;
|
||||
|
||||
/* MAC updating enabled by default */
|
||||
static int mac_updating = 1;
|
||||
#define RTE_LOGTYPE_L2FWD RTE_LOGTYPE_USER1
|
||||
#define MAX_PKT_BURST 32
|
||||
#define BURST_TX_DRAIN_US 5 /* TX drain every ~100us */
|
||||
#define MEMPOOL_CACHE_SIZE 256
|
||||
/*
|
||||
* Configurable number of RX/TX ring descriptors
|
||||
*/
|
||||
#define RTE_TEST_RX_DESC_DEFAULT 1024
|
||||
#define RTE_TEST_TX_DESC_DEFAULT 1024
|
||||
static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
|
||||
static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
|
||||
/* ethernet addresses of ports */
|
||||
static struct ether_addr l2fwd_ports_eth_addr[RTE_MAX_ETHPORTS];
|
||||
/* mask of enabled ports */
|
||||
static uint32_t l2fwd_enabled_port_mask = 0;
|
||||
/* list of enabled ports */
|
||||
static uint32_t l2fwd_dst_ports[RTE_MAX_ETHPORTS];
|
||||
static unsigned int l2fwd_rx_queue_per_lcore = 1;
|
||||
#define MAX_RX_QUEUE_PER_LCORE 16
|
||||
#define MAX_TX_QUEUE_PER_PORT 16
|
||||
struct lcore_queue_conf {
|
||||
unsigned n_rx_port;
|
||||
unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE];
|
||||
} __rte_cache_aligned;
|
||||
struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE];
|
||||
static struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS];
|
||||
static struct rte_eth_conf port_conf = {
|
||||
.rxmode = {
|
||||
.split_hdr_size = 0,
|
||||
.offloads = DEV_RX_OFFLOAD_JUMBO_FRAME,
|
||||
.split_hdr_size = 0,
|
||||
.max_rx_pkt_len = 9500,
|
||||
},
|
||||
.txmode = {
|
||||
.mq_mode = ETH_MQ_TX_NONE,
|
||||
},
|
||||
};
|
||||
struct rte_mempool * l2fwd_pktmbuf_pool = NULL;
|
||||
/* Per-port statistics struct */
|
||||
struct l2fwd_port_statistics {
|
||||
uint64_t tx;
|
||||
uint64_t rx;
|
||||
uint64_t dropped;
|
||||
} __rte_cache_aligned;
|
||||
struct l2fwd_port_statistics port_statistics[RTE_MAX_ETHPORTS];
|
||||
#define MAX_TIMER_PERIOD 86400 /* 1 day max */
|
||||
/* A tsc-based timer responsible for triggering statistics printout */
|
||||
static uint64_t timer_period = 10; /* default period is 10 seconds */
|
||||
/* Print out statistics on packets dropped */
|
||||
static void
|
||||
print_stats(void)
|
||||
{
|
||||
uint64_t total_packets_dropped, total_packets_tx, total_packets_rx;
|
||||
unsigned portid;
|
||||
total_packets_dropped = 0;
|
||||
total_packets_tx = 0;
|
||||
total_packets_rx = 0;
|
||||
const char clr[] = { 27, '[', '2', 'J', '\0' };
|
||||
const char topLeft[] = { 27, '[', '1', ';', '1', 'H','\0' };
|
||||
/* Clear screen and move to top left */
|
||||
printf("%s%s", clr, topLeft);
|
||||
printf("\nPort statistics ====================================");
|
||||
for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
|
||||
/* skip disabled ports */
|
||||
if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
|
||||
continue;
|
||||
printf("\nStatistics for port %u ------------------------------"
|
||||
"\nPackets sent: %24"PRIu64
|
||||
"\nPackets received: %20"PRIu64
|
||||
"\nPackets dropped: %21"PRIu64,
|
||||
portid,
|
||||
port_statistics[portid].tx,
|
||||
port_statistics[portid].rx,
|
||||
port_statistics[portid].dropped);
|
||||
total_packets_dropped += port_statistics[portid].dropped;
|
||||
total_packets_tx += port_statistics[portid].tx;
|
||||
total_packets_rx += port_statistics[portid].rx;
|
||||
}
|
||||
printf("\nAggregate statistics ==============================="
|
||||
"\nTotal packets sent: %18"PRIu64
|
||||
"\nTotal packets received: %14"PRIu64
|
||||
"\nTotal packets dropped: %15"PRIu64,
|
||||
total_packets_tx,
|
||||
total_packets_rx,
|
||||
total_packets_dropped);
|
||||
printf("\n====================================================\n");
|
||||
}
|
||||
static void
|
||||
l2fwd_simple_forward(struct rte_mbuf *m, unsigned portid, benetel_t *bs)
|
||||
{
|
||||
unsigned char *buf, *buf_tx;
|
||||
unsigned int len;
|
||||
struct ether_hdr *eth;
|
||||
|
||||
unsigned dst_port;
|
||||
int sent, prach_ctrl = 0;
|
||||
struct rte_eth_dev_tx_buffer *buffer;
|
||||
|
||||
unsigned char * IQ_ptr;
|
||||
|
||||
ul_packet_t p;
|
||||
|
||||
buf = rte_pktmbuf_mtod(m, unsigned char *);
|
||||
//DD Debug
|
||||
//rte_memdump(stdout, "Rx Packet",buf ,34);
|
||||
eth = rte_pktmbuf_mtod(m, struct ether_hdr *);
|
||||
len = rte_pktmbuf_data_len(m);
|
||||
|
||||
IQ_ptr = &buf[34];
|
||||
if (portid == 0)
|
||||
{
|
||||
//destination mac address (RRU MAC)
|
||||
/*eth->d_addr.addr_bytes[0] = 0x02;
|
||||
eth->d_addr.addr_bytes[1] = 0x00;
|
||||
eth->d_addr.addr_bytes[2] = 0x5e;
|
||||
eth->d_addr.addr_bytes[3] = 0x01;
|
||||
eth->d_addr.addr_bytes[4] = 0x01;
|
||||
eth->d_addr.addr_bytes[5] = 0x01;*/
|
||||
|
||||
eth->d_addr.addr_bytes[0] = buf[6];
|
||||
eth->d_addr.addr_bytes[1] = buf[7];
|
||||
eth->d_addr.addr_bytes[2] = buf[8];
|
||||
eth->d_addr.addr_bytes[3] = buf[9];
|
||||
eth->d_addr.addr_bytes[4] = buf[10];
|
||||
eth->d_addr.addr_bytes[5] = buf[11];
|
||||
|
||||
//source mac address (DU MAC)
|
||||
eth->s_addr.addr_bytes[0] = 0xdd;
|
||||
eth->s_addr.addr_bytes[1] = 0xdd;
|
||||
eth->s_addr.addr_bytes[2] = 0xdd;
|
||||
eth->s_addr.addr_bytes[3] = 0xdd;
|
||||
eth->s_addr.addr_bytes[4] = 0xdd;
|
||||
eth->s_addr.addr_bytes[5] = 0xdd;
|
||||
}
|
||||
else
|
||||
{
|
||||
//destination mac address (RRU MAC)
|
||||
/*eth->d_addr.addr_bytes[0] = 0x02;
|
||||
eth->d_addr.addr_bytes[1] = 0x00;
|
||||
eth->d_addr.addr_bytes[2] = 0x5e;
|
||||
eth->d_addr.addr_bytes[3] = 0x01;
|
||||
eth->d_addr.addr_bytes[4] = 0x01;
|
||||
eth->d_addr.addr_bytes[5] = 0x01;*/
|
||||
|
||||
eth->d_addr.addr_bytes[0] = buf[6];
|
||||
eth->d_addr.addr_bytes[1] = buf[7];
|
||||
eth->d_addr.addr_bytes[2] = buf[8];
|
||||
eth->d_addr.addr_bytes[3] = buf[9];
|
||||
eth->d_addr.addr_bytes[4] = buf[10];
|
||||
eth->d_addr.addr_bytes[5] = buf[11];
|
||||
//source mac address (DU MAC)
|
||||
eth->s_addr.addr_bytes[0] = 0xdd;
|
||||
eth->s_addr.addr_bytes[1] = 0xdd;
|
||||
eth->s_addr.addr_bytes[2] = 0xdd;
|
||||
eth->s_addr.addr_bytes[3] = 0xdd;
|
||||
eth->s_addr.addr_bytes[4] = 0xdd;
|
||||
eth->s_addr.addr_bytes[5] = 0xde;
|
||||
|
||||
}
|
||||
dst_port = l2fwd_dst_ports[portid];
|
||||
//DD Debug
|
||||
//printf("dstPort =%d\n", dst_port);
|
||||
// Handshake Condition
|
||||
if (buf[18] == 0xAA ) // ORAN handshake msgs.
|
||||
{
|
||||
printf("\n Start UP Request Received\n");
|
||||
printf("\n====================================================\n");
|
||||
|
||||
buf[18] = 0xBB;//change to msg2 after receive msg1
|
||||
|
||||
printf(" Start Up Response Sent\n");
|
||||
printf("\n====================================================\n");
|
||||
|
||||
}
|
||||
|
||||
// Trigger start send DL packets
|
||||
if(PAYLOAD_1 == 0x13 && PAYLOAD_2 == 0xe4 && SYMBOL == 0x44 && ANT_NUM == 0x00 && SUBFRAME == 0x00 && dl_start == 0){
|
||||
|
||||
printf("\nU-Plane Started\n");
|
||||
printf("\n====================================================\n");
|
||||
|
||||
dl_start = 1;
|
||||
count_symbol = 28;
|
||||
}
|
||||
|
||||
|
||||
if(PAYLOAD_1 == 0x13 && PAYLOAD_2 == 0xe4) {
|
||||
int subframe = SUBFRAME >> 4;
|
||||
int slot = ((SUBFRAME & 0x0f) << 2) | ((SYMBOL >> 6) & 0x03);
|
||||
p.frame = FRAME;
|
||||
p.slot = subframe * 2 + slot;
|
||||
p.symbol = SYMBOL & 0x3f;
|
||||
p.antenna = ANT_NUM;
|
||||
memcpy(p.iq, IQ_ptr, 5088);
|
||||
store_ul(bs, &p);
|
||||
// if (p.symbol==0) printf("store ul f.sl.sy %d.%d.%d\n", p.frame, p.slot, p.symbol);
|
||||
}
|
||||
|
||||
// U-PLANE UL PROCESSING
|
||||
if(PAYLOAD_1 == 0x13 && PAYLOAD_2 == 0xe4 && dl_start == 1)
|
||||
{
|
||||
int a = ANT_NUM & 0x01;
|
||||
int frame = FRAME;
|
||||
int subframe = SUBFRAME >> 4;
|
||||
int slot = ((SUBFRAME & 0x0f) << 2) | ((SYMBOL >> 6) & 0x03);
|
||||
int symbol = SYMBOL & 0x3f;
|
||||
int oai_slot;
|
||||
|
||||
int tx_frame = frame;
|
||||
int tx_subframe = subframe;
|
||||
int tx_slot = slot;
|
||||
int tx_symbol = symbol + 10;
|
||||
|
||||
if (tx_symbol > 13) {
|
||||
tx_symbol -= 14;
|
||||
tx_slot++;
|
||||
if (tx_slot == 2) {
|
||||
tx_slot = 0;
|
||||
tx_subframe++;
|
||||
if (tx_subframe == 10) {
|
||||
tx_subframe = 0;
|
||||
tx_frame = (tx_frame + 1) & 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mask the symbol bits from UL packet received and apply the shift.
|
||||
SYMBOL = ((SYMBOL & 0b00111111) + 10) % 14;
|
||||
|
||||
ANT_NUM = a;
|
||||
SUBFRAME = sf;
|
||||
|
||||
if (a == 1)
|
||||
slot_id_ctrl++;
|
||||
|
||||
// Slot id control for DL
|
||||
if (slot_id_ctrl > 13) {
|
||||
SYMBOL = SYMBOL | 0b01000000;
|
||||
|
||||
if (a == 1) {
|
||||
if (slot_id_ctrl > 27) {
|
||||
slot_id_ctrl = 0;
|
||||
sf = sf + 0x10;
|
||||
|
||||
if (sf > 0x90){
|
||||
sf = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* send actual DL data (if available) */
|
||||
oai_slot = tx_subframe * 2 + tx_slot;
|
||||
lock_dl_buffer(bs->buffers, oai_slot);
|
||||
if (!(bs->buffers->dl_busy[a][oai_slot] & (1 << tx_symbol))) {
|
||||
//printf("%s: warning, DL underflow (antenna %d sl.symbol %d.%d)\n", __FUNCTION__,a, oai_slot, tx_symbol);
|
||||
memset(IQ_ptr, 0, 1272 * 4);
|
||||
} else {
|
||||
memcpy(IQ_ptr, bs->buffers->dl[a][oai_slot] + tx_symbol * 1272*4,
|
||||
1272*4);
|
||||
}
|
||||
//printf("DL buffer f sf slot symbol %d %d %d %d (sf %d)\n", tx_frame, tx_subframe, tx_slot, tx_symbol, (int)sf);
|
||||
bs->buffers->dl_busy[a][oai_slot] &= ~(1 << tx_symbol);
|
||||
unlock_dl_buffer(bs->buffers, oai_slot);
|
||||
}
|
||||
|
||||
// U-PLANE PRACH PROCESSING
|
||||
else if(PAYLOAD_1 == 0x0d && PAYLOAD_2 == 0x28){
|
||||
if (ANT_NUM == 0) {
|
||||
int subframe = SUBFRAME >> 4;
|
||||
//int slot = ((SUBFRAME & 0x0f) << 2) | ((SYMBOL >> 6) & 0x03);
|
||||
if (subframe==9) {
|
||||
//printf("store prach f.sf.sl %d.%d.%d %d\n", FRAME, subframe, slot, subframe * 2 + slot);
|
||||
store_prach(bs, FRAME, subframe * 2 + 1, IQ_ptr);
|
||||
}
|
||||
}
|
||||
rte_pktmbuf_free(m);
|
||||
return;
|
||||
}
|
||||
|
||||
// Send Packets
|
||||
buffer = tx_buffer[dst_port];
|
||||
|
||||
sent = rte_eth_tx_buffer(dst_port, 0, buffer, m);
|
||||
|
||||
if (sent)
|
||||
port_statistics[dst_port].tx += sent;
|
||||
|
||||
}
|
||||
/* main processing loop */
|
||||
static void
|
||||
l2fwd_main_loop(benetel_t *bs)
|
||||
{
|
||||
struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
|
||||
struct rte_mbuf *m;
|
||||
int sent;
|
||||
unsigned lcore_id;
|
||||
uint64_t prev_tsc, diff_tsc, cur_tsc, timer_tsc;
|
||||
unsigned i, j, portid, nb_rx;
|
||||
struct lcore_queue_conf *qconf;
|
||||
const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S *
|
||||
BURST_TX_DRAIN_US;
|
||||
struct rte_eth_dev_tx_buffer *buffer;
|
||||
prev_tsc = 0;
|
||||
timer_tsc = 0;
|
||||
lcore_id = rte_lcore_id();
|
||||
qconf = &lcore_queue_conf[lcore_id];
|
||||
if (qconf->n_rx_port == 0) {
|
||||
RTE_LOG(INFO, L2FWD, "lcore %u has nothing to do\n", lcore_id);
|
||||
return;
|
||||
}
|
||||
RTE_LOG(INFO, L2FWD, "entering main loop on lcore %u\n", lcore_id);
|
||||
for (i = 0; i < qconf->n_rx_port; i++) {
|
||||
portid = qconf->rx_port_list[i];
|
||||
RTE_LOG(INFO, L2FWD, " -- lcoreid=%u portid=%u\n", lcore_id,
|
||||
portid);
|
||||
}
|
||||
while (!force_quit) {
|
||||
cur_tsc = rte_rdtsc();
|
||||
/*
|
||||
* TX burst queue drain
|
||||
*/
|
||||
diff_tsc = cur_tsc - prev_tsc;
|
||||
if (unlikely(diff_tsc > drain_tsc)) {
|
||||
for (i = 0; i < qconf->n_rx_port; i++) {
|
||||
portid = l2fwd_dst_ports[qconf->rx_port_list[i]];
|
||||
buffer = tx_buffer[portid];
|
||||
sent = rte_eth_tx_buffer_flush(portid, 0, buffer);
|
||||
if (sent)
|
||||
port_statistics[portid].tx += sent;
|
||||
}
|
||||
/* if timer is enabled */
|
||||
if (timer_period > 0) {
|
||||
/* advance the timer */
|
||||
timer_tsc += diff_tsc;
|
||||
/* if timer has reached its timeout */
|
||||
if (unlikely(timer_tsc >= timer_period)) {
|
||||
/* do this only on master core */
|
||||
if (lcore_id == rte_get_master_lcore()) {
|
||||
print_stats();
|
||||
/* reset the timer */
|
||||
timer_tsc = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev_tsc = cur_tsc;
|
||||
}
|
||||
/*
|
||||
* Read packet from RX queues
|
||||
*/
|
||||
for (i = 0; i < qconf->n_rx_port; i++) {
|
||||
portid = qconf->rx_port_list[i];
|
||||
nb_rx = rte_eth_rx_burst(portid, 0,
|
||||
pkts_burst, MAX_PKT_BURST);
|
||||
port_statistics[portid].rx += nb_rx;
|
||||
for (j = 0; j < nb_rx; j++) {
|
||||
m = pkts_burst[j];
|
||||
rte_prefetch0(rte_pktmbuf_mtod(m, void *));
|
||||
l2fwd_simple_forward(m, portid, bs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static int
|
||||
l2fwd_launch_one_lcore(void *bs)
|
||||
{
|
||||
l2fwd_main_loop(bs);
|
||||
return 0;
|
||||
}
|
||||
/* display usage */
|
||||
static void
|
||||
l2fwd_usage(const char *prgname)
|
||||
{
|
||||
printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n"
|
||||
" -p PORTMASK: hexadecimal bitmask of ports to configure\n"
|
||||
" -q NQ: number of queue (=ports) per lcore (default is 1)\n"
|
||||
" -T PERIOD: statistics will be refreshed each PERIOD seconds (0 to disable, 10 default, 86400 maximum)\n"
|
||||
" --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n"
|
||||
" When enabled:\n"
|
||||
" - The source MAC address is replaced by the TX port MAC address\n"
|
||||
" - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n",
|
||||
prgname);
|
||||
}
|
||||
static int
|
||||
l2fwd_parse_portmask(const char *portmask)
|
||||
{
|
||||
char *end = NULL;
|
||||
unsigned long pm;
|
||||
/* parse hexadecimal string */
|
||||
pm = strtoul(portmask, &end, 16);
|
||||
if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
|
||||
return -1;
|
||||
if (pm == 0)
|
||||
return -1;
|
||||
return pm;
|
||||
}
|
||||
static unsigned int
|
||||
l2fwd_parse_nqueue(const char *q_arg)
|
||||
{
|
||||
char *end = NULL;
|
||||
unsigned long n;
|
||||
/* parse hexadecimal string */
|
||||
n = strtoul(q_arg, &end, 10);
|
||||
if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
|
||||
return 0;
|
||||
if (n == 0)
|
||||
return 0;
|
||||
if (n >= MAX_RX_QUEUE_PER_LCORE)
|
||||
return 0;
|
||||
return n;
|
||||
}
|
||||
static int
|
||||
l2fwd_parse_timer_period(const char *q_arg)
|
||||
{
|
||||
char *end = NULL;
|
||||
int n;
|
||||
/* parse number string */
|
||||
n = strtol(q_arg, &end, 10);
|
||||
if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
|
||||
return -1;
|
||||
if (n >= MAX_TIMER_PERIOD)
|
||||
return -1;
|
||||
return n;
|
||||
}
|
||||
static const char short_options[] =
|
||||
"p:" /* portmask */
|
||||
"q:" /* number of queues */
|
||||
"T:" /* timer period */
|
||||
;
|
||||
#define CMD_LINE_OPT_MAC_UPDATING "mac-updating"
|
||||
#define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating"
|
||||
enum {
|
||||
/* long options mapped to a short option */
|
||||
/* first long only option value must be >= 256, so that we won't
|
||||
* conflict with short options */
|
||||
CMD_LINE_OPT_MIN_NUM = 256,
|
||||
};
|
||||
static const struct option lgopts[] = {
|
||||
{ CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1},
|
||||
{ CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
/* Parse the argument given in the command line of the application */
|
||||
static int
|
||||
l2fwd_parse_args(int argc, char **argv)
|
||||
{
|
||||
int opt, ret, timer_secs;
|
||||
char **argvopt;
|
||||
int option_index;
|
||||
char *prgname = argv[0];
|
||||
argvopt = argv;
|
||||
while ((opt = getopt_long(argc, argvopt, short_options,
|
||||
lgopts, &option_index)) != EOF) {
|
||||
switch (opt) {
|
||||
/* portmask */
|
||||
case 'p':
|
||||
l2fwd_enabled_port_mask = l2fwd_parse_portmask(optarg);
|
||||
if (l2fwd_enabled_port_mask == 0) {
|
||||
printf("invalid portmask\n");
|
||||
l2fwd_usage(prgname);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
/* nqueue */
|
||||
case 'q':
|
||||
l2fwd_rx_queue_per_lcore = l2fwd_parse_nqueue(optarg);
|
||||
if (l2fwd_rx_queue_per_lcore == 0) {
|
||||
printf("invalid queue number\n");
|
||||
l2fwd_usage(prgname);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
/* timer period */
|
||||
case 'T':
|
||||
timer_secs = l2fwd_parse_timer_period(optarg);
|
||||
if (timer_secs < 0) {
|
||||
printf("invalid timer period\n");
|
||||
l2fwd_usage(prgname);
|
||||
return -1;
|
||||
}
|
||||
timer_period = timer_secs;
|
||||
break;
|
||||
/* long options */
|
||||
case 0:
|
||||
break;
|
||||
case 'proc-type':
|
||||
break;
|
||||
case 'socket-mem':
|
||||
break;
|
||||
case 'file-prefix':
|
||||
l2fwd_rx_queue_per_lcore = l2fwd_parse_nqueue(optarg);
|
||||
if (l2fwd_rx_queue_per_lcore == 0) {
|
||||
printf("invalid queue number\n");
|
||||
l2fwd_usage(prgname);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
/* timer period */
|
||||
/* timer period */
|
||||
/* timer period */
|
||||
default:
|
||||
l2fwd_usage(prgname);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (optind >= 0)
|
||||
argv[optind-1] = prgname;
|
||||
ret = optind-1;
|
||||
optind = 1; /* reset getopt lib */
|
||||
return ret;
|
||||
}
|
||||
/* Check the link status of all ports in up to 9s, and print them finally */
|
||||
static void
|
||||
check_all_ports_link_status(uint32_t port_mask)
|
||||
{
|
||||
#define CHECK_INTERVAL 100 /* 100ms */
|
||||
#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
|
||||
uint16_t portid;
|
||||
uint8_t count, all_ports_up, print_flag = 0;
|
||||
struct rte_eth_link link;
|
||||
printf("\nChecking link status");
|
||||
fflush(stdout);
|
||||
for (count = 0; count <= MAX_CHECK_TIME; count++) {
|
||||
if (force_quit)
|
||||
return;
|
||||
all_ports_up = 1;
|
||||
RTE_ETH_FOREACH_DEV(portid) {
|
||||
if (force_quit)
|
||||
return;
|
||||
if ((port_mask & (1 << portid)) == 0)
|
||||
continue;
|
||||
memset(&link, 0, sizeof(link));
|
||||
rte_eth_link_get_nowait(portid, &link);
|
||||
/* print link status if flag set */
|
||||
if (print_flag == 1) {
|
||||
if (link.link_status)
|
||||
printf(
|
||||
"Port%d Link Up. Speed %u Mbps - %s\n",
|
||||
portid, link.link_speed,
|
||||
(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
|
||||
("full-duplex") : ("half-duplex\n"));
|
||||
else
|
||||
printf("Port %d Link Down\n", portid);
|
||||
continue;
|
||||
}
|
||||
/* clear all_ports_up flag if any link down */
|
||||
if (link.link_status == ETH_LINK_DOWN) {
|
||||
all_ports_up = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* after finally printing all link status, get out */
|
||||
if (print_flag == 1)
|
||||
break;
|
||||
if (all_ports_up == 0) {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
rte_delay_ms(CHECK_INTERVAL);
|
||||
}
|
||||
/* set the print_flag if all ports up or timeout */
|
||||
if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
|
||||
print_flag = 1;
|
||||
printf("done\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
static void
|
||||
signal_handler(int signum)
|
||||
{
|
||||
if (signum == SIGINT || signum == SIGTERM) {
|
||||
printf("\n\nSignal %d received, preparing to exit...\n",
|
||||
signum);
|
||||
force_quit = true;
|
||||
}
|
||||
}
|
||||
int
|
||||
dpdk_main(int argc, char **argv, benetel_t *bs)
|
||||
{
|
||||
struct lcore_queue_conf *qconf;
|
||||
int ret;
|
||||
uint16_t nb_ports;
|
||||
uint16_t nb_ports_available = 0;
|
||||
uint16_t portid, last_port;
|
||||
unsigned lcore_id, rx_lcore_id;
|
||||
unsigned nb_ports_in_mask = 0;
|
||||
unsigned int nb_lcores = 0;
|
||||
unsigned int nb_mbufs;
|
||||
/* init EAL */
|
||||
ret = rte_eal_init(argc, argv);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n");
|
||||
argc -= ret;
|
||||
argv += ret;
|
||||
force_quit = false;
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
/* parse application arguments (after the EAL ones) */
|
||||
ret = l2fwd_parse_args(argc, argv);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE, "Invalid L2FWD arguments\n");
|
||||
printf("MAC updating %s\n", mac_updating ? "enabled" : "disabled");
|
||||
/* convert to number of cycles */
|
||||
timer_period *= rte_get_timer_hz();
|
||||
nb_ports = rte_eth_dev_count_avail();
|
||||
if (nb_ports == 0)
|
||||
rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
|
||||
/* check port mask to possible port mask */
|
||||
if (l2fwd_enabled_port_mask & ~((1 << nb_ports) - 1))
|
||||
rte_exit(EXIT_FAILURE, "Invalid portmask; possible (0x%x)\n",
|
||||
(1 << nb_ports) - 1);
|
||||
/* reset l2fwd_dst_ports */
|
||||
for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++)
|
||||
l2fwd_dst_ports[portid] = 0;
|
||||
last_port = 0;
|
||||
/*
|
||||
* Each logical core is assigned a dedicated TX queue on each port.
|
||||
*/
|
||||
RTE_ETH_FOREACH_DEV(portid) {
|
||||
/* skip ports that are not enabled */
|
||||
if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
|
||||
continue;
|
||||
if (nb_ports_in_mask % 2) {
|
||||
l2fwd_dst_ports[portid] = last_port;
|
||||
l2fwd_dst_ports[last_port] = portid;
|
||||
}
|
||||
else
|
||||
last_port = portid;
|
||||
nb_ports_in_mask++;
|
||||
}
|
||||
if (nb_ports_in_mask % 2) {
|
||||
printf("Notice: odd number of ports in portmask.\n");
|
||||
l2fwd_dst_ports[last_port] = last_port;
|
||||
}
|
||||
rx_lcore_id = 0;
|
||||
qconf = NULL;
|
||||
/* Initialize the port/queue configuration of each logical core */
|
||||
RTE_ETH_FOREACH_DEV(portid) {
|
||||
/* skip ports that are not enabled */
|
||||
if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
|
||||
continue;
|
||||
/* get the lcore_id for this port */
|
||||
while (rte_lcore_is_enabled(rx_lcore_id) == 0 ||
|
||||
lcore_queue_conf[rx_lcore_id].n_rx_port ==
|
||||
l2fwd_rx_queue_per_lcore) {
|
||||
rx_lcore_id++;
|
||||
if (rx_lcore_id >= RTE_MAX_LCORE)
|
||||
rte_exit(EXIT_FAILURE, "Not enough cores\n");
|
||||
}
|
||||
if (qconf != &lcore_queue_conf[rx_lcore_id]) {
|
||||
/* Assigned a new logical core in the loop above. */
|
||||
qconf = &lcore_queue_conf[rx_lcore_id];
|
||||
nb_lcores++;
|
||||
}
|
||||
qconf->rx_port_list[qconf->n_rx_port] = portid;
|
||||
qconf->n_rx_port++;
|
||||
printf("Lcore %u: RX port %u\n", rx_lcore_id, portid);
|
||||
}
|
||||
nb_mbufs = RTE_MAX(nb_ports * (nb_rxd + nb_txd + MAX_PKT_BURST +
|
||||
nb_lcores * MEMPOOL_CACHE_SIZE), 8192U);
|
||||
/* create the mbuf pool */
|
||||
//DD Debug
|
||||
//printf ("Proc Type %d \n", rte_eal_process_type());
|
||||
l2fwd_pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", nb_mbufs,
|
||||
MEMPOOL_CACHE_SIZE, 0, 9680,
|
||||
rte_socket_id());
|
||||
if (l2fwd_pktmbuf_pool == NULL)
|
||||
rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n");
|
||||
/* Initialise each port */
|
||||
RTE_ETH_FOREACH_DEV(portid) {
|
||||
struct rte_eth_rxconf rxq_conf;
|
||||
struct rte_eth_txconf txq_conf;
|
||||
struct rte_eth_conf local_port_conf = port_conf;
|
||||
struct rte_eth_dev_info dev_info;
|
||||
/* skip ports that are not enabled */
|
||||
if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) {
|
||||
printf("Skipping disabled port %u\n", portid);
|
||||
continue;
|
||||
}
|
||||
nb_ports_available++;
|
||||
/* init port */
|
||||
printf("Initializing port %u... ", portid);
|
||||
fflush(stdout);
|
||||
rte_eth_dev_info_get(portid, &dev_info);
|
||||
if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
|
||||
local_port_conf.txmode.offloads |=
|
||||
DEV_TX_OFFLOAD_MBUF_FAST_FREE;
|
||||
ret = rte_eth_dev_configure(portid, 1, 1, &local_port_conf);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE, "Cannot configure device: err=%d, port=%u\n",
|
||||
ret, portid);
|
||||
ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd,
|
||||
&nb_txd);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE,
|
||||
"Cannot adjust number of descriptors: err=%d, port=%u\n",
|
||||
ret, portid);
|
||||
rte_eth_macaddr_get(portid,&l2fwd_ports_eth_addr[portid]);
|
||||
/* init one RX queue */
|
||||
fflush(stdout);
|
||||
rxq_conf = dev_info.default_rxconf;
|
||||
rxq_conf.offloads = local_port_conf.rxmode.offloads;
|
||||
ret = rte_eth_rx_queue_setup(portid, 0, nb_rxd,
|
||||
rte_eth_dev_socket_id(portid),
|
||||
&rxq_conf,
|
||||
l2fwd_pktmbuf_pool);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup:err=%d, port=%u\n",
|
||||
ret, portid);
|
||||
/* init one TX queue on each port */
|
||||
fflush(stdout);
|
||||
txq_conf = dev_info.default_txconf;
|
||||
txq_conf.offloads = local_port_conf.txmode.offloads;
|
||||
ret = rte_eth_tx_queue_setup(portid, 0, nb_txd,
|
||||
rte_eth_dev_socket_id(portid),
|
||||
&txq_conf);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup:err=%d, port=%u\n",
|
||||
ret, portid);
|
||||
/* Initialize TX buffers */
|
||||
tx_buffer[portid] = rte_zmalloc_socket("tx_buffer",
|
||||
RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0,
|
||||
rte_eth_dev_socket_id(portid));
|
||||
if (tx_buffer[portid] == NULL)
|
||||
rte_exit(EXIT_FAILURE, "Cannot allocate buffer for tx on port %u\n",
|
||||
portid);
|
||||
rte_eth_tx_buffer_init(tx_buffer[portid], MAX_PKT_BURST);
|
||||
ret = rte_eth_tx_buffer_set_err_callback(tx_buffer[portid],
|
||||
rte_eth_tx_buffer_count_callback,
|
||||
&port_statistics[portid].dropped);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE,
|
||||
"Cannot set error callback for tx buffer on port %u\n",
|
||||
portid);
|
||||
/* Start device */
|
||||
ret = rte_eth_dev_start(portid);
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE, "rte_eth_dev_start:err=%d, port=%u\n",
|
||||
ret, portid);
|
||||
printf("done: \n");
|
||||
rte_eth_promiscuous_enable(portid);
|
||||
printf("Port %u, MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n\n",
|
||||
portid,
|
||||
l2fwd_ports_eth_addr[portid].addr_bytes[0],
|
||||
l2fwd_ports_eth_addr[portid].addr_bytes[1],
|
||||
l2fwd_ports_eth_addr[portid].addr_bytes[2],
|
||||
l2fwd_ports_eth_addr[portid].addr_bytes[3],
|
||||
l2fwd_ports_eth_addr[portid].addr_bytes[4],
|
||||
l2fwd_ports_eth_addr[portid].addr_bytes[5]);
|
||||
/* initialize port stats */
|
||||
memset(&port_statistics, 0, sizeof(port_statistics));
|
||||
}
|
||||
if (!nb_ports_available) {
|
||||
rte_exit(EXIT_FAILURE,
|
||||
"All available ports are disabled. Please set portmask.\n");
|
||||
}
|
||||
check_all_ports_link_status(l2fwd_enabled_port_mask);
|
||||
ret = 0;
|
||||
/* launch per-lcore init on every lcore */
|
||||
rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, bs, CALL_MASTER);
|
||||
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
|
||||
if (rte_eal_wait_lcore(lcore_id) < 0) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
RTE_ETH_FOREACH_DEV(portid) {
|
||||
if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
|
||||
continue;
|
||||
printf("Closing port %d...", portid);
|
||||
rte_eth_dev_stop(portid);
|
||||
rte_eth_dev_close(portid);
|
||||
printf(" Done\n");
|
||||
}
|
||||
printf("Bye...\n");
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include "low.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void store_ul(benetel_t *bs, ul_packet_t *ul)
|
||||
{
|
||||
#if 0
|
||||
struct timespec t;
|
||||
static struct timespec old;
|
||||
clock_gettime(CLOCK_REALTIME, &t);
|
||||
printf("store_ul %d.%ld (%ld)\n", (int)(t.tv_sec % 60), t.tv_nsec, t.tv_nsec - old.tv_nsec);
|
||||
old = t;
|
||||
#endif
|
||||
|
||||
int a = ul->antenna;
|
||||
|
||||
if (ul->slot != bs->next_slot[a] ||
|
||||
ul->symbol != bs->next_symbol[a]) {
|
||||
printf("%s: error, antenna %d expected frame.sl.symbol %d.%d.%d, got %d.%d.%d\n",
|
||||
__FUNCTION__,
|
||||
a, bs->expected_benetel_frame[a], bs->next_slot[a], bs->next_symbol[a],
|
||||
ul->frame, ul->slot, ul->symbol);
|
||||
}
|
||||
|
||||
/* fill missing data with 0s */
|
||||
while (ul->slot != bs->next_slot[a] ||
|
||||
ul->symbol != bs->next_symbol[a]) {
|
||||
lock_ul_buffer(bs->buffers, bs->next_slot[a]);
|
||||
if (bs->buffers->ul_busy[a][bs->next_slot[a]] & (1 << bs->next_symbol[a])) {
|
||||
printf("%s: warning, antenna %d UL overflow (sl.symbol %d.%d)\n", __FUNCTION__,
|
||||
a, bs->next_slot[a], bs->next_symbol[a]);
|
||||
}
|
||||
|
||||
memset(bs->buffers->ul[a][bs->next_slot[a]] + bs->next_symbol[a] * 1272*4,
|
||||
0, 1272*4);
|
||||
bs->buffers->ul_busy[a][bs->next_slot[a]] |= (1 << bs->next_symbol[a]);
|
||||
signal_ul_buffer(bs->buffers, bs->next_slot[a]);
|
||||
unlock_ul_buffer(bs->buffers, bs->next_slot[a]);
|
||||
|
||||
bs->next_symbol[a]++;
|
||||
if (bs->next_symbol[a] == 14) {
|
||||
bs->next_symbol[a] = 0;
|
||||
bs->next_slot[a] = (bs->next_slot[a] + 1) % 20;
|
||||
if (bs->next_slot[a] == 0) {
|
||||
bs->expected_benetel_frame[a]++;
|
||||
bs->expected_benetel_frame[a] &= 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lock_ul_buffer(bs->buffers, bs->next_slot[a]);
|
||||
if (bs->buffers->ul_busy[a][bs->next_slot[a]] & (1 << bs->next_symbol[a])) {
|
||||
printf("%s: warning, antenna %d UL overflow (sl.symbol %d.%d)\n", __FUNCTION__,
|
||||
a, bs->next_slot[a], bs->next_symbol[a]);
|
||||
}
|
||||
|
||||
memcpy(bs->buffers->ul[a][bs->next_slot[a]] + bs->next_symbol[a] * 1272*4,
|
||||
ul->iq, 1272*4);
|
||||
bs->buffers->ul_busy[a][bs->next_slot[a]] |= (1 << bs->next_symbol[a]);
|
||||
signal_ul_buffer(bs->buffers, bs->next_slot[a]);
|
||||
unlock_ul_buffer(bs->buffers, bs->next_slot[a]);
|
||||
|
||||
bs->next_symbol[a]++;
|
||||
if (bs->next_symbol[a] == 14) {
|
||||
bs->next_symbol[a] = 0;
|
||||
bs->next_slot[a] = (bs->next_slot[a] + 1) % 20;
|
||||
if (bs->next_slot[a] == 0) {
|
||||
bs->expected_benetel_frame[a]++;
|
||||
bs->expected_benetel_frame[a] &= 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void store_prach(benetel_t *bs, int frame, int slot, void *data)
|
||||
{
|
||||
static int last_frame = -1;
|
||||
static int last_slot = -1;
|
||||
/* hack: antenna number is always 0, discard second packet with same f/sf */
|
||||
//if (frame == last_frame && slot == last_slot) return;
|
||||
if (frame == last_frame && slot == last_slot) { printf("got f.sl %d.%d twice\n", frame, slot); return; }
|
||||
last_frame = frame;
|
||||
last_slot = slot;
|
||||
|
||||
lock_ul_buffer(bs->buffers, slot);
|
||||
if (bs->buffers->prach_busy[slot]) {
|
||||
printf("store_prach: fatal: previous prach buffer not processed\n");
|
||||
unlock_ul_buffer(bs->buffers, slot);
|
||||
return;
|
||||
}
|
||||
bs->buffers->prach_busy[slot] = 1;
|
||||
memcpy(bs->buffers->prach[slot], data, 839*4);
|
||||
signal_ul_buffer(bs->buffers, slot);
|
||||
unlock_ul_buffer(bs->buffers, slot);
|
||||
|
||||
}
|
||||
|
||||
void *benetel_start_dpdk(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line);
|
||||
|
||||
void *benetel_start(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line)
|
||||
{
|
||||
if (!strcmp(ifname, "dpdk"))
|
||||
return benetel_start_dpdk(ifname, buffers, dpdk_main_command_line);
|
||||
printf("benetel: fatal: interface %s not supported, only dpdpk is supported\n", ifname);
|
||||
exit(1);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef _BENETEL_5G_LOW_H_
|
||||
#define _BENETEL_5G_LOW_H_
|
||||
|
||||
#include "shared_buffers.h"
|
||||
|
||||
typedef struct {
|
||||
shared_buffers *buffers;
|
||||
/* [2] is for two antennas */
|
||||
int next_slot[2];
|
||||
int next_symbol[2];
|
||||
int expected_benetel_frame[2];
|
||||
char *dpdk_main_command_line;
|
||||
} benetel_t;
|
||||
|
||||
typedef struct {
|
||||
int frame;
|
||||
int slot;
|
||||
int symbol;
|
||||
int antenna;
|
||||
unsigned char iq[5088];
|
||||
} ul_packet_t;
|
||||
|
||||
void *benetel_start(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line);
|
||||
|
||||
void store_ul(benetel_t *bs, ul_packet_t *ul);
|
||||
void store_prach(benetel_t *bs, int frame, int slot, void *data);
|
||||
|
||||
#endif /* _BENETEL_5G_LOW_H_ */
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/* those 2 lines for CPU_SET */
|
||||
#define _GNU_SOURCE
|
||||
#include <sched.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "low.h"
|
||||
|
||||
int dpdk_main(int argc, char **argv, benetel_t *);
|
||||
|
||||
void *dpdk_thread(void *_bs)
|
||||
{
|
||||
benetel_t *bs = _bs;
|
||||
int n = 0;
|
||||
char **v = NULL; // = { "softmodem", "-n", "2", "--file-prefix", "pg2", "-l", "6", "--", "-p", "0x1" };
|
||||
char *s = bs->dpdk_main_command_line;
|
||||
char *tok;
|
||||
|
||||
while ((tok = strtok(s, " ")) != NULL) {
|
||||
n++;
|
||||
v = realloc(v, n * sizeof(char *));
|
||||
if (v == NULL) {
|
||||
printf("%s: out of memory\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
v[n-1] = tok;
|
||||
s = NULL;
|
||||
}
|
||||
|
||||
dpdk_main(n, v, bs);
|
||||
exit(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *benetel_start_dpdk(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line)
|
||||
{
|
||||
benetel_t *bs;
|
||||
|
||||
bs = calloc(1, sizeof(benetel_t));
|
||||
if (bs == NULL) {
|
||||
printf("%s: out of memory\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bs->buffers = buffers;
|
||||
|
||||
bs->expected_benetel_frame[0] = 255;
|
||||
bs->expected_benetel_frame[1] = 255;
|
||||
|
||||
bs->dpdk_main_command_line = dpdk_main_command_line;
|
||||
|
||||
pthread_attr_t attr;
|
||||
|
||||
if (pthread_attr_init(&attr) != 0) {
|
||||
printf("pthread_attr_init failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
cpu_set_t cpuset;
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(10,&cpuset);
|
||||
if (pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset) != 0) {
|
||||
printf("pthread_attr_setaffinity_np failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) != 0) {
|
||||
printf("pthread_attr_setschedpolicy failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct sched_param params;
|
||||
params.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
||||
if (sched_get_priority_max(SCHED_FIFO) == -1) {
|
||||
printf("sched_get_priority_max failed\n");
|
||||
exit(1);
|
||||
}
|
||||
if (pthread_attr_setschedparam(&attr, ¶ms) != 0) {
|
||||
printf("pthread_setschedparam failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pthread_t t;
|
||||
if (pthread_create(&t, &attr, dpdk_thread, bs) != 0) {
|
||||
printf("%s: thread creation failed\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pthread_attr_destroy(&attr) != 0) {
|
||||
printf("pthread_attr_init failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return bs;
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include "shared_buffers.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void init_buffers(shared_buffers *s)
|
||||
{
|
||||
int slot;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
|
||||
for (slot = 0; slot < 20; slot++) {
|
||||
if (pthread_mutex_init(&s->m_dl[slot], NULL) != 0 ||
|
||||
pthread_cond_init(&s->c_dl[slot], NULL) != 0 ||
|
||||
pthread_mutex_init(&s->m_ul[slot], NULL) != 0 ||
|
||||
pthread_cond_init(&s->c_ul[slot], NULL) != 0) {
|
||||
printf("%s: error initializing mutex/cond\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* the gNB's first transmitted DL slot is 6 but the device
|
||||
* needs to have slots 1, 2 and 3, 4 and 5 ready. Let's pretend
|
||||
* they are ready.
|
||||
*/
|
||||
s->dl_busy[0][1] = 0x3fff;
|
||||
s->dl_busy[0][2] = 0x3fff;
|
||||
s->dl_busy[0][3] = 0x3fff;
|
||||
s->dl_busy[0][4] = 0x3fff;
|
||||
s->dl_busy[0][5] = 0x3fff;
|
||||
s->dl_busy[1][1] = 0x3fff;
|
||||
s->dl_busy[1][2] = 0x3fff;
|
||||
s->dl_busy[1][3] = 0x3fff;
|
||||
s->dl_busy[1][4] = 0x3fff;
|
||||
s->dl_busy[1][5] = 0x3fff;
|
||||
}
|
||||
|
||||
void lock_dl_buffer(shared_buffers *s, int slot)
|
||||
{
|
||||
if (pthread_mutex_lock(&s->m_dl[slot]) != 0) {
|
||||
printf("%s: fatal: lock fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_dl_buffer(shared_buffers *s, int slot)
|
||||
{
|
||||
if (pthread_mutex_unlock(&s->m_dl[slot]) != 0) {
|
||||
printf("%s: fatal: unlock fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void wait_dl_buffer(shared_buffers *s, int slot)
|
||||
{
|
||||
if (pthread_cond_wait(&s->c_dl[slot], &s->m_dl[slot]) != 0) {
|
||||
printf("%s: fatal: cond_wait fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void signal_dl_buffer(shared_buffers *s, int slot)
|
||||
{
|
||||
if (pthread_cond_broadcast(&s->c_dl[slot]) != 0) {
|
||||
printf("%s: fatal: cond_broadcast fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void lock_ul_buffer(shared_buffers *s, int slot)
|
||||
{
|
||||
if (pthread_mutex_lock(&s->m_ul[slot]) != 0) {
|
||||
printf("%s: fatal: lock fails\n", __FUNCTION__);
|
||||
printf("%s: fatal: lock fails slot %d\n", __FUNCTION__, slot);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_ul_buffer(shared_buffers *s, int slot)
|
||||
{
|
||||
if (pthread_mutex_unlock(&s->m_ul[slot]) != 0) {
|
||||
printf("%s: fatal: unlock fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void wait_ul_buffer(shared_buffers *s, int slot)
|
||||
{
|
||||
if (pthread_cond_wait(&s->c_ul[slot], &s->m_ul[slot]) != 0) {
|
||||
printf("%s: fatal: cond_wait fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void signal_ul_buffer(shared_buffers *s, int slot)
|
||||
{
|
||||
if (pthread_cond_broadcast(&s->c_ul[slot]) != 0) {
|
||||
printf("%s: fatal: cond_broadcast fails\n", __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef _BENETEL_5G_SHARED_BUFFERS_H_
|
||||
#define _BENETEL_5G_SHARED_BUFFERS_H_
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
/* [2] is for two antennas */
|
||||
unsigned char dl[2][20][14*1272*4];
|
||||
unsigned char ul[2][20][14*1272*4];
|
||||
uint16_t dl_busy[2][20];
|
||||
uint16_t ul_busy[2][20];
|
||||
|
||||
pthread_mutex_t m_ul[20];
|
||||
pthread_cond_t c_ul[20];
|
||||
|
||||
pthread_mutex_t m_dl[20];
|
||||
pthread_cond_t c_dl[20];
|
||||
|
||||
unsigned char prach[20][849*4];
|
||||
unsigned char prach_busy[20];
|
||||
|
||||
/* statistics/error counting */
|
||||
int ul_overflow;
|
||||
int dl_underflow;
|
||||
} shared_buffers;
|
||||
|
||||
void init_buffers(shared_buffers *s);
|
||||
|
||||
void lock_dl_buffer(shared_buffers *s, int slot);
|
||||
void unlock_dl_buffer(shared_buffers *s, int slot);
|
||||
void wait_dl_buffer(shared_buffers *s, int slot);
|
||||
void signal_dl_buffer(shared_buffers *s, int slot);
|
||||
|
||||
void lock_ul_buffer(shared_buffers *s, int slot);
|
||||
void unlock_ul_buffer(shared_buffers *s, int slot);
|
||||
void wait_ul_buffer(shared_buffers *s, int slot);
|
||||
void signal_ul_buffer(shared_buffers *s, int slot);
|
||||
|
||||
#endif /* _BENETEL_5G_SHARED_BUFFERS_H_ */
|
||||
Reference in New Issue
Block a user