mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
290 lines
10 KiB
C
290 lines
10 KiB
C
/*
|
|
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
|
*/
|
|
|
|
/*!
|
|
* \brief common APIs for different RF frontend device
|
|
*/
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <strings.h>
|
|
#include <dlfcn.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "common_lib.h"
|
|
#include "assertions.h"
|
|
#include "common/utils/load_module_shlib.h"
|
|
#include "common/utils/LOG/log.h"
|
|
#include "executables/softmodem-common.h"
|
|
#include "common/config/config_paramdesc.h"
|
|
#include "common/config/config_userapi.h"
|
|
#include "common/cmake_defs.h"
|
|
|
|
#define MAX_GAP 100ULL
|
|
const char *const devtype_names[MAX_RF_DEV_TYPE] =
|
|
{"", "USRP B200", "USRP X300", "USRP N300", "USRP X400", "BLADERF", "LMSSDR", "IRIS", "No HW", "UEDv2", "RFSIMULATOR"};
|
|
|
|
const char *get_devname(int devtype) {
|
|
if (devtype < MAX_RF_DEV_TYPE && devtype !=MIN_RF_DEV_TYPE )
|
|
return devtype_names[devtype];
|
|
return "none";
|
|
}
|
|
|
|
int set_device(openair0_device_t *device)
|
|
{
|
|
const 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;
|
|
}
|
|
|
|
int set_transport(openair0_device_t *device)
|
|
{
|
|
switch (device->transp_type) {
|
|
case ETHERNET_TP:
|
|
LOG_I(HW,"[%s] has loaded ETHERNET trasport protocol.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
|
|
return 0;
|
|
break;
|
|
|
|
case NONE_TP:
|
|
LOG_I(HW,"[%s] has not loaded a transport protocol.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
|
|
return 0;
|
|
break;
|
|
|
|
default:
|
|
LOG_E(HW,"[%s] invalid transport protocol.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
|
|
return -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
typedef int (*devfunc_t)(openair0_device_t *, openair0_config_t *, eth_params_t *);
|
|
|
|
/* look for the interface library and load it */
|
|
int load_lib(openair0_device_t *device, openair0_config_t *openair0_cfg, eth_params_t *cfg, uint8_t flag)
|
|
{
|
|
loader_shlibfunc_t shlib_fdesc[1];
|
|
int ret=0;
|
|
char *deflibname=OAI_RF_LIBNAME;
|
|
openair0_cfg->command_line_sample_advance = get_softmodem_params()->command_line_sample_advance;
|
|
openair0_cfg->recplay_mode = read_recplayconfig(&(openair0_cfg->recplay_conf),&(device->recplay_state));
|
|
|
|
if (openair0_cfg->recplay_mode == RECPLAY_RECORDMODE) {
|
|
IS_SOFTMODEM_IQRECORDER = true; // softmodem has to know we use the iqrecorder to workaround randomized algorithms
|
|
}
|
|
if (openair0_cfg->recplay_mode == RECPLAY_REPLAYMODE) {
|
|
deflibname=OAI_IQPLAYER_LIBNAME;
|
|
shlib_fdesc[0].fname="device_init";
|
|
IS_SOFTMODEM_IQPLAYER = true; // softmodem has to know we use the iqplayer to workaround randomized algorithms
|
|
} 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)
|
|
deflibname="rfsimulator";
|
|
else
|
|
deflibname=OAI_RF_LIBNAME;
|
|
shlib_fdesc[0].fname="device_init";
|
|
} else if (flag == RAU_REMOTE_THIRDPARTY_RADIO_HEAD) {
|
|
deflibname=OAI_THIRDPARTY_TP_LIBNAME;
|
|
shlib_fdesc[0].fname="transport_init";
|
|
} else {
|
|
deflibname=OAI_TP_LIBNAME;
|
|
shlib_fdesc[0].fname="transport_init";
|
|
}
|
|
|
|
char *devname=NULL;
|
|
paramdef_t device_params[]=DEVICE_PARAMS_DESC ;
|
|
int numparams = sizeofArray(device_params);
|
|
int devname_pidx = config_paramidx_fromname(device_params,numparams, CONFIG_DEVICEOPT_NAME);
|
|
device_params[devname_pidx].defstrval=deflibname;
|
|
|
|
config_get(config_get_if(), 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);
|
|
}
|
|
|
|
int openair0_device_load(openair0_device_t *device, openair0_config_t *openair0_cfg)
|
|
{
|
|
int rc=0;
|
|
rc=load_lib(device, openair0_cfg, NULL,RAU_LOCAL_RADIO_HEAD );
|
|
|
|
if ( rc >= 0) {
|
|
if ( set_device(device) < 0) {
|
|
LOG_E(HW, "%s %d:Unsupported radio head\n", __FILE__, __LINE__);
|
|
return -1;
|
|
}
|
|
} else
|
|
AssertFatal(false, "can't open the radio device: %s\n", get_devname(device->type));
|
|
|
|
pthread_mutex_init(&device->reOrder.mutex_store, NULL);
|
|
pthread_mutex_init(&device->reOrder.mutex_write, NULL);
|
|
return rc;
|
|
}
|
|
|
|
int openair0_transport_load(openair0_device_t *device, openair0_config_t *openair0_cfg, eth_params_t *eth_params)
|
|
{
|
|
int rc;
|
|
rc=load_lib(device, openair0_cfg, eth_params, RAU_REMOTE_RADIO_HEAD);
|
|
|
|
if ( rc >= 0) {
|
|
if ( set_transport(device) < 0) {
|
|
LOG_E(HW, "%s %d:Unsupported transport protocol\n", __FILE__, __LINE__);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void writerEnqueue(re_order_t *ctx, openair0_timestamp_t timestamp, void **txp, int nsamps, int nbAnt, int flags)
|
|
{
|
|
pthread_mutex_lock(&ctx->mutex_store);
|
|
LOG_D(HW, "Enqueue write for TS: %lu\n", timestamp);
|
|
int i;
|
|
for (i = 0; i < WRITE_QUEUE_SZ; i++)
|
|
if (!ctx->queue[i].active) {
|
|
ctx->queue[i].timestamp = timestamp;
|
|
ctx->queue[i].active = true;
|
|
ctx->queue[i].nsamps = nsamps;
|
|
ctx->queue[i].nbAnt = nbAnt;
|
|
ctx->queue[i].flags = flags;
|
|
AssertFatal(nbAnt <= NB_ANTENNAS_TX, "");
|
|
for (int j = 0; j < nbAnt; j++)
|
|
ctx->queue[i].txp[j] = txp[j];
|
|
break;
|
|
}
|
|
AssertFatal(i < WRITE_QUEUE_SZ, "Write queue full\n");
|
|
pthread_mutex_unlock(&ctx->mutex_store);
|
|
}
|
|
|
|
typedef struct PHY_VARS_NR_UE_s PHY_VARS_NR_UE;
|
|
typedef int (*nrue_ru_write_t)(PHY_VARS_NR_UE *UE, openair0_timestamp_t timestamp, void **txp, int nsamps, int nbAnt, int flags);
|
|
|
|
static void writerProcessWaitingQueue(nrue_ru_write_t nrue_ru_write, PHY_VARS_NR_UE *UE, openair0_device_t *device)
|
|
{
|
|
bool found = false;
|
|
re_order_t *ctx = &device->reOrder;
|
|
do {
|
|
found = false;
|
|
pthread_mutex_lock(&ctx->mutex_store);
|
|
for (int i = 0; i < WRITE_QUEUE_SZ; i++) {
|
|
if (ctx->queue[i].active && llabs(ctx->queue[i].timestamp - ctx->nextTS) < MAX_GAP) {
|
|
openair0_timestamp_t timestamp = ctx->queue[i].timestamp;
|
|
LOG_D(HW, "Dequeue write for TS: %lu\n", timestamp);
|
|
int nsamps = ctx->queue[i].nsamps;
|
|
int nbAnt = ctx->queue[i].nbAnt;
|
|
int flags = ctx->queue[i].flags;
|
|
void *txp[NB_ANTENNAS_TX];
|
|
AssertFatal(nbAnt <= NB_ANTENNAS_TX, "");
|
|
for (int j = 0; j < nbAnt; j++)
|
|
txp[j] = ctx->queue[i].txp[j];
|
|
ctx->queue[i].active = false;
|
|
pthread_mutex_unlock(&ctx->mutex_store);
|
|
found = true;
|
|
if (flags || IS_SOFTMODEM_RFSIM) {
|
|
int wroteSamples;
|
|
if (nrue_ru_write)
|
|
wroteSamples = nrue_ru_write(UE, timestamp, txp, nsamps, nbAnt, flags);
|
|
else
|
|
wroteSamples = device->trx_write_func(device, timestamp, txp, nsamps, nbAnt, flags);
|
|
if (wroteSamples != nsamps)
|
|
LOG_W(HW, "Failed to write to RF: wrote %d out of %d samples\n", wroteSamples, nsamps);
|
|
}
|
|
ctx->nextTS = timestamp + nsamps;
|
|
pthread_mutex_lock(&ctx->mutex_store);
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&ctx->mutex_store);
|
|
} while (found);
|
|
}
|
|
|
|
// We assume the data behind *txp are permanently allocated
|
|
// When we will go further, we can remove all RC.xxx.txdata buffers in xNB, in UE
|
|
// but to make zerocopy and agnostic design, we need to make a proper ring buffer with mutex protection
|
|
// mutex (or atomic flags) will be mandatory because this out order system root cause is there are several writer threads
|
|
|
|
int openair0_write_reorder_common(nrue_ru_write_t nrue_ru_write,
|
|
PHY_VARS_NR_UE *UE,
|
|
openair0_device_t *device,
|
|
openair0_timestamp_t timestamp,
|
|
void **txp,
|
|
int nsamps,
|
|
int nbAnt,
|
|
int flags)
|
|
{
|
|
int wroteSamples = 0;
|
|
re_order_t *ctx = &device->reOrder;
|
|
LOG_D(HW, "received write order ts: %lu, nb samples %d, next ts %luflags %d\n", timestamp, nsamps, timestamp + nsamps, flags);
|
|
pthread_mutex_lock(&ctx->mutex_store);
|
|
if (!ctx->initDone) {
|
|
ctx->nextTS = timestamp;
|
|
for (int i = 0; i < WRITE_QUEUE_SZ; i++) {
|
|
ctx->queue[i].txp = malloc(sizeof(void *) * NB_ANTENNAS_TX);
|
|
}
|
|
ctx->initDone = true;
|
|
}
|
|
pthread_mutex_unlock(&ctx->mutex_store);
|
|
if (pthread_mutex_trylock(&ctx->mutex_write) == 0) {
|
|
// We have the write exclusivity
|
|
if (llabs(timestamp - ctx->nextTS) < MAX_GAP) { // We are writing in sequence of the previous write
|
|
if (flags || IS_SOFTMODEM_RFSIM) {
|
|
if (nrue_ru_write)
|
|
wroteSamples = nrue_ru_write(UE, timestamp, txp, nsamps, nbAnt, flags);
|
|
else
|
|
wroteSamples = device->trx_write_func(device, timestamp, txp, nsamps, nbAnt, flags);
|
|
if (wroteSamples != nsamps)
|
|
LOG_W(HW, "Failed to write to RF: wrote %d out of %d samples\n", wroteSamples, nsamps);
|
|
} else
|
|
wroteSamples = nsamps;
|
|
ctx->nextTS = timestamp + nsamps;
|
|
|
|
} else {
|
|
writerEnqueue(ctx, timestamp, txp, nsamps, nbAnt, flags);
|
|
}
|
|
writerProcessWaitingQueue(nrue_ru_write, UE, device);
|
|
pthread_mutex_unlock(&ctx->mutex_write);
|
|
return wroteSamples ? wroteSamples : nsamps;
|
|
}
|
|
writerEnqueue(ctx, timestamp, txp, nsamps, nbAnt, flags);
|
|
if (pthread_mutex_trylock(&ctx->mutex_write) == 0) {
|
|
writerProcessWaitingQueue(nrue_ru_write, UE, device);
|
|
pthread_mutex_unlock(&ctx->mutex_write);
|
|
}
|
|
return nsamps;
|
|
}
|
|
|
|
int openair0_write_reorder(openair0_device_t *device, openair0_timestamp_t timestamp, void **txp, int nsamps, int nbAnt, int flags)
|
|
{
|
|
return openair0_write_reorder_common(NULL, NULL, device, timestamp, txp, nsamps, nbAnt, flags);
|
|
}
|
|
|
|
void openair0_write_reorder_clear_context(openair0_device_t *device)
|
|
{
|
|
LOG_I(HW, "received write reorder clear context\n");
|
|
re_order_t *ctx = &device->reOrder;
|
|
if (!ctx->initDone)
|
|
return;
|
|
if (pthread_mutex_trylock(&ctx->mutex_write) != 0)
|
|
LOG_E(HW, "write_reorder_clear_context call while still writing on the device\n");
|
|
else
|
|
pthread_mutex_unlock(&ctx->mutex_write);
|
|
pthread_mutex_lock(&ctx->mutex_store);
|
|
for (int i = 0; i < WRITE_QUEUE_SZ; i++) {
|
|
ctx->queue[i].active = false;
|
|
free(ctx->queue[i].txp);
|
|
}
|
|
ctx->initDone = false;
|
|
pthread_mutex_unlock(&ctx->mutex_store);
|
|
}
|