Files
openairinterface5g/radio/COMMON/common_lib.c
2026-07-09 11:34:00 +02:00

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);
}