wqMerge remote-tracking branch 'origin/develop' into develop-oriecpriupdates

This commit is contained in:
Raymond Knopp
2020-10-26 19:58:25 +01:00
981 changed files with 143173 additions and 56205 deletions
@@ -4,7 +4,7 @@ MODULE="riffa"
KERNEL_VERSION=$(uname -r)
RHR=/etc/redhat-release
sudo apt-get install -y --allow-unauthenticated linux-headers-`uname -r`
sudo apt-get install -y linux-headers-`uname -r`
if lsmod | grep "$MODULE" &> /dev/null ; then
echo "Previous SYRIQ is loaded!"
@@ -330,7 +330,10 @@ int trx_brf_set_gains(openair0_device* device,
{
return(0);
}
int trx_brf_write_init(openair0_device *device)
{
return 0;
}
#define RXDCLENGTH 16384
int16_t cos_fsover8[8] = {2047, 1447, 0, -1448, -2047, -1448, 0, 1447};
@@ -1145,6 +1148,7 @@ int device_init(openair0_device *device,
device->trx_stop_func = trx_brf_stop;
device->trx_set_freq_func = trx_brf_set_freq;
device->trx_set_gains_func = trx_brf_set_gains;
device->trx_write_init = trx_brf_write_init;
device->openair0_cfg = openair0_cfg;
device->priv = (void *)brf;
+49 -59
View File
@@ -37,53 +37,27 @@
#include <stdlib.h>
#include "common_lib.h"
#include "assertions.h"
#include "common/utils/load_module_shlib.h"
#include "common/utils/LOG/log.h"
#include "targets/RT/USER/lte-softmodem.h"
char *get_devname(int devtype) {
char *devnames[MAX_RF_DEV_TYPE]=DEVTYPE_NAMES;
if (devtype < MAX_RF_DEV_TYPE && devtype !=MIN_RF_DEV_TYPE )
return devnames[devtype];
return "none";
}
int set_device(openair0_device *device)
{
switch (device->type) {
case EXMIMO_DEV:
LOG_I(HW,"[%s] has loaded EXPRESS MIMO device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
break;
case USRP_B200_DEV:
LOG_I(HW,"[%s] has loaded USRP B200 device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
break;
case USRP_X300_DEV:
LOG_I(HW,"[%s] has loaded USRP X300 device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
break;
case BLADERF_DEV:
LOG_I(HW,"[%s] has loaded BLADERF device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
break;
case LMSSDR_DEV:
LOG_I(HW,"[%s] has loaded LMSSDR device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
break;
case IRIS_DEV:
LOG_I(HW,"[%s] has loaded Iris device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
break;
case ADRV9371_ZC706_DEV:
LOG_I(HW,"[%s] has loaded ADRV9371_ZC706 device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
break;
case UEDv2_DEV:
LOG_I(HW,"[%s] has loaded UEDv2 device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
break;
case NONE_DEV:
LOG_I(HW,"[%s] has not loaded a HW device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
break;
default:
char *devname = get_devname(device->type);
if (strcmp(devname,"none") != 0) {
LOG_I(HW,"[%s] has loaded %s device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"),devname);
} else {
LOG_E(HW,"[%s] invalid HW device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
return -1;
}
}
return 0;
}
@@ -108,7 +82,7 @@ int set_transport(openair0_device *device)
}
typedef int(*devfunc_t)(openair0_device *, openair0_config_t *, eth_params_t *);
//loader_shlibfunc_t shlib_fdesc[2];
/* look for the interface library and load it */
int load_lib(openair0_device *device,
@@ -118,31 +92,47 @@ int load_lib(openair0_device *device,
{
loader_shlibfunc_t shlib_fdesc[1];
int ret=0;
char *libname;
if ( IS_SOFTMODEM_BASICSIM ) {
libname=OAI_BASICSIM_LIBNAME;
char *deflibname=OAI_RF_LIBNAME;
openair0_cfg->recplay_mode = read_recplayconfig(&(openair0_cfg->recplay_conf),&(device->recplay_state));
if ( openair0_cfg->recplay_mode == RECPLAY_REPLAYMODE ) {
deflibname=OAI_IQPLAYER_LIBNAME;
shlib_fdesc[0].fname="device_init";
set_softmodem_optmask(SOFTMODEM_RECPLAY_BIT); // softmodem has to know we use the iqplayer to workaround randomized algorithms
} else if ( IS_SOFTMODEM_BASICSIM ) {
deflibname=OAI_BASICSIM_LIBNAME;
shlib_fdesc[0].fname="device_init";
} else if (IS_SOFTMODEM_RFSIM && flag == RAU_LOCAL_RADIO_HEAD) {
deflibname=OAI_RFSIM_LIBNAME;
shlib_fdesc[0].fname="device_init";
} else if (flag == RAU_LOCAL_RADIO_HEAD) {
if (IS_SOFTMODEM_RFSIM)
libname="rfsimulator";
else
libname=OAI_RF_LIBNAME;
shlib_fdesc[0].fname="device_init";
} else if (flag == RAU_REMOTE_RADIO_HEAD) {
libname=OAI_TP_LIBNAME;
shlib_fdesc[0].fname="transport_init";
if (IS_SOFTMODEM_RFSIM)
deflibname="rfsimulator";
else
deflibname=OAI_RF_LIBNAME;
shlib_fdesc[0].fname="device_init";
} else if (flag == RAU_REMOTE_THIRDPARTY_RADIO_HEAD) {
libname=OAI_THIRDPARTY_TP_LIBNAME;
deflibname=OAI_THIRDPARTY_TP_LIBNAME;
shlib_fdesc[0].fname="transport_init";
}
ret=load_module_shlib(libname,shlib_fdesc,1,NULL);
if (ret < 0) {
LOG_E(HW,"Library %s couldn't be loaded\n",libname);
} else {
ret=((devfunc_t)shlib_fdesc[0].fptr)(device,openair0_cfg,cfg);
deflibname=OAI_TP_LIBNAME;
shlib_fdesc[0].fname="transport_init";
}
return ret;
char *devname=NULL;
paramdef_t device_params[]=DEVICE_PARAMS_DESC ;
int numparams = sizeof(device_params)/sizeof(paramdef_t);
int devname_pidx = config_paramidx_fromname(device_params,numparams, CONFIG_DEVICEOPT_NAME);
device_params[devname_pidx].defstrval=deflibname;
config_get(device_params,numparams,DEVICE_SECTION);
ret=load_module_shlib(devname,shlib_fdesc,1,NULL);
AssertFatal( (ret >= 0),
"Library %s couldn't be loaded\n",devname);
return ((devfunc_t)shlib_fdesc[0].fptr)(device,openair0_cfg,cfg);
}
+93 -17
View File
@@ -35,21 +35,36 @@
#include <stdint.h>
#include <sys/types.h>
#include <openair1/PHY/TOOLS/tools_defs.h>
#include "record_player.h"
/* name of shared library implementing the radio front end */
/* default name of shared library implementing the radio front end */
#define OAI_RF_LIBNAME "oai_device"
/* name of shared library implementing the transport */
#define OAI_TP_LIBNAME "oai_transpro"
<<<<<<< HEAD
/* name of shared library implementing a third-party transport */
#define OAI_THIRDPARTY_TP_LIBNAME "thirdparty_transpro"
/* name of shared library implementing the basic/rf simulator */
=======
/* name of shared library implementing the rf simulator */
>>>>>>> origin/develop
#define OAI_RFSIM_LIBNAME "rfsimulator"
/* name of shared library implementing the basic/rf simulator */
/* name of shared library implementing the basic simulator */
#define OAI_BASICSIM_LIBNAME "tcp_bridge_oai"
/* name of shared library implementing the iq player */
#define OAI_IQPLAYER_LIBNAME "oai_iqplayer"
/* flags for BBU to determine whether the attached radio head is local or remote */
#define RAU_LOCAL_RADIO_HEAD 0
#define RAU_REMOTE_RADIO_HEAD 1
<<<<<<< HEAD
#define RAU_REMOTE_THIRDPARTY_RADIO_HEAD 2
=======
#define MAX_WRITE_THREAD_PACKAGE 10
#define MAX_WRITE_THREAD_BUFFER_SIZE 8
>>>>>>> origin/develop
#ifndef MAX_CARDS
#define MAX_CARDS 8
#endif
@@ -67,13 +82,9 @@ typedef enum {
max_gain=0,med_gain,byp_gain
} rx_gain_t;
#if OCP_FRAMEWORK
#include <enums.h>
#else
typedef enum {
duplex_mode_TDD=1,duplex_mode_FDD=0
} duplex_mode_t;
#endif
/** @addtogroup _GENERIC_PHY_RF_INTERFACE_
@@ -89,6 +100,8 @@ typedef enum {
USRP_B200_DEV,
/*!\brief device is USRP X300/X310*/
USRP_X300_DEV,
/*!\brief device is USRP N300/N310*/
USRP_N300_DEV,
/*!\brief device is BLADE RF*/
BLADERF_DEV,
/*!\brief device is LMSSDR (SoDeRa)*/
@@ -103,7 +116,7 @@ typedef enum {
UEDv2_DEV,
MAX_RF_DEV_TYPE
} dev_type_t;
#define DEVTYPE_NAMES {"","EXMIMO","USRP B200","USRP X300","USRP N300","BLADERF","LMSSDR","IRIS","No HW","ADRV9371_ZC706","UEDv2"}
/*!\brief transport protocol types
*/
typedef enum {
@@ -147,6 +160,7 @@ typedef enum {
gpsdo=2
} clock_source_t;
/*! \brief RF frontend parameters set by application */
typedef struct {
//! Module ID for this configuration
@@ -221,15 +235,9 @@ typedef struct {
char *my_addr;
//! local port number for Ethernet interface (eNB/BBU, UE)
unsigned int my_port;
#if defined(USRP_REC_PLAY)
unsigned short sf_mode; // 1=record, 2=replay
char sf_filename[1024]; // subframes file path
unsigned int sf_max; // max number of recorded subframes
unsigned int sf_loops; // number of loops in replay mode
unsigned int sf_read_delay; // read delay in replay mode
unsigned int sf_write_delay; // write delay in replay mode
unsigned int eth_mtu; // ethernet MTU
#endif
//! record player configuration, definition in record_player.h
uint32_t recplay_mode;
recplay_conf_t *recplay_conf;
//! number of samples per tti
unsigned int samples_per_tti;
//! the sample rate for receive.
@@ -278,8 +286,37 @@ typedef struct {
void *rx;
} if_buffer_t;
typedef struct {
openair0_timestamp timestamp;
void *buff[MAX_WRITE_THREAD_BUFFER_SIZE];// buffer to be write;
int nsamps;
int cc;
signed char first_packet;
signed char last_packet;
int flags_msb;
} openair0_write_package_t;
typedef struct {
openair0_write_package_t write_package[MAX_WRITE_THREAD_PACKAGE];
int start;
int end;
/// \internal This variable is protected by \ref mutex_write
int count_write;
/// pthread struct for trx write thread
pthread_t pthread_write;
/// pthread attributes for trx write thread
pthread_attr_t attr_write;
/// condition varible for trx write thread
pthread_cond_t cond_write;
/// mutex for trx write thread
pthread_mutex_t mutex_write;
} openair0_thread_t;
/*!\brief structure holds the parameters to configure USRP devices */
struct openair0_device_t {
/*!tx write thread*/
openair0_thread_t write_thread;
/*!brief Module ID of this device */
int Mod_id;
@@ -300,7 +337,8 @@ struct openair0_device_t {
/* !brief ETH params set by application */
eth_params_t *eth_params;
//! record player data, definition in record_player.h
recplay_state_t *recplay_state;
/* !brief Indicates if device already initialized */
int is_init;
@@ -427,6 +465,7 @@ struct openair0_device_t {
*/
void (*configure_rru)(int idx, void *arg);
<<<<<<< HEAD
/*! \brief Pointer to generic RRU private information
*/
@@ -446,6 +485,18 @@ struct openair0_device_t {
*/
int (*thirdparty_startstreaming)(openair0_device *device);
=======
/*! \brief RRU Configuration callback
* \param idx RU index
* \param arg pointer to capabilities or configuration
*/
int (*trx_write_init)(openair0_device *device);
/* \brief Get internal parameter
* \param id parameter to get
* \return a pointer to the parameter
*/
void *(*get_internal_parameter)(char *id);
>>>>>>> origin/develop
};
/* type of device init function, implemented in shared lib */
@@ -489,11 +540,31 @@ typedef struct {
#define OPTION_LZ4 0x00000001 // LZ4 compression (option_value is set to compressed size)
#ifdef __cplusplus
extern "C"
{
#endif
#define DEVICE_SECTION "device"
#define CONFIG_HLP_DEVICE "Identifies the oai device (the interface to RF) to use, the shared lib \"lib_<name>.so\" will be loaded"
#define CONFIG_DEVICEOPT_NAME "name"
/* inclusion for device configuration */
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/* config parameters for oai device */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#define DEVICE_PARAMS_DESC {\
{ CONFIG_DEVICEOPT_NAME, CONFIG_HLP_DEVICE, 0, strptr:&devname, defstrval:NULL, TYPE_STRING, 0}\
}
/*! \brief get device name from device type */
char *get_devname(int devtype);
/*! \brief Initialize openair RF target. It returns 0 if OK */
int openair0_device_load(openair0_device *device, openair0_config_t *openair0_cfg);
/*! \brief Initialize transport protocol . It returns 0 if OK */
@@ -511,6 +582,11 @@ openair0_timestamp get_usrp_time(openair0_device *device);
* \returns 0 in success
*/
int openair0_set_rx_frequencies(openair0_device *device, openair0_config_t *openair0_cfg);
/*! \brief read the iq record/player configuration */
extern int read_recplayconfig(recplay_conf_t **recplay_conf, recplay_state_t **recplay_state);
/*! \brief store recorded iqs from memory to file. */
extern void iqrecorder_end(openair0_device *device);
#define gettid() syscall(__NR_gettid)
/*@}*/
+115
View File
@@ -0,0 +1,115 @@
/*
* 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
*/
/** record_player.c
*
* \author: Nokia bellLabs B. Mongazon F. Taburet
*
*/
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/sysinfo.h>
#include <sys/resource.h>
#include "common/utils/LOG/log.h"
#include "assertions.h"
#include "common_lib.h"
#include "record_player.h"
/*! \brief read the oai recorder or player configuration, called from common device code
* \param recplay_conf: store parameters
* recplay_state: store recorder or player data while the device runs
*/
int read_recplayconfig(recplay_conf_t **recplay_conf, recplay_state_t **recplay_state) {
unsigned int u_sf_record = 0; // record mode
unsigned int u_sf_replay = 0; // replay mode
*recplay_conf = calloc(sizeof(recplay_conf_t),1);
paramdef_t device_recplay_params[]=DEVICE_RECPLAY_PARAMS_DESC ;
config_get(device_recplay_params,sizeof(device_recplay_params)/sizeof(paramdef_t),DEVICE_RECPLAY_SECTION);
if (u_sf_record || u_sf_replay ) {
struct sysinfo systeminfo;
*recplay_state = calloc(sizeof(recplay_state_t),1);
if ( *recplay_state == NULL) {
LOG_E(HW,"calloc error in %s\n", __FILE__);
return -1;
}
// Use mmap for IQ files for systems with less than 6GB total RAM
sysinfo(&systeminfo);
if (systeminfo.totalram < 6144000000) {
(*recplay_state)->use_mmap = 0;
} else {
(*recplay_state)->use_mmap = 1;
}
} else { /* record player enabled */
free(*recplay_conf);
*recplay_conf=NULL;
}
if (u_sf_replay == 1) return RECPLAY_REPLAYMODE;
else if (u_sf_record == 1) return RECPLAY_RECORDMODE;
return 0;
}
/*! \brief Terminate operation of the oai iq recorder. to be called by any device
* used in record mode
* \param device, the hardware used
*/
void iqrecorder_end(openair0_device *device) {
if (device->recplay_state != NULL) { // subframes store
iqfile_header_t fh = {device->type,device->openair0_cfg->tx_sample_advance, device->openair0_cfg->rx_bw,OAIIQFILE_ID};
recplay_state_t *rs = device->recplay_state;
recplay_conf_t *rc = device->openair0_cfg[0].recplay_conf;
rs->pFile = fopen (rc->u_sf_filename,"wb+");
if (rs->pFile == NULL) {
LOG_E(HW,"Cannot open %s\n", rc->u_sf_filename);
} else {
unsigned int i = 0;
unsigned int modu = 0;
if ((modu = rs->nb_samples % 10) != 0) {
rs->nb_samples -= modu; // store entire number of frames
}
LOG_I(HW,"Writing file header to %s \n", rc->u_sf_filename );
fwrite(&fh, sizeof(fh), 1, rs->pFile);
LOG_UI(HW,"Writing %u subframes to %s \n",rs->nb_samples, rc->u_sf_filename );
for (i = 0; i < rs->nb_samples; i++) {
fwrite(rs->ms_sample+i, sizeof(unsigned char), sizeof(iqrec_t), rs->pFile);
}
fclose (rs->pFile);
LOG_UI(HW,"File %s closed\n",rc->u_sf_filename );
}
if (rs->ms_sample != NULL) {
free((void *)rs->ms_sample);
rs->ms_sample = NULL;
}
}
}
@@ -1,5 +1,5 @@
#ifndef __USRP_LIB_H
#define __USRP_LIB_H
#ifndef __RECORD_PLAYER_H
#define __RECORD_PLAYER_H
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -21,7 +21,7 @@
* contact@openairinterface.org
*/
/** usrp_lib.h
/** targets/ARCH/COMMON/record-player.h
*
* \author: bruno.mongazon-cazavet@nokia-bell-labs.com
*/
@@ -37,19 +37,6 @@
extern "C"
{
#endif
#define CONFIG_OPT_RECPLAY "enable_recplay"
#define CONFIG_HLP_RECPLAY "Allow record player"
#define USRP_SECTION "device.usrp"
/* inclusion for device configuration */
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/* command line parameters for USRP record/playback */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#define USRP_DEVICE_PARAMS_DESC { \
{CONFIG_OPT_RECPLAY, CONFIG_HLP_RECPLAY, PARAMFLAG_BOOL, uptr:&enable_recplay, defuintval:0, TYPE_UINT, 0} \
}
/* inclusions for record player */
#define RECPLAY_DISABLED 0
@@ -59,6 +46,15 @@ extern "C"
#define BELL_LABS_IQ_HEADER 0xabababababababab
#define BELL_LABS_IQ_PER_SF 7680 // Up to 5MHz bw for now
#define BELL_LABS_IQ_BYTES_PER_SF (BELL_LABS_IQ_PER_SF * 4)
#define OAIIQFILE_ID "OIQF"
typedef struct {
uint64_t devtype;
uint64_t tx_sample_advance;
double bw;
char oaiid[4];
} iqfile_header_t;
typedef struct {
int64_t header;
int64_t ts;
@@ -75,8 +71,8 @@ typedef struct {
/* help strings definition for config options, used in CMDLINE_XXX_DESC macros and printed when -h option is used */
#define CONFIG_HLP_SF_FILE "Path of the file used for subframes record or replay"
#define CONFIG_HLP_SF_REC "Record subframes from USRP driver into a file for later replay"
#define CONFIG_HLP_SF_REP "Replay subframes into USRP driver from a file"
#define CONFIG_HLP_SF_REC "Record subframes from device driver into a file for later replay"
#define CONFIG_HLP_SF_REP "Replay subframes from a file using the oai replay driver"
#define CONFIG_HLP_SF_MAX "Maximum count of subframes to be recorded in subframe file"
#define CONFIG_HLP_SF_LOOPS "Number of loops to replay of the entire subframes file"
#define CONFIG_HLP_SF_RDELAY "Delay in microseconds to read a subframe in replay mode"
@@ -91,41 +87,43 @@ typedef struct {
#define CONFIG_OPT_SF_RDELAY "subframes-read-delay"
#define CONFIG_OPT_SF_WDELAY "subframes-write-delay"
#define USRP_RECPLAY_SECTION "device.recplay"
#define DEVICE_RECPLAY_SECTION "device.recplay"
/* For information only - the macro is not usable in C++ */
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/* command line parameters for USRP record/playback */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#define USRP_RECPLAY_PARAMS_DESC { \
{CONFIG_OPT_SF_FILE, CONFIG_HLP_SF_FILE, 0, strptr:(char **)((*recplay_state)->u_sf_filename), defstrval:DEF_SF_FILE, TYPE_STRING, 1024}, \
{CONFIG_OPT_SF_REC, CONFIG_HLP_SF_REC, PARAMFLAG_BOOL, uptr:&(u_sf_record), defuintval:0, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_REP, CONFIG_HLP_SF_REP, PARAMFLAG_BOOL, uptr:&(u_sf_replay), defuintval:0, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_MAX, CONFIG_HLP_SF_MAX, 0, uptr:&((*recplay_state)->u_sf_max), defintval:DEF_NB_SF, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_LOOPS, CONFIG_HLP_SF_LOOPS, 0, uptr:&((*recplay_state)->u_sf_loops), defintval:DEF_SF_NB_LOOP, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_RDELAY, CONFIG_HLP_SF_RDELAY, 0, uptr:&((*recplay_state)->u_sf_read_delay), defintval:DEF_SF_DELAY_READ, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_WDELAY, CONFIG_HLP_SF_WDELAY, 0, uptr:&((*recplay_state)->u_sf_write_delay), defintval:DEF_SF_DELAY_WRITE, TYPE_UINT, 0}, \
}/*! \brief USRP Configuration and state */
#define DEVICE_RECPLAY_PARAMS_DESC { \
{CONFIG_OPT_SF_FILE, CONFIG_HLP_SF_FILE, 0, strptr:(char **)((*recplay_conf)->u_sf_filename), defstrval:DEF_SF_FILE, TYPE_STRING, 1024}, \
{CONFIG_OPT_SF_REC, CONFIG_HLP_SF_REC, PARAMFLAG_BOOL, uptr:&(u_sf_record), defuintval:0, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_REP, CONFIG_HLP_SF_REP, PARAMFLAG_BOOL, uptr:&(u_sf_replay), defuintval:0, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_MAX, CONFIG_HLP_SF_MAX, 0, uptr:&((*recplay_conf)->u_sf_max), defintval:DEF_NB_SF, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_LOOPS, CONFIG_HLP_SF_LOOPS, 0, uptr:&((*recplay_conf)->u_sf_loops), defintval:DEF_SF_NB_LOOP, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_RDELAY, CONFIG_HLP_SF_RDELAY, 0, uptr:&((*recplay_conf)->u_sf_read_delay), defintval:DEF_SF_DELAY_READ, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_WDELAY, CONFIG_HLP_SF_WDELAY, 0, uptr:&((*recplay_conf)->u_sf_write_delay), defintval:DEF_SF_DELAY_WRITE, TYPE_UINT, 0}, \
}/*! \brief Record Player Configuration and state */
typedef struct {
FILE *pFile;
int mmapfd;
int iqfd;
int use_mmap; // default is to use mmap
size_t mapsize;
iqrec_t *ms_sample; // memory for all subframes
unsigned int nb_samples;
char u_sf_filename[1024]; // subframes file path
unsigned int u_sf_max ; // max number of recorded subframes
unsigned int u_sf_loops ; // number of loops in replay mode
unsigned int u_sf_read_delay; // read delay in replay mode
unsigned int u_sf_write_delay ; // write delay in replay mode
} recplay_conf_t;
typedef struct {
int use_mmap; // default is to use mmap
size_t mapsize;
FILE *pFile;
int mmapfd;
int iqfd;
iqrec_t *ms_sample; // memory for all subframes
unsigned int nb_samples;
} recplay_state_t;
extern int read_usrpconfig(uint32_t *recplay_mode, recplay_state_t **recplay_state);
#ifdef __cplusplus
}
#endif
#endif // __USRP_LIB_H
#endif // __RECORD_PLAYER_H
@@ -202,6 +202,10 @@ 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,
@@ -289,7 +293,7 @@ int ethernet_tune(openair0_device *device,
/******************* 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));
strncpy(ifr.ifr_name,eth->if_name, sizeof(ifr.ifr_name)-1);
ifr.ifr_mtu =value;
if (ioctl(eth->sockfdd,SIOCSIFMTU,(caddr_t)&ifr) < 0 )
perror ("[ETHERNET] Can't set the MTU");
@@ -299,7 +303,7 @@ int ethernet_tune(openair0_device *device,
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));
strncpy(ifr.ifr_name,eth->if_name, sizeof(ifr.ifr_name)-1);
ifr.ifr_qlen =value;
if (ioctl(eth->sockfdd,SIOCSIFTXQLEN,(caddr_t)&ifr) < 0 )
perror ("[ETHERNET] Can't set the txqueuelen");
@@ -411,8 +415,9 @@ int transport_init(openair0_device *device,
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_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;
@@ -30,6 +30,9 @@
* \warning
*/
#ifndef __ETHERNET_USERSPACE_LIB_IF_DEFS__H__
#define __ETHERNET_USERSPACE_LIB_IF_DEFS__H__
#include <netinet/ether.h>
#include <stdint.h>
@@ -39,15 +42,11 @@
#endif
// ETH transport preference modes
#ifdef OCP_FRAMEWORK
#include "enums.h"
#else
#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
#endif
// COMMOM HEADER LENGTHS
@@ -86,3 +85,5 @@
#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
+386
View File
@@ -0,0 +1,386 @@
/*
* 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] = 1;
cap->nb_tx[0] = 1;
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 = 0;
lock_ul_buffer(&s->buffers, *subframe);
next:
while (!(s->buffers.ul_busy[*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[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 (symbol = 0; symbol < 14; symbol++) {
int i;
uint16_t *p = (uint16_t *)(&s->buffers.ul[*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[*subframe][symbol*1200*4],
600 * 4);
memcpy(rxdata,
&s->buffers.ul[*subframe][symbol*1200*4] + 600*4,
600 * 4);
#endif
}
s->buffers.ul_busy[*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 = 0;
//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[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 || ru->nb_tx != 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 (symbol = 0; symbol < 14; symbol++) {
txdata = &ru->common.txdataF_BF[aa][symbol * fp->ofdm_symbol_size];
#if 1
memcpy(&s->buffers.dl[subframe][symbol*1200*4],
txdata + 2048 - 600,
600 * 4);
memcpy(&s->buffers.dl[subframe][symbol*1200*4] + 600*4,
txdata + 1,
600 * 4);
#endif
int i;
uint16_t *p = (uint16_t *)(&s->buffers.dl[subframe][symbol*1200*4]);
for (i = 0; i < 1200*2; i++) {
p[i] = htons(p[i]);
}
}
s->buffers.dl_busy[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(&eth->buffers);
return 0;
}
@@ -0,0 +1,787 @@
/* 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_CRC_STRIP,
.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 && ANT_NUM == 0x00) {
p.frame = FRAME;
p.subframe = SUBFRAME >> 4;
p.slot = 0; /* unused */
p.symbol = SYMBOL;
p.antenna = 0;
memcpy(p.iq, IQ_ptr, 4800);
store_ul(bs, &p);
}
// U-PLANE UL ANT_0 PROCESSING
if(PAYLOAD_1 == 0x12 && PAYLOAD_2 == 0xce && ANT_NUM == 0x00 && dl_start == 1)
{
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;
/* antenna 0 - send actual DL data (if available) */
lock_dl_buffer(bs->buffers, tx_subframe);
if (!(bs->buffers->dl_busy[tx_subframe] & (1 << tx_symbol))) {
printf("%s: warning, DL underflow (sf.symbol %d.%d)\n", __FUNCTION__,
tx_subframe, tx_symbol);
memset(IQ_ptr, 0, 1200 * 4);
} else {
memcpy(IQ_ptr, bs->buffers->dl[tx_subframe] + tx_symbol * 1200*4,
1200*4);
}
bs->buffers->dl_busy[tx_subframe] &= ~(1 << tx_symbol);
unlock_dl_buffer(bs->buffers, tx_subframe);
// fill DL Data for ant 0 with 0 value
// memset(IQ_ptr, 0, 4800);
}
// U-PLANE UL ANT_1 PROCESSING
else if(PAYLOAD_1 == 0x12 && PAYLOAD_2 == 0xce && ANT_NUM == 0x01 && dl_start == 1)
{
// ANT_NUM = 0x01;
SYMBOL = (SYMBOL + 5) % 14;
PAYLOAD_2 = 0xc8;
// fill DL Data for ant 1 with 0 value
memset(IQ_ptr, 0, 4800);
}
// 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;
}
+95
View File
@@ -0,0 +1,95 @@
/*
* 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)
{
/* only antenna 0 for the moment */
if (ul->antenna != 0)
return;
if (ul->subframe != bs->next_subframe ||
ul->symbol != bs->next_symbol) {
printf("%s: fatal, expected frame.sf.symbol %d.%d.%d, got %d.%d.%d\n",
__FUNCTION__,
bs->expected_benetel_frame, bs->next_subframe, bs->next_symbol,
ul->frame, ul->subframe, ul->symbol);
exit(1);
}
lock_ul_buffer(bs->buffers, bs->next_subframe);
if (bs->buffers->ul_busy[bs->next_subframe] & (1 << bs->next_symbol)) {
printf("%s: warning, UL overflow (sf.symbol %d.%d)\n", __FUNCTION__,
bs->next_subframe, bs->next_symbol);
}
memcpy(bs->buffers->ul[bs->next_subframe] + bs->next_symbol * 1200*4,
ul->iq, 1200*4);
bs->buffers->ul_busy[bs->next_subframe] |= (1 << bs->next_symbol);
signal_ul_buffer(bs->buffers, bs->next_subframe);
unlock_ul_buffer(bs->buffers, bs->next_subframe);
bs->next_symbol++;
if (bs->next_symbol == 14) {
bs->next_symbol = 0;
bs->next_subframe = (bs->next_subframe + 1) % 10;
if (bs->next_subframe == 0) {
bs->expected_benetel_frame++;
bs->expected_benetel_frame &= 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);
}
+49
View File
@@ -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_4G_LOW_H_
#define _BENETEL_4G_LOW_H_
#include "shared_buffers.h"
typedef struct {
shared_buffers *buffers;
int next_subframe;
int next_symbol;
int expected_benetel_frame;
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_ */
+118
View File
@@ -0,0 +1,118 @@
/*
* 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 = 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, &params) != 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,114 @@
/*
* 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[1] = 0x3fff;
s->dl_busy[2] = 0x3fff;
s->dl_busy[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,60 @@
/*
* 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 {
unsigned char dl[10][14*1200*4];
unsigned char ul[10][14*1200*4];
uint16_t dl_busy[10];
uint16_t ul_busy[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_ */
+388
View File
@@ -0,0 +1,388 @@
/*
* 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;
cap->nb_rx[0] = 1;
cap->nb_tx[0] = 1;
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 = 0;
lock_ul_buffer(&s->buffers, *slot);
#if 1
next:
while (!(s->buffers.ul_busy[*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[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 (symbol = 0; symbol < 14; symbol++) {
int i;
uint16_t *p = (uint16_t *)(&s->buffers.ul[*slot][symbol*1272*4]);
for (i = 0; i < 1272*2; i++) {
p[i] = htons(p[i]);
}
rxdata = &ru->common.rxdataF[antenna][symbol * fp->ofdm_symbol_size];
#if 1
memcpy(rxdata + 2048 - 1272/2,
&s->buffers.ul[*slot][symbol*1272*4],
(1272/2) * 4);
memcpy(rxdata,
&s->buffers.ul[*slot][symbol*1272*4] + (1272/2)*4,
(1272/2) * 4);
#endif
}
s->buffers.ul_busy[*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 = 0;
//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[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 || ru->nb_tx != 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 (symbol = 0; symbol < 14; symbol++) {
txdata = &ru->common.txdataF_BF[aa][symbol * fp->ofdm_symbol_size];
#if 1
memcpy(&s->buffers.dl[slot][symbol*1272*4],
txdata + 2048 - (1272/2),
(1272/2) * 4);
memcpy(&s->buffers.dl[slot][symbol*1272*4] + (1272/2)*4,
txdata,
(1272/2) * 4);
#endif
int i;
uint16_t *p = (uint16_t *)(&s->buffers.dl[slot][symbol*1272*4]);
for (i = 0; i < 1272*2; i++) {
p[i] = htons(p[i]);
}
}
s->buffers.dl_busy[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(&eth->buffers);
return 0;
}
@@ -0,0 +1,856 @@
/* 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_CRC_STRIP,
.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 == 0x46 && 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 && ANT_NUM == 0x00) {
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 = 0;
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 ANT_0 PROCESSING
if(PAYLOAD_1 == 0x13 && PAYLOAD_2 == 0xe4 && ANT_NUM == 0x00 && dl_start == 1)
{
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 + 8;
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;
}
}
}
ANT_NUM = 0x00;
// Mask the symbol bits from UL packet received and apply the shift.
SYMBOL = ((SYMBOL & 0b00111111) + 8) % 14;
ANT_NUM = 0x00;
SUBFRAME = sf;
// Slot id control for DL
if(slot_id_ctrl > 13){
SYMBOL = SYMBOL | 0b01000000;
}
/* antenna 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[oai_slot] & (1 << tx_symbol))) {
printf("%s: warning, DL underflow (sl.symbol %d.%d)\n", __FUNCTION__,
oai_slot, tx_symbol);
memset(IQ_ptr, 0, 1272 * 4);
} else {
memcpy(IQ_ptr, bs->buffers->dl[oai_slot] + tx_symbol * 1272*4,
1272*4);
}
bs->buffers->dl_busy[oai_slot] &= ~(1 << tx_symbol);
unlock_dl_buffer(bs->buffers, oai_slot);
// fill DL Data for ant 0 with 0 value
// memset(IQ_ptr, 0, 5088);
}
// U-PLANE UL ANT_1 PROCESSING
if(PAYLOAD_1 == 0x13 && PAYLOAD_2 == 0xe4 && ANT_NUM == 0x01 && dl_start == 1)
{
// Mask the symbol bits from UL packet received and apply the shift.
SYMBOL = ((SYMBOL & 0b00111111) + 8) % 14;
ANT_NUM = 0x01;
SUBFRAME = sf;
slot_id_ctrl++;
// Slot id control for DL
if(slot_id_ctrl > 13){
SYMBOL = SYMBOL | 0b01000000;
if(slot_id_ctrl > 27){
slot_id_ctrl = 0;
sf = sf + 0x10;
if (sf >0x90){
sf = 0;
}
}
}
// fill DL Data for ant 1 with 0 value
memset(IQ_ptr, 0, 5088);
}
// 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, slot /*subframe * 2 + slot*/, 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;
}
+97
View File
@@ -0,0 +1,97 @@
/*
* 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)
{
/* only antenna 0 for the moment */
if (ul->antenna != 0)
return;
if (ul->slot != bs->next_slot ||
ul->symbol != bs->next_symbol) {
printf("%s: fatal, expected frame.sl.symbol %d.%d.%d, got %d.%d.%d\n",
__FUNCTION__,
bs->expected_benetel_frame, bs->next_slot, bs->next_symbol,
ul->frame, ul->slot, ul->symbol);
exit(1);
}
lock_ul_buffer(bs->buffers, bs->next_slot);
if (bs->buffers->ul_busy[bs->next_slot] & (1 << bs->next_symbol)) {
printf("%s: warning, UL overflow (sl.symbol %d.%d)\n", __FUNCTION__,
bs->next_slot, bs->next_symbol);
}
memcpy(bs->buffers->ul[bs->next_slot] + bs->next_symbol * 1272*4,
ul->iq, 1272*4);
bs->buffers->ul_busy[bs->next_slot] |= (1 << bs->next_symbol);
signal_ul_buffer(bs->buffers, bs->next_slot);
unlock_ul_buffer(bs->buffers, bs->next_slot);
bs->next_symbol++;
if (bs->next_symbol == 14) {
bs->next_symbol = 0;
bs->next_slot = (bs->next_slot + 1) % 20;
if (bs->next_slot == 0) {
bs->expected_benetel_frame++;
bs->expected_benetel_frame &= 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);
}
+48
View File
@@ -0,0 +1,48 @@
/*
* 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;
int next_slot;
int next_symbol;
int expected_benetel_frame;
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_ */
+118
View File
@@ -0,0 +1,118 @@
/*
* 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 = 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, &params) != 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,118 @@
/*
* 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[1] = 0x3fff;
s->dl_busy[2] = 0x3fff;
s->dl_busy[3] = 0x3fff;
s->dl_busy[4] = 0x3fff;
s->dl_busy[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,60 @@
/*
* 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 {
unsigned char dl[20][14*1272*4];
unsigned char ul[20][14*1272*4];
uint16_t dl_busy[20];
uint16_t ul_busy[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_ */
@@ -510,6 +510,11 @@ int trx_iris_reset_stats(openair0_device *device) {
}
int trx_iris_write_init(openair0_device *device)
{
return 0;
}
extern "C" {
/*! \brief Initialize Openair Iris target. It returns 0 if OK
@@ -831,6 +836,7 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
device->trx_set_freq_func = trx_iris_set_freq;
device->trx_set_gains_func = trx_iris_set_gains;
device->openair0_cfg = openair0_cfg;
device->trx_write_init = trx_iris_write_init;
s->sample_rate = openair0_cfg[0].sample_rate;
// TODO:
@@ -350,6 +350,10 @@ void trx_lms_end(openair0_device *device) {
}
int trx_lms_write_init(openair0_device *device)
{
return 0;
}
extern "C" {
/*! \brief Initialize Openair LMSSDR target. It returns 0 if OK
* \param device the hardware to use
@@ -406,6 +410,7 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg){
device->trx_stop_func = trx_lms_stop;
device->trx_set_freq_func = trx_lms_set_freq;
device->trx_set_gains_func = trx_lms_set_gains;
device->trx_write_init = trx_lms_write_init;
device->openair0_cfg = openair0_cfg;
File diff suppressed because it is too large Load Diff
@@ -1,73 +0,0 @@
/*
* 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
*/
/** usrp_lib_config.c
*
* \author: HongliangXU : hong-liang-xu@agilent.com
*/
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/sysinfo.h>
#include <sys/resource.h>
#include "common/utils/LOG/log.h"
#include "assertions.h"
#include "common_lib.h"
#include "usrp_lib.h"
int read_usrpconfig(uint32_t *recplay_mode, recplay_state_t **recplay_state) {
unsigned int u_sf_record = 0; // record mode
unsigned int u_sf_replay = 0; // replay mode
uint32_t enable_recplay;
paramdef_t usrp_params[] = USRP_DEVICE_PARAMS_DESC;
config_get(usrp_params,sizeof(usrp_params)/sizeof(paramdef_t),USRP_SECTION);
if (enable_recplay) {
*recplay_state = calloc(sizeof(recplay_state_t),1);
paramdef_t usrp_recplay_params[]=USRP_RECPLAY_PARAMS_DESC ;
struct sysinfo systeminfo;
// Use mmap for IQ files for systems with less than 6GB total RAM
sysinfo(&systeminfo);
if (systeminfo.totalram < 6144000000) {
(*recplay_state)->use_mmap = 0;
} else {
(*recplay_state)->use_mmap = 1;
}
memset((*recplay_state)->u_sf_filename, 0, 1024);
config_get(usrp_recplay_params,sizeof(usrp_recplay_params)/sizeof(paramdef_t),USRP_RECPLAY_SECTION);
if (strlen((*recplay_state)->u_sf_filename) == 0) {
(void) strcpy((
*recplay_state)->u_sf_filename, DEF_SF_FILE);
}
} /* record player enabled */
if (u_sf_replay == 1) *recplay_mode = RECPLAY_REPLAYMODE;
if (u_sf_record == 1) *recplay_mode = RECPLAY_RECORDMODE;
return 0;
}
@@ -0,0 +1,163 @@
# I/Q record-playback
## using the iq recorder or iq player
This feature provides I/Q record-playback as presented in the 4th OAI workshop. Tests have only been performed with a eNB, but the implementation should allow any softmodem executable to use the iq's record/player.
The I/Q record/playback feature is briefly described hereafter, it allows you to:
* record subframes received by the USRP board in a file while the system is operating (for example you can record a full UE connection/traffic/ disconnection sequence). Today the oai USRP device is the only one supporting the recording feature.
* replay subframes from a file to operate the system (possibly in multiple loops if the recorded sequence is convenient from the logical point of view).
The record/replay are activated and configured using oai configuration parameters
### Record mode
options for record mode are:
* `subframes-record` Activate record mode
* `subframes-file` Path of the file used for subframes recording (default is `/tmp/iqfile`)
* `subframes-max` Maximum count of subframes to be recorded in subframe file (default is 120000)
Note that the value of `--subframes-max` parameter needs to be tuned according to your RAM capabilities. The default value is 120000, which allows for 120 seconds of record/replay but will require ~3.6GB of RAM and disk space.
If RAM does not allow for the parameter value, an error message will be displayed and the run will be aborted. In record mode, to minimize pzerformance impact,
subframes are saved into memory and written to disk when the process is terminating . When you estimate the recorded sequence is achieved, you should terminate the eNB/RRU by typing `CTRL-C`. At that time, up to the value of `--subframes-max` subframes will be written on disk. The eNB/RRU will indicate the exact count of subframes written to disk, which may be less (but not higher) that the value of `--subframes-max` parameter (120000 by default). The number of subframes written to disk is rounded to an integral number of frames.
Although it is possible to perform recording with a fronthaul setup, it is suggested to perform recording in full eNB mode and then replay in either monolithic or split eNB.
The option can be used while operating full eNB or RCC/RRU linked by NGFI IF5/IF4p5 fronthaul techniques. One of the advantage of the record/replay technique is that you can replay on a fronthauling setup a file that has been recorded in full eNB mode. This can easily asserts that the fronthauling underlying set up or technique is correct.
>recording session example:
```bash
./lte-softmodem -O libconfig:/usr/local/oai/conf/enb.band7.tm1.20PRB.usrpb210.conf:dbgl5 --log_config.global_log_options level,thread --device.recplay.subframes-record 1
............................................
............................................
[CONFIG] device.recplay.subframes-file set to default value "/tmp/iqfile"
[CONFIG] subframes-record: 0
[CONFIG] device.recplay.subframes-record set to default value
[CONFIG] subframes-replay: 0
[CONFIG] device.recplay.subframes-replay set to default value
[CONFIG] subframes-max: 120000
[CONFIG] device.recplay.subframes-max set to default value
[CONFIG] subframes-loops: 5
[CONFIG] device.recplay.subframes-loops set to default value
[CONFIG] subframes-read-delay: 700
[CONFIG] device.recplay.subframes-read-delay set to default value
[CONFIG] subframes-write-delay: 15
[CONFIG] device.recplay.subframes-write-delay set to default value
[LIBCONFIG] device.recplay: 7/7 parameters successfully set, (7 to default value)
[CONFIG] subframes-record: 1
[CONFIG] device.recplay 1 options set from command line
[CONFIG] loader.oai_device.shlibversion set to default value ""
[LIBCONFIG] loader.oai_device: 2/2 parameters successfully set, (1 to default value)
[CONFIG] loader.oai_device 0 options set from command line
[LOADER] library liboai_device.so successfully loaded
USRP device initialized in subframes record mode
[HW]I lte-softmodem UHD version 3.14.1.1-release (3.14.1)
...................................................
...................................................
[PHY]I lte-softmodem prach_I0 = 0.0 dB
[PHY]I ru thread max_I0 18, min_I0 12
^C
Run time: 0h 26s
Time executing user inst.: 1s 865251us
Time executing system inst.: 2s 805162us
Max. Phy. memory usage: 4068328kB
Page fault number (no io): 1008741
Page fault number (requiring io): 15
Number of file system read: 97672
Number of filesystem write: 0
Number of context switch (process origin, io...): 562871
Number of context switch (os origin, priority...): 112
Linux signal Interrupt...
/usr/local/oai/develop/openairinterface5g/executables/softmodem-common.c:188 signal_handler() Exiting OAI softmodem: softmodem starting exit procedure
Exiting ru_thread
U[HW]I ru thread Writing file header to /tmp/iqfile
[HW]I lte-softmodem Writing file header to /tmp/iqfile
[HW]I ru thread Writing 19730 subframes to /tmp/iqfile
[HW]I lte-softmodem Writing 19730 subframes to /tmp/iqfile
[HW]I lte-softmodem File /tmp/iqfile closed
[HW]I ru thread File /tmp/iqfile closed
[PHY]I ru thread RU 0 rf device stopped
```
### Playback/replay mode
The option works only for 5MHz bandwidth because the information stored in the iq's file header regarding bandwidth is not yet properly processed.
In this version, mismatch between file content and run time parameters might lead to unpredictable results. In addition a file recorded on a AVX2-capable processor cannot be replayed on a non-AVX2-capable processor (this is to be further investigated).
options for replay mode are:
* `subframes-replay` Activate replay mode
* `subframes-file` Path of the file used for subframes replay (default is `/tmp/iqfile`)
* `subframes-loops` Number of iterations to replay the entire subframes file (default is 5)
* `subframes-read-delay` Delay in microseconds to read a subframe in replay mode (default 200)
* `subframes-write-delay` Delay in milliseconds to write a subframe in replay mode (default 15)
>iq player session example:
```bash
./lte-softmodem -O libconfig:/usr/local/oai/conf/enb.nbiot.band7.tm1.50PRB.usrpb210.conf:dbgl5 --T_stdout 1 --log_config.global_log_options level,thread --eNBs.[0].component_carriers.[0].N_RB_DL 25 --device.recplay.subframes-replay 1
[CONFIG] get parameters from libconfig /usr/local/oai/conf/enb.nbiot.band7.tm1.50PRB.usrpb210.conf , debug flags: 0x00000005
................................
..................................
[PHY]I lte-softmodem rxdataF[0] 0x55f52df52f40 for RU 0
[CONFIG] device.recplay.subframes-file set to default value "/tmp/iqfile"
[CONFIG] subframes-record: 0
[CONFIG] device.recplay.subframes-record set to default value
[CONFIG] subframes-replay: 0
[CONFIG] device.recplay.subframes-replay set to default value
[CONFIG] subframes-max: 120000
[CONFIG] device.recplay.subframes-max set to default value
[CONFIG] subframes-loops: 5
[CONFIG] device.recplay.subframes-loops set to default value
[CONFIG] subframes-read-delay: 700
[CONFIG] device.recplay.subframes-read-delay set to default value
[CONFIG] subframes-write-delay: 15
[CONFIG] device.recplay.subframes-write-delay set to default value
[LIBCONFIG] device.recplay: 7/7 parameters successfully set, (7 to default value)
[CONFIG] subframes-replay: 1
[CONFIG] device.recplay 1 options set from command line
[CONFIG] loader.oai_iqplayer.shlibversion set to default value ""
[LIBCONFIG] loader.oai_iqplayer: 2/2 parameters successfully set, (1 to default value)
[CONFIG] loader.oai_iqplayer 0 options set from command line
[LOADER] library liboai_iqplayer.so successfully loaded
[HW]I lte-softmodem Loading subframes using mmap() from /tmp/iqfile size=324433632 bytes ...
[HW]I lte-softmodem Replay iqs from USRP B200 device, bandwidth 5.000000e+06
[HW]I lte-softmodem Loaded 10550 subframes.
[HW]I lte-softmodem iqplayer device initialized, replay /tmp/iqfile for 5 iterations[HW]I lte-softmodem [RAU] has loaded USRP B200 device.
.............................................
.............................................
[PHY]I ru thread max_I0 18, min_I0 12
[PHY]I lte-softmodem prach_I0 = 0.0 dB
[PHY]I ru thread max_I0 18, min_I0 0
[HW]I ru thread starting subframes file with wrap_count=1 wrap_ts=81024000 ts=107616555
[PHY]I lte-softmodem prach_I0 = 0.0 dB
[PHY]I ru thread max_I0 18, min_I0 12
[HW]I ru thread starting subframes file with wrap_count=2 wrap_ts=162048000 ts=188640555
[PHY]I lte-softmodem prach_I0 = 0.0 dB
[PHY]I ru thread max_I0 18, min_I0 0
[HW]I ru thread starting subframes file with wrap_count=3 wrap_ts=243072000 ts=269664555
[PHY]I lte-softmodem prach_I0 = 0.0 dB
[PHY]I ru thread max_I0 19, min_I0 12
[HW]I ru thread starting subframes file with wrap_count=4 wrap_ts=324096000 ts=350688555
[PHY]I lte-softmodem prach_I0 = 0.0 dB
[PHY]I ru thread max_I0 18, min_I0 12
[HW]W ru thread iqplayer device terminating subframes replay after 5 iteration
/usr/local/oai/develop/openairinterface5g/targets/ARCH/iqplayer/iqplayer_lib.c:222 trx_iqplayer_read() Exiting OAI softmodem: replay ended, triggering process termination
```
## iq recorder and iq player implementation overview
### configuration
The iq's record/player is using the [configuration module](../../../../common/config/DOC/config.md). Configuration parameters supported by the record player are defined in the [record_player.h](../../COMMON/record_player.h) include file.
There are no specific sections for the recorder or the replayer, parameters are defined under the `device.recplay`section and the `read_recplayconfig` function is common to both the recorder and the player. `device.recplay` configuration section is read from oai code common to all devices:
Implemented in [record_player.c](../../COMMON/record_player.c), the `read_recplayconfig` function is called from `load_lib` and reads the configuration parameters, saving them in a `recplay_conf_t` sub-structure of the oai device. It also allocates a `recplay_state_t` structure which will save the recorder or player data.
3GPP authentication algorithm doesn't allow session establishment via replaying iq's. When using the replayer, a softmodem parameter is set (`SOFTMODEM_RECPLAY_BIT` in the `optmask` bitmask of the `softmodem_params_t` structure. It allows bypassing or activating specific code in the softmodem to allow session establishment replay. This mechanism only works if in parallel specific code is also activated in the EPC, which is supported by the NOKIA LTE box.
An easiest scenario is to use the record player in parallel with the noS1 mode, as described [here](../../../../doc/RUNMODEM.md)
### iq recorder
most of the implementation is now located in the oai device common code:
* The`load_lib` function in [common-lib.c](../../COMMON/common_lib.c) reads the record player parameters.
* Recorder specific functions are implemented in `iqrecorder_end` saves the cached iq's to disk, it has to be called by the oai device in it's end function.
* When recording is enabled, the device must implement iq's caching in it's read function. It also has to call the `iqrecorder_end ` function when terminating to write iqs to a file on disk. Look at the USRP device ( [../usrp_lib.cpp](../../USRP/USERSPACE/LIB/usrp_lib.cpp) ) for details.
### iq player
The replay feature is implemented as a oai device.
* The`load_lib` function in [common-lib.c](../../COMMON/common_lib.c) reads the record player parameters, this code is common with the recorder.
* The player device is implemented in [iqplayer_lib.c](../iqplayer_lib.c) which at build time creates the `liboai_iqplayer.so` shared library. Thi
* s device is automatically, dynamicaly loaded when the player feature is activated at runtime when the boolean `device.recplay.subframes-replay` option is set.
### iq's file format
The recorder adds a header containing the device type, the bandwith and a format identifier. Using these information when replaying a file has not yet been implemented, so the player only supports USRP B2xx device and 5MHz bandwith.
[oai Wikis home](https://gitlab.eurecom.fr/oai/openairinterface5g/wikis/home)
+317
View File
@@ -0,0 +1,317 @@
/*
* 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
*/
/** iqplayer_lib.cpp
*
* \author:FrancoisTaburet: francois.taburet@nokia-bell-labs.com
*/
#define _LARGEFILE_SOURCE
#define _FILE_OFFSET_BITS 64
#define NB_ANTENNAS_RX 2
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <sys/resource.h>
#include <errno.h>
#include "common_lib.h"
#include "assertions.h"
#include "common/utils/LOG/log.h"
static void parse_iqfile_header(openair0_device *device, iqfile_header_t *iq_fh) {
AssertFatal((memcmp(iq_fh->oaiid,OAIIQFILE_ID,sizeof(OAIIQFILE_ID)) == 0),
"iqfile doesn't seem to be compatible with oai (invalid id %.4s in header)\n",
iq_fh->oaiid);
device->type = iq_fh->devtype;
device->openair0_cfg[0].tx_sample_advance=iq_fh->tx_sample_advance;
device->openair0_cfg[0].tx_bw = device->openair0_cfg[0].rx_bw = iq_fh->bw;
LOG_UI(HW,"Replay iqs from %s device, bandwidth %e\n",get_devname(iq_fh->devtype),iq_fh->bw);
}
/*! \brief Called to start the iqplayer device. Return 0 if OK, < 0 if error
@param device pointer to the device structure specific to the RF hardware target
*/
static int iqplayer_loadfile(openair0_device *device, openair0_config_t *openair0_cfg) {
recplay_state_t *s = device->recplay_state;
recplay_conf_t *c = openair0_cfg->recplay_conf;
if (s->use_mmap) {
// use mmap
s->mmapfd = open(c->u_sf_filename, O_RDONLY );
if (s->mmapfd != 0) {
struct stat sb;
fstat(s->mmapfd, &sb);
s->mapsize=sb.st_size;
LOG_I(HW,"Loading subframes using mmap() from %s size=%lu bytes ...\n",c->u_sf_filename, (uint64_t)sb.st_size );
void *mptr = mmap(NULL, sb.st_size, PROT_WRITE, MAP_PRIVATE, s->mmapfd, 0) ;
s->ms_sample = (iqrec_t *) ( mmap(NULL, sb.st_size, PROT_WRITE, MAP_PRIVATE, s->mmapfd, 0) + sizeof(iqfile_header_t));
if (mptr != MAP_FAILED) {
parse_iqfile_header(device, (iqfile_header_t *)mptr);
s->ms_sample = (iqrec_t *)((char *)mptr + sizeof(iqfile_header_t));
s->nb_samples = ((sb.st_size-sizeof(iqfile_header_t)) / sizeof(iqrec_t));
int aligned = (((unsigned long)s->ms_sample & 31) == 0)? 1:0;
LOG_I(HW,"Loaded %u subframes.\n",s->nb_samples );
if (aligned == 0) {
LOG_E(HW, "mmap address is not 32 bytes aligned, exiting.\n" );
close(s->mmapfd);
exit(-1);
}
} else {
LOG_E(HW,"Cannot mmap file, exiting.\n");
close(s->mmapfd);
exit(-1);
}
} else {
LOG_E( HW,"Cannot open %s exiting.\n", c->u_sf_filename );
exit(-1);
}
} else {
s->iqfd = open(c->u_sf_filename, O_RDONLY);
if (s->iqfd != 0) {
struct stat sb;
iqfile_header_t fh;
size_t hs = read(s->iqfd,&fh,sizeof(fh));
if (hs == sizeof(fh)) {
parse_iqfile_header(device, &fh);
fstat(s->iqfd, &sb);
s->mapsize=sb.st_size;
s->nb_samples = ((sb.st_size-sizeof(iqfile_header_t))/ sizeof(iqrec_t));
LOG_I(HW, "Loading %u subframes from %s,size=%lu bytes ...\n",s->nb_samples, c->u_sf_filename,(uint64_t)sb.st_size);
// allocate buffer for 1 sample at a time
s->ms_sample = (iqrec_t *) malloc(sizeof(iqrec_t));
if (s->ms_sample == NULL) {
LOG_E(HW,"Memory allocation failed for individual subframe replay mode.\n" );
close(s->iqfd);
exit(-1);
}
memset(s->ms_sample, 0, sizeof(iqrec_t));
// point at beginning of iqs in file
if (lseek(s->iqfd,sizeof(iqfile_header_t), SEEK_SET) == 0) {
LOG_I(HW,"Initial seek at beginning of the file\n" );
} else {
LOG_I(HW,"Problem initial seek at beginning of the file\n");
}
} else {
LOG_E(HW,"Cannot read header in %s exiting.\n",c->u_sf_filename );
close(s->iqfd);
exit(-1);
}
} else {
LOG_E(HW,"Cannot open %s exiting.\n",c->u_sf_filename );
exit(-1);
}
}
return 0;
}
/*! \brief start the oai iq player
* \param device, the hardware used
*/
static int trx_iqplayer_start(openair0_device *device){
return 0;
}
/*! \brief Terminate operation of the oai iq player
* \param device, the hardware used
*/
static void trx_iqplayer_end(openair0_device *device) {
if (device == NULL)
return;
if (device->recplay_state == NULL)
return;
if (device->recplay_state->use_mmap) {
if (device->recplay_state->ms_sample != MAP_FAILED) {
munmap(device->recplay_state->ms_sample, device->recplay_state->mapsize);
device->recplay_state->ms_sample = NULL;
}
if (device->recplay_state->mmapfd != 0) {
close(device->recplay_state->mmapfd);
device->recplay_state->mmapfd = 0;
}
} else {
if (device->recplay_state->ms_sample != NULL) {
free(device->recplay_state->ms_sample);
device->recplay_state->ms_sample = NULL;
}
if (device->recplay_state->iqfd != 0) {
close(device->recplay_state->iqfd);
device->recplay_state->iqfd = 0;
}
}
}
/*! \brief Write iqs function when in replay mode, just introduce a delay, as configured at init time,
@param device pointer to the device structure specific to the RF hardware target
@param timestamp The timestamp at which the first sample MUST be sent
@param buff Buffer which holds the samples
@param nsamps number of samples to be sent
@param antenna_id index of the antenna if the device has multiple antennas
@param flags flags must be set to TRUE if timestamp parameter needs to be applied
*/
static int trx_iqplayer_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) {
struct timespec req;
req.tv_sec = 0;
req.tv_nsec = device->openair0_cfg->recplay_conf->u_sf_write_delay * 1000;
nanosleep(&req, NULL);
return nsamps;
}
/*! \brief Receive samples from iq file.
* Read \ref nsamps samples from each channel to buffers. buff[0] is the array for
* the first channel. *ptimestamp is the time at which the first sample
* was received.
* \param device the hardware to use
* \param[out] ptimestamp the time at which the first sample was received.
* \param[out] buff An array of pointers to buffers for received samples. The buffers must be large enough to hold the number of samples \ref nsamps.
* \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte.
* \param antenna_id Index of antenna for which to receive samples
* \returns the number of sample read
*/
static int trx_iqplayer_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) {
int samples_received=0;
static unsigned int cur_samples;
static int64_t wrap_count;
static int64_t wrap_ts;
recplay_state_t *s = device->recplay_state;
if (cur_samples == s->nb_samples) {
cur_samples = 0;
wrap_count++;
if (wrap_count == device->openair0_cfg->recplay_conf->u_sf_loops) {
LOG_W(HW, "iqplayer device terminating subframes replay after %u iteration\n",device->openair0_cfg->recplay_conf->u_sf_loops);
exit_function(__FILE__, __FUNCTION__, __LINE__,"replay ended, triggering process termination\n");
}
wrap_ts = wrap_count * (s->nb_samples * (((int)(device->openair0_cfg[0].sample_rate)) / 1000));
if (!device->recplay_state->use_mmap) {
if (lseek(device->recplay_state->iqfd, 0, SEEK_SET) == 0) {
LOG_I(HW,"Seeking at the beginning of IQ file");
} else {
LOG_I(HW, "Problem seeking at the beginning of IQ file");
}
}
}
if (s->use_mmap) {
if (cur_samples < s->nb_samples) {
*ptimestamp = (s->ms_sample[0].ts + (cur_samples * (((int)(device->openair0_cfg[0].sample_rate)) / 1000))) + wrap_ts;
if (cur_samples == 0) {
LOG_I(HW,"starting subframes file with wrap_count=%lu wrap_ts=%lu ts=%lu\n", wrap_count,wrap_ts,*ptimestamp);
}
memcpy(buff[0], &s->ms_sample[cur_samples].samples[0], nsamps*4);
cur_samples++;
}
} else {
// read sample from file
if (read(s->iqfd, s->ms_sample, sizeof(iqrec_t)) != sizeof(iqrec_t)) {
LOG_E(HW,"pb reading iqfile at index %lu\n",sizeof(iqrec_t)*cur_samples );
close(s->iqfd);
free(s->ms_sample);
s->ms_sample = NULL;
s->iqfd = 0;
exit(-1);
}
if (cur_samples < s->nb_samples) {
static int64_t ts0 = 0;
if ((cur_samples == 0) && (wrap_count == 0)) {
ts0 = s->ms_sample->ts;
}
*ptimestamp = ts0 + (cur_samples * (((int)(device->openair0_cfg[0].sample_rate)) / 1000)) + wrap_ts;
if (cur_samples == 0) {
LOG_I(HW, "starting subframes file with wrap_count=%lu wrap_ts=%lu ts=%lu ",wrap_count,wrap_ts, *ptimestamp);
}
memcpy(buff[0], &s->ms_sample->samples[0], nsamps*4);
cur_samples++;
// Prepare for next read
off_t where = lseek(s->iqfd, cur_samples * sizeof(iqrec_t), SEEK_SET);
if (where < 0) {
LOG_E(HW,"Cannot lseek in iqfile: %s\n",strerror(errno));
exit(-1);
}
}
}
struct timespec req;
req.tv_sec = 0;
req.tv_nsec = (device->openair0_cfg[0].recplay_conf->u_sf_read_delay) * 1000;
nanosleep(&req, NULL);
return nsamps;
return samples_received;
}
int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
device->openair0_cfg = openair0_cfg;
device->trx_start_func = trx_iqplayer_start;
device->trx_get_stats_func = NULL;
device->trx_reset_stats_func = NULL;
device->trx_end_func = trx_iqplayer_end;
device->trx_stop_func = NULL;
device->trx_set_freq_func = NULL;
device->trx_set_gains_func = NULL;
// Replay subframes from from file
// openair0_cfg[0].rx_gain_calib_table = calib_table_b210_38;
// bw_gain_adjust=1;
device->trx_write_func = trx_iqplayer_write;
device->trx_read_func = trx_iqplayer_read;
iqplayer_loadfile(device, openair0_cfg);
LOG_UI(HW,"iqplayer device initialized, replay %s for %i iterations",openair0_cfg->recplay_conf->u_sf_filename,openair0_cfg->recplay_conf->u_sf_loops);
return 0;
}
/*@}*/
+38 -3
View File
@@ -3,6 +3,12 @@ This is an RF simulator that allows to test OAI without an RF board. It replaces
As much as possible, it works like an RF board, but not in real-time: It can run faster than real-time if there is enough CPU, or slower (it is CPU-bound instead of real-time RF sampling-bound).
It can be run either in:
- "noS1" mode: the generated IP traffic is sent and received between gNB and UE IP tunnel interfaces ("oaitun") by applications like ping and iperf
- "phy-test" mode: random UL and DL traffic is generated at every scheduling opportunity
# build
## From [build_oai](../../../doc/BUILD.md) script
@@ -84,19 +90,48 @@ make rfsimulator
### Launch gNB in one window
```bash
sudo RFSIMULATOR=server ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf --parallel-config PARALLEL_SINGLE_THREAD
sudo RFSIMULATOR=server ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf --parallel-config PARALLEL_SINGLE_THREAD --rfsim --phy-test
```
### Launch UE in another window
```bash
sudo RFSIMULATOR=127.0.0.1 ./nr-uesoftmodem --numerology 1 -r 106 -C 3510000000
sudo RFSIMULATOR=<TARGET_GNB_INTERFACE_ADDRESS> ./nr-uesoftmodem --rfsim --phy-test --rrc_config_path .
```
Of course, set the gNB machine IP address if the UE and the gNB are not on the same machine.
Notes:
1. <TARGET_GNB_INTERFACE_ADDRESS> can be 127.0.0.1 if both gNB and nrUE executables run on the same host, OR the IP interface address of the remote host running the gNB executable, if the gNB and nrUE run on separate hosts
2. the --rrc_config_path parameter SHALL specify where the 2 RAW files are located (`rbconfig.raw` and `reconfig.raw`).
- If you are running on the same machine and launched the 2 executables (`nr-softmodem` and `nr-uesoftmodem`) from the same directory, nothing has to be done.
- If you launched the 2 executables from 2 different folders, just point to the location where you launched the `nr-softmodem`:
* `sudo RFSIMULATOR=<TARGET_GNB_INTERFACE_ADDRESS> ./nr-uesoftmodem --rfsim --phy-test --rrc_config_path /the/path/where/you/launched/nr-softmodem`
- If you are not running on the same machine or launched the 2 executables from 2 different folders, you need to **COPY** the 2 raw files
* `scp usera@machineA:/the/path/where/you/launched/nr-softmodem/r*config.raw userb@machineB:/the/path/where/you/will/launch/nr-uesoftmodem/`
* Obviously this operation SHALL be done before launching the `nr-uesoftmodem` executable.
3. to enable the noS1 mode --noS1 and --nokrnmod 1 options should be added to the command line
In the UE, you can add `-d` option to get the softscope.
### Testing IP traffic (ping and iperf)
```
ping -I oaitun_enb1 10.0.1.2 (from gNB mchine)
ping -I oaitun_ue1 10.0.1.1 (from nrUE mchine)
```
```iperf (Downlink):
Server nrUE machine: iperf -s -i 1 -u -B 10.0.1.2
Client gNB machine: iperf -c 10.0.1.2 -u -b 0.1M --bind 10.0.1.1
```
```iperf (Uplink):
Server gNB machine: iperf -s -i 1 -u -B 10.0.1.1
Client nrUE machine: iperf -c 10.0.1.1 -u -b 0.1M --bind 10.0.1.2
Note: iperf tests can be performed only when running gNB and nrUE on separate hosts.
```
### Store and replay
You can store emitted I/Q samples. If you set the option `saviq`, the simulator will write all the I/Q samples into this file. Then, you can replay with the executable `replay_node`.
+26 -5
View File
@@ -1,7 +1,24 @@
/*
Author: Laurent THOMAS, Open Cells for Nokia
copyleft: OpenAirInterface Software Alliance and it's licence
* 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
*
* Author and copyright: Laurent Thomas, open-cells.com
*
* 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
*/
@@ -17,6 +34,9 @@
#include <common/utils/LOG/log.h>
#include <common/config/config_userapi.h>
#include <openair1/SIMULATION/TOOLS/sim.h>
#include <common/utils/telnetsrv/telnetsrv.h>
#include <common/utils/load_module_shlib.h>
#include <targets/ARCH/rfsimulator/rfsimulator.h>
/*
Legacy study:
@@ -82,8 +102,8 @@ void rxAddInput( struct complex16 *input_sig, struct complex16 *after_channel_si
} //l
}
out_ptr->r += round(rx_tmp.x*pathLossLinear + noise_per_sample*gaussdouble(0.0,1.0));
out_ptr->i += round(rx_tmp.y*pathLossLinear + noise_per_sample*gaussdouble(0.0,1.0));
out_ptr->r += round(rx_tmp.x*pathLossLinear + noise_per_sample*gaussZiggurat(0.0,1.0));
out_ptr->i += round(rx_tmp.y*pathLossLinear + noise_per_sample*gaussZiggurat(0.0,1.0));
out_ptr++;
}
@@ -95,3 +115,4 @@ void rxAddInput( struct complex16 *input_sig, struct complex16 *after_channel_si
channelDesc->path_loss_dB,
10*log10(noise_per_sample));
}
+108
View File
@@ -0,0 +1,108 @@
/*
* 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
*
* Author and copyright: Laurent Thomas, open-cells.com
*
* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <errno.h>
#include <common/utils/assertions.h>
#include <common/utils/LOG/log.h>
#include <common/config/config_userapi.h>
#include <openair1/SIMULATION/TOOLS/sim.h>
#include <targets/ARCH/rfsimulator/rfsimulator.h>
// Ziggurat
static double wn[128],fn[128];
static uint32_t iz,jz,jsr=123456789,kn[128];
static int32_t hz;
#define SHR3 (jz=jsr, jsr^=(jsr<<13),jsr^=(jsr>>17),jsr^=(jsr<<5),jz+jsr)
#define UNI (0.5+(signed) SHR3 * 0.2328306e-9)
double nfix(void) {
const double r = 3.442620;
static double x, y;
for (;;) {
x=hz * wn[iz];
if (iz==0) {
do {
x = - 0.2904764 * log (UNI);
y = - log (UNI);
} while (y+y < x*x);
return (hz>0)? r+x : -r-x;
}
if (fn[iz]+UNI*(fn[iz-1]-fn[iz])<exp(-0.5*x*x)) {
return x;
}
hz = SHR3;
iz = hz&127;
if (abs(hz) < kn[iz]) {
return ((hz)*wn[iz]);
}
}
}
/*!\Procedure to create tables for normal distribution kn,wn and fn. */
void tableNor(unsigned long seed) {
jsr=seed;
double dn = 3.442619855899;
int i;
const double m1 = 2147483648.0;
double q;
double tn = 3.442619855899;
const double vn = 9.91256303526217E-03;
q = vn/exp(-0.5*dn*dn);
kn[0] = ((dn/q)*m1);
kn[1] = 0;
wn[0] = ( q / m1 );
wn[127] = ( dn / m1 );
fn[0] = 1.0;
fn[127] = ( exp ( - 0.5 * dn * dn ) );
for ( i = 126; 1 <= i; i-- ) {
dn = sqrt (-2.0 * log ( vn/dn + exp(-0.5*dn*dn)));
kn[i+1] = ((dn / tn)*m1);
tn = dn;
fn[i] = (exp (-0.5*dn*dn));
wn[i] = (dn / m1);
}
return;
}
double gaussZiggurat(double mean, double variance) {
hz=SHR3;
iz=hz&127;
return abs(hz)<kn[iz]? hz*wn[iz] : nfix();
}
+37
View File
@@ -0,0 +1,37 @@
/*
* 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
*
* Author and copyright: Laurent Thomas, open-cells.com
*
* 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 __RFSIMULATOR_H
#define __RFSIMULATOR_H
double gaussZiggurat(double mean, double variance);
void tableNor(unsigned long seed);
void rxAddInput( struct complex16 *input_sig,
struct complex16 *after_channel_sig,
int rxAnt,
channel_desc_t *channelDesc,
int nbSamples,
uint64_t TS,
uint32_t CirSize
);
#endif
+156 -29
View File
@@ -1,8 +1,27 @@
/*
Author: Laurent THOMAS, Open Cells for Nokia
copyleft: OpenAirInterface Software Alliance and it's licence
* 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
*
* Author and copyright: Laurent Thomas, open-cells.com
*
* 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
*/
/*
* Open issues and limitations
* The read and write should be called in the same thread, that is not new USRP UHD design
@@ -25,15 +44,17 @@
#include <common/utils/assertions.h>
#include <common/utils/LOG/log.h>
#include <common/utils/load_module_shlib.h>
#include <common/utils/telnetsrv/telnetsrv.h>
#include <common/config/config_userapi.h>
#include "common_lib.h"
#include <openair1/PHY/defs_eNB.h>
#include "openair1/PHY/defs_UE.h"
#define CHANNELMOD_DYNAMICLOAD
#include <openair1/SIMULATION/TOOLS/sim.h>
#include <targets/ARCH/rfsimulator/rfsimulator.h>
#define PORT 4043 //default TCP port for this simulator
#define CirSize 3072000 // 100ms is enough
#define CirSize 307200 // 100ms is enough
#define sampleToByte(a,b) ((a)*(b)*sizeof(sample_t))
#define byteToSample(a,b) ((a)/(sizeof(sample_t)*(b)))
@@ -58,11 +79,25 @@ extern RAN_CONTEXT_t RC;
#define RFSIMULATOR_PARAMS_DESC {\
{"serveraddr", "<ip address to connect to>\n", 0, strptr:&(rfsimulator->ip), defstrval:"127.0.0.1", TYPE_STRING, 0 },\
{"serverport", "<port to connect to>\n", 0, u16ptr:&(rfsimulator->port), defuintval:PORT, TYPE_UINT16, 0 },\
{RFSIMU_OPTIONS_PARAMNAME, RFSIM_CONFIG_HELP_OPTIONS, 0, strlistptr:NULL, defstrlistval:NULL, TYPE_STRINGLIST,0},\
{RFSIMU_OPTIONS_PARAMNAME, RFSIM_CONFIG_HELP_OPTIONS, 0, strlistptr:NULL, defstrlistval:NULL, TYPE_STRINGLIST,0 },\
{"IQfile", "<file path to use when saving IQs>\n", 0, strptr:&(saveF), defstrval:"/tmp/rfsimulator.iqs",TYPE_STRING, 0 },\
{"modelname", "<channel model name>\n", 0, strptr:&(modelname), defstrval:"AWGN", TYPE_STRING, 0 }\
{"modelname", "<channel model name>\n", 0, strptr:&(modelname), defstrval:"AWGN", TYPE_STRING, 0 },\
{"ploss", "<channel path loss in dB>\n", 0, dblptr:&(rfsimulator->chan_pathloss), defdblval:0, TYPE_DOUBLE, 0 },\
{"forgetfact", "<channel forget factor ((0 to 1)>\n", 0, dblptr:&(rfsimulator->chan_forgetfact), defdblval:0, TYPE_DOUBLE, 0 },\
{"offset", "<channel offset in samps>\n", 0, iptr:&(rfsimulator->chan_offset), defintval:0, TYPE_INT, 0 }\
};
static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
static telnetshell_cmddef_t rfsimu_cmdarray[] = {
{"setmodel","<model name>",(cmdfunc_t)rfsimu_setchanmod_cmd,TELNETSRV_CMDFLAG_PUSHINTPOOLQ},
{"","",NULL},
};
static telnetshell_vardef_t rfsimu_vardef[] = {
{"",0,NULL}
};
pthread_mutex_t Sockmutex;
typedef struct complex16 sample_t; // 2*16 bits complex number
@@ -94,6 +129,11 @@ typedef struct {
double sample_rate;
double tx_bw;
int channelmod;
double chan_pathloss;
double chan_forgetfact;
int chan_offset;
void *telnetcmd_qid;
poll_telnetcmdq_func_t poll_telnetcmdq;
} rfsimulator_state_t;
@@ -127,13 +167,29 @@ void allocCirBuf(rfsimulator_state_t *bridge, int sock) {
// the value channel_model->path_loss_dB seems only a storage place (new_channel_desc_scm() only copy the passed value)
// Legacy changes directlty the variable channel_model->path_loss_dB place to place
// while calling new_channel_desc_scm() with path losses = 0
static bool init_done=false;
if (!init_done) {
uint64_t rand;
FILE *h=fopen("/dev/random","r");
if ( 1 != fread(&rand,sizeof(rand),1,h) )
LOG_W(HW, "Simulator can't read /dev/random\n");
fclose(h);
randominit(rand);
tableNor(rand);
init_done=true;
}
ptr->channel_model=new_channel_desc_scm(bridge->tx_num_channels,bridge->rx_num_channels,
bridge->channelmod,
bridge->sample_rate,
bridge->tx_bw,
0.0, // forgetting_factor
0, // maybe used for TA
0); // path_loss in dB
30e-9, // TDL delay-spread parameter
bridge->chan_forgetfact, // forgetting_factor
bridge->chan_offset, // maybe used for TA
bridge->chan_pathloss); // path_loss in dB
set_channeldesc_owner(ptr->channel_model, RFSIMU_MODULEID);
random_channel(ptr->channel_model,false);
}
}
@@ -260,6 +316,42 @@ void rfsimulator_readconfig(rfsimulator_state_t *rfsimulator) {
rfsimulator->typeStamp = UE_MAGICDL_FDD;
}
static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg) {
char *modelname=NULL;
int s = sscanf(buff,"%ms\n",&modelname);
if (s == 1) {
int channelmod=modelid_fromname(modelname);
if (channelmod<0)
prnt("ERROR: model %s unknown\n",modelname);
else {
rfsimulator_state_t *t = (rfsimulator_state_t *)arg;
for (int i=0; i<FD_SETSIZE; i++) {
buffer_t *b=&t->buf[i];
if (b->conn_sock >= 0 ) {
channel_desc_t *newmodel=new_channel_desc_scm(t->tx_num_channels,t->rx_num_channels,
channelmod,
t->sample_rate,
t->tx_bw,
30e-9, // TDL delay-spread parameter
t->chan_forgetfact, // forgetting_factor
t->chan_offset, // maybe used for TA
t->chan_pathloss); // path_loss in dB
set_channeldesc_owner(newmodel, RFSIMU_MODULEID);
random_channel(newmodel,false);
channel_desc_t *oldmodel=b->channel_model;
b->channel_model=newmodel;
free_channel_desc_scm(oldmodel);
prnt("New model %s applied to channel connected to sock %d\n",modelname,i);
}
}
}
} else {
prnt("ERROR: no model specified\n");
}
free(modelname);
return CMDSTATUS_FOUND;
}
int server_start(openair0_device *device) {
rfsimulator_state_t *t = (rfsimulator_state_t *) device->priv;
t->typeStamp=ENB_MAGICDL_FDD;
@@ -346,8 +438,10 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, openair0_timestamp
if ( t->lastWroteTS != 0 && abs((double)t->lastWroteTS-timestamp) > (double)CirSize)
LOG_E(HW,"Discontinuous TX gap too large Tx:%lu, %lu\n", t->lastWroteTS, timestamp);
AssertFatal(t->lastWroteTS <= timestamp+1, " Not supported to send Tx out of order (same in USRP) %lu, %lu\n",
if (t->lastWroteTS > timestamp+nsamps)
LOG_E(HW,"Not supported to send Tx out of order (same in USRP) %lu, %lu\n",
t->lastWroteTS, timestamp);
t->lastWroteTS=timestamp+nsamps;
if (!alreadyLocked)
@@ -365,7 +459,7 @@ int rfsimulator_write(openair0_device *device, openair0_timestamp timestamp, voi
static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initial) {
// Process all incoming events on sockets
// store the data in lists
struct epoll_event events[FD_SETSIZE]= {0};
struct epoll_event events[FD_SETSIZE]= {{0}};
int nfds = epoll_wait(t->epollfd, events, FD_SETSIZE, timeout);
if ( nfds==-1 ) {
@@ -420,7 +514,7 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi
if ( sz < 0 ) {
if ( errno != EAGAIN ) {
LOG_E(HW,"socket failed %s\n", strerror(errno));
abort();
//abort();
}
} else if ( sz == 0 )
continue;
@@ -439,22 +533,31 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi
b->headerMode=false;
if ( t->nextTimestamp == 0 ) { // First block in UE, resync with the eNB current TS
t->nextTimestamp = b->th.timestamp > nsamps_for_initial ? b->th.timestamp - nsamps_for_initial : 0;
b->lastReceivedTS = b->th.timestamp > nsamps_for_initial ? b->th.timestamp : nsamps_for_initial;
LOG_W(HW,"UE got first timestamp: starting at %lu\n", t->nextTimestamp);
b->trashingPacket=true;
} else if ( b->lastReceivedTS < b->th.timestamp) {
t->nextTimestamp=b->th.timestamp> nsamps_for_initial ?
b->th.timestamp - nsamps_for_initial :
0;
b->lastReceivedTS=b->th.timestamp> nsamps_for_initial ?
b->th.timestamp :
nsamps_for_initial;
LOG_W(HW,"UE got first timestamp: starting at %lu\n", t->nextTimestamp);
b->trashingPacket=true;
} else if ( b->lastReceivedTS < b->th.timestamp) {
int nbAnt= b->th.nbAnt;
if ( b->th.timestamp-b->lastReceivedTS < CirSize ) {
for (uint64_t index=b->lastReceivedTS; index < b->th.timestamp; index++ ) {
for (int a=0; a < nbAnt; a++) {
b->circularBuf[(index*nbAnt+a)%CirSize].r = 0;
b->circularBuf[(index*nbAnt+a)%CirSize].i = 0;
}
}
} else {
memset(b->circularBuf, 0, sampleToByte(CirSize,1));
}
if (b->lastReceivedTS != 0 && b->th.timestamp-b->lastReceivedTS > 50 )
LOG_W(HW,"UEsock: %d gap of: %ld in reception\n", fd, b->th.timestamp-b->lastReceivedTS );
b->lastReceivedTS=b->th.timestamp;
} else if ( b->lastReceivedTS > b->th.timestamp && b->th.size == 1 ) {
@@ -463,8 +566,8 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi
} else if ( b->lastReceivedTS == b->th.timestamp ) {
// normal case
} else {
abort();
AssertFatal(false, "received data in past: current is %lu, new reception: %lu!\n", b->lastReceivedTS, b->th.timestamp);
LOG_E(HW, "received data in past: current is %lu, new reception: %lu!\n", b->lastReceivedTS, b->th.timestamp);
b->trashingPacket=true;
}
pthread_mutex_lock(&Sockmutex);
@@ -473,14 +576,14 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi
LOG_E(HW,"UEsock: %d Tx/Rx shift too large Tx:%lu, Rx:%lu\n", fd, t->lastWroteTS, b->lastReceivedTS);
pthread_mutex_unlock(&Sockmutex);
b->transferPtr=(char *)&b->circularBuf[b->lastReceivedTS%CirSize];
b->transferPtr=(char *)&b->circularBuf[(b->lastReceivedTS*b->th.nbAnt)%CirSize];
b->remainToTransfer=sampleToByte(b->th.size, b->th.nbAnt);
}
if ( b->headerMode==false ) {
LOG_D(HW,"UEsock: %d Set b->lastReceivedTS %ld\n", fd, b->lastReceivedTS);
if ( ! b->trashingPacket ) {
b->lastReceivedTS=b->th.timestamp+b->th.size-byteToSample(b->remainToTransfer,b->th.nbAnt);
LOG_D(HW,"UEsock: %d Set b->lastReceivedTS %ld\n", fd, b->lastReceivedTS);
}
if ( b->remainToTransfer==0) {
@@ -537,6 +640,7 @@ int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimestamp, vo
pthread_mutex_unlock(&Sockmutex);
usleep(10000);
pthread_mutex_lock(&Sockmutex);
if ( t->lastWroteTS < t->nextTimestamp ) {
// Assuming Tx is not done fully in another thread
// We can never write is the past from the received time
@@ -550,6 +654,7 @@ int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimestamp, vo
for ( int i=0; i < t->tx_num_channels; i++)
samplesVoid[i]=(void *)&v;
LOG_I(HW, "No samples Tx occured, so we send 1 sample to notify it: Tx:%lu, Rx:%lu\n",
t->lastWroteTS, t->nextTimestamp);
rfsimulator_write_internal(t, t->nextTimestamp,
@@ -601,8 +706,10 @@ int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimestamp, vo
// it seems legacy behavior is: never in UL, each frame in DL
if (reGenerateChannel)
random_channel(ptr->channel_model,0);
if (t->poll_telnetcmdq)
t->poll_telnetcmdq(t->telnetcmd_qid,t);
for (int a=0; a<nbAnt; a++) {
for (int a=0; a<nbAnt; a++) {//loop over number of Rx antennas
if ( ptr->channel_model != NULL ) // apply a channel model
rxAddInput( ptr->circularBuf, (struct complex16 *) samplesVoid[a],
a,
@@ -613,13 +720,16 @@ int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimestamp, vo
);
else { // no channel modeling
sample_t *out=(sample_t *)samplesVoid[a];
for ( int i=0; i < nsamps; i++ ) {
out[i].r+=ptr->circularBuf[((t->nextTimestamp+i)*nbAnt+a)%CirSize].r;
out[i].i+=ptr->circularBuf[((t->nextTimestamp+i)*nbAnt+a)%CirSize].i;
}
int nbAnt_tx = ptr->th.nbAnt;//number of Tx antennas
//LOG_I(HW, "nbAnt_tx %d\n",nbAnt_tx);
for (int i=0; i < nsamps; i++) {//loop over nsamps
for (int a_tx=0; a_tx<nbAnt_tx; a_tx++){//sum up signals from nbAnt_tx antennas
out[i].r+=ptr->circularBuf[((t->nextTimestamp+i)*nbAnt_tx+a_tx)%CirSize].r;
out[i].i+=ptr->circularBuf[((t->nextTimestamp+i)*nbAnt_tx+a_tx)%CirSize].i;
} // end for a_tx
} // end for i (number of samps)
} // end of no channel modeling
} // end for a...
} // end for a (number of rx antennas)
}
}
@@ -655,6 +765,9 @@ int rfsimulator_set_freq(openair0_device *device, openair0_config_t *openair0_cf
int rfsimulator_set_gains(openair0_device *device, openair0_config_t *openair0_cfg) {
return 0;
}
int rfsimulator_write_init(openair0_device *device){
return 0;
}
__attribute__((__visibility__("default")))
int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
// to change the log level, use this on command line
@@ -678,6 +791,7 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
device->type = USRP_B200_DEV;
device->openair0_cfg=&openair0_cfg[0];
device->priv = rfsimulator;
device->trx_write_init = rfsimulator_write_init;
for (int i=0; i<FD_SETSIZE; i++)
rfsimulator->buf[i].conn_sock=-1;
@@ -688,7 +802,20 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
rfsimulator->rx_num_channels=openair0_cfg->rx_num_channels;
rfsimulator->sample_rate=openair0_cfg->sample_rate;
rfsimulator->tx_bw=openair0_cfg->tx_bw;
randominit(0);
//randominit(0);
set_taus_seed(0);
/* look for telnet server, if it is loaded, add the channel modeling commands to it */
add_telnetcmd_func_t addcmd = (add_telnetcmd_func_t)get_shlibmodule_fptr("telnetsrv", TELNET_ADDCMD_FNAME);
if (addcmd != NULL) {
rfsimulator->poll_telnetcmdq = (poll_telnetcmdq_func_t)get_shlibmodule_fptr("telnetsrv", TELNET_POLLCMDQ_FNAME);
addcmd("rfsimu",rfsimu_vardef,rfsimu_cmdarray);
for(int i=0; rfsimu_cmdarray[i].cmdfunc != NULL; i++) {
if ( rfsimu_cmdarray[i].qptr != NULL) {
rfsimulator->telnetcmd_qid = rfsimu_cmdarray[i].qptr;
break;
}
}
}
return 0;
}
+21 -2
View File
@@ -1,8 +1,27 @@
/*
Author: Laurent THOMAS, Open Cells
copyleft: OpenAirInterface Software Alliance and it's licence
* 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
*
* Author and copyright: Laurent Thomas, open-cells.com
*
* 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 <common/utils/simple_executable.h>
volatile int oai_exit = 0;
+6
View File
@@ -243,6 +243,11 @@ ts += nsamps;
return nsamps;
}
int tcp_bridge_write_init(openair0_device *device)
{
return 0;
}
__attribute__((__visibility__("default")))
int device_init(openair0_device* device, openair0_config_t *openair0_cfg)
{
@@ -267,6 +272,7 @@ int device_init(openair0_device* device, openair0_config_t *openair0_cfg)
device->trx_set_gains_func = tcp_bridge_set_gains;
device->trx_write_func = tcp_bridge_write;
device->trx_read_func = tcp_bridge_read;
device->trx_write_init = tcp_bridge_write_init;
device->priv = tcp_bridge;