/*******************************************************************************
OpenAirInterface
Copyright(c) 1999 - 2014 Eurecom
OpenAirInterface is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenAirInterface is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenAirInterface.The full GNU General Public License is
included in this distribution in the file called "COPYING". If not,
see .
Contact Information
OpenAirInterface Admin: openair_admin@eurecom.fr
OpenAirInterface Tech : openair_tech@eurecom.fr
OpenAirInterface Dev : openair4g-devel@eurecom.fr
Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
*******************************************************************************/
/*! \file ethernet_lib.c
* \brief API to stream I/Q samples over standard ethernet
* \author 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "common_lib.h"
#include "ethernet_lib.h"
//#define DEBUG 1
int num_devices_eth = 0;
int dest_addr_len[MAX_INST];
char sendbuf[MAX_INST][BUF_SIZ]; /*TODO*/
/*! \fn static int eth_socket_init(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
*/
static int eth_socket_init(openair0_device *device);
/*! \fn static int eth_set_dev_conf(openair0_device *device)
* \brief
* \param[in] *device openair device
* \param[out]
* \return 0 on success, otherwise -1
* \note
* @ingroup _oai
*/
static int eth_set_dev_conf(openair0_device *device);
/*! \fn static int eth_get_dev_conf(openair0_device *device)
* \brief
* \param[in] *device openair device
* \param[out]
* \return 0 on success, otherwise -1
* \note
* @ingroup _oai
*/
static int eth_get_dev_conf(openair0_device *device);
int trx_eth_start(openair0_device *device) {
/* initialize socket */
if (eth_socket_init(device)!=0) {
return -1;
}
/* RRH gets openair0 device configuration BBU sets openair0 device configuration*/
if (device->func_type == BBU_FUNC) {
return eth_set_dev_conf(device);
} else {
return eth_get_dev_conf(device);
}
return 0;
}
int trx_eth_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int cc, int flags) {
int n_written=0,i;
uint16_t header_size=sizeof(int32_t) + sizeof(openair0_timestamp);
eth_state_t *eth = (eth_state_t*)device->priv;
int Mod_id = device->Mod_id;
int sendto_flag =0;
sendto_flag|=MSG_DONTWAIT;
for (i=0;isockfd[Mod_id],(nsamps<<2)+header_size, buff2, antenna_id);*/
while(n_written < nsamps) {
/* Send packet */
if ((n_written += sendto(eth->sockfd[Mod_id],
buff2,
(nsamps<<2)+header_size,
0,
(struct sockaddr*)ð->dest_addr[Mod_id],
dest_addr_len[Mod_id])) < 0) {
perror("ETHERNET WRITE");
exit(-1);
}
}
#if DEBUG
printf("Buffer head TX: nu=%d an_id=%d ts%d samples_send=%d i=%d data=%x\n",
*(int16_t *)buff2,
*(int16_t *)(buff2 + sizeof(int16_t)),
*(openair0_timestamp *)(buff2 + sizeof(int32_t)),
n_written>>2,i,*(int32_t *)(buff2 + 20*sizeof(int32_t)));
#endif
/* tx buffer values restored */
*(int32_t *)buff2 = temp0;
*(openair0_timestamp *)(buff2 + sizeof(int32_t)) = temp1;
}
return n_written;
}
int trx_eth_read(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) {
int bytes_received=0;
int block_cnt=0;
int ret=0,i;
uint16_t header_size=sizeof(int32_t) + sizeof(openair0_timestamp);
eth_state_t *eth = (eth_state_t*)device->priv;
int Mod_id = device->Mod_id;
for (i=0;isockfd[Mod_id],(nsamps<<2)+header_size, buff2, antenna_id);*/
while(bytes_received < (int)((nsamps<<2))) {
ret=recvfrom(eth->sockfd[Mod_id],
buff2+bytes_received,
(nsamps<<2)+header_size-bytes_received,
0,//MSG_DONTWAIT,
(struct sockaddr *)ð->dest_addr[Mod_id],
(socklen_t *)&dest_addr_len[Mod_id]);
if (ret==-1) {
if (errno == EAGAIN) {
perror("ETHERNET READ: ");
return((nsamps<<2) + header_size);
} else if (errno == EWOULDBLOCK) {
block_cnt++;
usleep(10);
if (block_cnt == 100) return(-1);
}
} else {
bytes_received+=ret;
}
}
#if DEBUG
printf("Buffer head RX: nu=%d an_id=%d ts%d samples_recv=%d i=%d data=%x\n",
*(int16_t *)buff2,
*(int16_t *)(buff2 + sizeof(int16_t)),
*(openair0_timestamp *)(buff2 + sizeof(int32_t)),
ret>>2,i,*(int32_t *)(buff2 + 20*sizeof(int32_t)));
#endif
/* store the timestamp value from packet's header */
*timestamp = *(openair0_timestamp *)(buff2 + sizeof(int32_t));
/* tx buffer values restored */
*(int32_t *)buff2 = temp0;
*(openair0_timestamp *)(buff2 + sizeof(int32_t)) = temp1;
}
return nsamps;
}
void trx_eth_end(openair0_device *device) {
eth_state_t *eth = (eth_state_t*)device->priv;
int Mod_id = device->Mod_id;
/*destroys socket only for the processes that call the eth_end fuction-- shutdown() for beaking the pipe */
if ( close(eth->sockfd[Mod_id]) <0 ) {
perror("ETHERNET: Failed to close socket");
exit(0);
} else {
printf("[RRH] socket for mod_id %d has been successfully closed.\n",Mod_id);
}
}
int trx_eth_request(openair0_device *device, void *msg, ssize_t msg_len) {
int Mod_id = device->Mod_id;
eth_state_t *eth = (eth_state_t*)device->priv;
/* BBU sends a message to RRH */
if (sendto(eth->sockfd[Mod_id],msg,msg_len,0,(struct sockaddr *)ð->dest_addr[Mod_id],dest_addr_len[Mod_id])==-1) {
perror("ETHERNET: ");
exit(0);
}
return 0;
}
int trx_eth_reply(openair0_device *device, void *msg, ssize_t msg_len) {
eth_state_t *eth = (eth_state_t*)device->priv;
int Mod_id = device->Mod_id;
/* RRH receives from BBU a message */
if (recvfrom(eth->sockfd[Mod_id],
msg,
msg_len,
0,
(struct sockaddr *)ð->dest_addr[Mod_id],
(socklen_t *)&dest_addr_len[Mod_id])==-1) {
perror("ETHERNET: ");
exit(0);
}
return 0;
}
static int eth_set_dev_conf(openair0_device *device) {
int Mod_id = device->Mod_id;
eth_state_t *eth = (eth_state_t*)device->priv;
void *msg;
ssize_t msg_len;
/* a BBU client sents to RRH 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(sizeof(openair0_config_t));
msg_len=sizeof(openair0_config_t);
memcpy(msg,(void*)&device->openair0_cfg,msg_len);
if (sendto(eth->sockfd[Mod_id],msg,msg_len,0,(struct sockaddr *)ð->dest_addr[Mod_id],dest_addr_len[Mod_id])==-1) {
perror("ETHERNET: ");
exit(0);
}
return 0;
}
static int eth_get_dev_conf(openair0_device *device) {
eth_state_t *eth = (eth_state_t*)device->priv;
int Mod_id = device->Mod_id;
char str[INET_ADDRSTRLEN];
void *msg;
ssize_t msg_len;
msg=malloc(sizeof(openair0_config_t));
msg_len=sizeof(openair0_config_t);
/* RRH receives from BBU openair0_config_t */
if (recvfrom(eth->sockfd[Mod_id],
msg,
msg_len,
0,
(struct sockaddr *)ð->dest_addr[Mod_id],
(socklen_t *)&dest_addr_len[Mod_id])==-1) {
perror("ETHERNET: ");
exit(0);
}
memcpy((void*)&device->openair0_cfg,msg,msg_len);
inet_ntop(AF_INET, &(eth->dest_addr[Mod_id].sin_addr), str, INET_ADDRSTRLEN);
device->openair0_cfg.remote_port =ntohs(eth->dest_addr[Mod_id].sin_port);
device->openair0_cfg.remote_ip=str;
/*apply additional configuration*/
//ethernet_tune (device, RING_PAR);
// printf("[RRH] write mod_%d %d to %s:%d\n",Mod_id,eth->sockfd[Mod_id],str,ntohs(eth->dest_addr[Mod_id].sin_port));
return 0;
}
int trx_eth_stop(int card) {
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);
}
static int eth_socket_init(openair0_device *device) {
int i = 0;
eth_state_t *eth = (eth_state_t*)device->priv;
int Mod_id = device->Mod_id;
char str[INET_ADDRSTRLEN];
const char *dest_ip;
int dest_port=0;
if (device->func_type == RRH_FUNC ) {
dest_ip = device->openair0_cfg.my_ip;
dest_port = device->openair0_cfg.my_port;
printf("[RRH] ip addr %s port %d\n",dest_ip, dest_port);
} else {
dest_ip = device->openair0_cfg.remote_ip;
dest_port = device->openair0_cfg.remote_port;
printf("[BBU] ip addr %s port %d\n",dest_ip, dest_port);
}
/* Open RAW socket to send on */
if ((eth->sockfd[Mod_id] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
perror("ETHERNET: Error opening socket");
exit(0);
}
/* initialize destination address */
for (i=0; i< MAX_INST; i++) {
bzero((void *)&(eth->dest_addr[i]), sizeof(eth->dest_addr[i]));
}
// bzero((void *)dest,sizeof(struct sockaddr_in));
eth->dest_addr[Mod_id].sin_family = AF_INET;
inet_pton(AF_INET,dest_ip,&(eth->dest_addr[Mod_id].sin_addr.s_addr));
eth->dest_addr[Mod_id].sin_port=htons(dest_port);
dest_addr_len[Mod_id] = sizeof(struct sockaddr_in);
inet_ntop(AF_INET, &(eth->dest_addr[Mod_id].sin_addr), str, INET_ADDRSTRLEN);
/* if RRH, then I am the server, so bind */
if (device->func_type == RRH_FUNC ) {
if (bind(eth->sockfd[Mod_id],(struct sockaddr *)ð->dest_addr[Mod_id], dest_addr_len[Mod_id])<0) {
perror("ETHERNET: Cannot bind to socket");
exit(0);
} else {
printf("[RRH] binding mod_%d to %s:%d\n",Mod_id,str,ntohs(eth->dest_addr[Mod_id].sin_port));
}
} else {
printf("[BBU] Connecting to %s:%d\n",str,ntohs(eth->dest_addr[Mod_id].sin_port));
}
return 0;
}
int ethernet_tune(openair0_device *device , eth_opt_t option) {
eth_state_t *eth = (eth_state_t*)device->priv;
int Mod_id=device->Mod_id;
unsigned int sndbuf_size=0, rcvbuf_size=0;
struct timeval snd_timeout, rcv_timeout;
struct ifreq ifr;
char system_cmd[256];
char* if_name=DEFAULT_IF;
/****************** socket level options ************************/
if (option== SND_BUF_SIZE) { /* transmit socket buffer size */
if (setsockopt(eth->sockfd[Mod_id],
SOL_SOCKET,
SO_SNDBUF,
&sndbuf_size,sizeof(sndbuf_size))) {
perror("[ETHERNET] setsockopt()");
} else {
printf( "sndbuf_size= %d bytes\n", sndbuf_size);
}
} else if (option== RCV_BUF_SIZE) { /* receive socket buffer size */
if (setsockopt(eth->sockfd[Mod_id],
SOL_SOCKET,
SO_RCVBUF,
&rcvbuf_size,sizeof(rcvbuf_size))) {
perror("[ETHERNET] setsockopt()");
} else {
printf( "rcvbuf_size= %d bytes\n", rcvbuf_size);
}
} else if (option==RCV_TIMEOUT) {
rcv_timeout.tv_sec = 0;
rcv_timeout.tv_usec = 180;//less than rt_period
if (setsockopt(eth->sockfd[Mod_id],
SOL_SOCKET,
SO_RCVTIMEO,
(char *)&rcv_timeout,sizeof(rcv_timeout))) {
perror("[ETHERNET] setsockopt()");
} else {
printf( "rcv_timeout= %d usecs\n", rcv_timeout.tv_usec);
}
} else if (option==SND_TIMEOUT) {
snd_timeout.tv_sec = 0;
snd_timeout.tv_usec = 180;//less than rt_period
if (setsockopt(eth->sockfd[Mod_id],
SOL_SOCKET,
SO_SNDTIMEO,
(char *)&snd_timeout,sizeof(snd_timeout))) {
perror("[ETHERNET] setsockopt()");
} else {
printf( "snd_timeout= %d usecs\n", snd_timeout.tv_usec);
}
}
/******************* interface level options *************************/
else if (option==MTU_SIZE) { /* change MTU of the eth interface */
ifr.ifr_addr.sa_family = AF_INET;
strncpy(ifr.ifr_name,if_name, sizeof(ifr.ifr_name));
ifr.ifr_mtu =8960;
if (ioctl(eth->sockfd[Mod_id],SIOCSIFMTU,(caddr_t)&ifr) < 0 )
perror ("[ETHERNET] Can't set the MTU");
else
printf("[ETHERNET] %s MTU size has changed to %d\n",DEFAULT_IF,ifr.ifr_mtu);
} else if (option==TX_Q_LEN) { /* change TX queue length of eth interface */
ifr.ifr_addr.sa_family = AF_INET;
strncpy(ifr.ifr_name,if_name, sizeof(ifr.ifr_name));
ifr.ifr_qlen =3000 ;
if (ioctl(eth->sockfd[Mod_id],SIOCSIFTXQLEN,(caddr_t)&ifr) < 0 )
perror ("[ETHERNET] Can't set the txqueuelen");
else
printf("[ETHERNET] %s txqueuelen size has changed to %d\n",DEFAULT_IF,ifr.ifr_qlen);
/******************* device level options *************************/
} else if (option==COALESCE_PAR) {
if (snprintf(system_cmd,sizeof(system_cmd),"ethtool -C %s rx-usecs 3",DEFAULT_IF) > 0) {
system(system_cmd);
printf("[ETHERNET] Coalesce parameters %s\n",system_cmd);
} else {
perror("[ETHERNET] Can't set coalesce parameters\n");
}
} else if (option==PAUSE_PAR ) {
if (snprintf(system_cmd,sizeof(system_cmd),"ethtool -A %s autoneg off rx off tx off",DEFAULT_IF) > 0) {
system(system_cmd);
printf("[ETHERNET] Pause parameters %s\n",system_cmd);
} else {
perror("[ETHERNET] Can't set pause parameters\n");
}
} else if (option==RING_PAR ) {
if (snprintf(system_cmd,sizeof(system_cmd),"ethtool -G %s rx 4096 tx 4096",DEFAULT_IF) > 0) {
system(system_cmd);
printf("[ETHERNET] Ring parameters %s\n",system_cmd);
} else {
perror("[ETHERNET] Can't set ring parameters\n");
}
}
return 0;
}
int openair0_dev_init_eth(openair0_device *device, openair0_config_t *openair0_cfg) {
eth_state_t *eth = (eth_state_t*)malloc(sizeof(eth_state_t));
int card = 0;
memset(eth, 0, sizeof(eth_state_t));
eth->buffer_size = (unsigned int)openair0_cfg[card].samples_per_packet*sizeof(int32_t); // buffer size = 4096 for sample_len of 1024
eth->sample_rate = (unsigned int)openair0_cfg[card].sample_rate;
device->priv = eth;
printf("ETHERNET: Initializing openair0_device for %s ...\n", ((device->func_type == BBU_FUNC) ? "BBU": "RRH"));
device->Mod_id = num_devices_eth++;
device->trx_start_func = trx_eth_start;
device->trx_request_func = trx_eth_request;
device->trx_reply_func = trx_eth_reply;
device->trx_write_func = trx_eth_write;
device->trx_read_func = trx_eth_read;
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;
memcpy((void*)&device->openair0_cfg,(void*)openair0_cfg,sizeof(openair0_config_t));
return 0;
}