- all RAN code, CI code, configuration files, dockerfiles, in CSSL v1.0
- all deployment code (openshift, charts, ancillary files like shell
scripts), in MIT
- documentation in CC-BY-4.0
- exceptions might apply and are listed in NOTICE
- there is a new LICENSES folder with all licenses
- CONTRIBUTIONS.md has been updated accordingly
For automated changes based on OAI PL v1.1:
perl -i~ -0pe 's/\/\*.*Licensed to the OpenAirInterface.*openairinterface.org\n#?/\/*\n * SPDX-License-Identifier: LicenseRef-CSSL-1.0\n/s' **/*.{c,h,cpp}
perl -i~ -0pe 's/\/\*.*Licensed to the OpenAirInterface.*openairinterface.org\n#?/\/*\n * SPDX-License-Identifier: LicenseRef-CSSL-1.0\n/s' **/*.ts
perl -i~ -0pe 's/<!--.*Licensed to the OpenAirInterface.*openairinterface.org\n.*-->/<!-- SPDX-License-Identifier: LicenseRef-CSSL-1.0 -->/s' **/*.xml
The rest (cmake, files with missing license, cmake) manually.
247 lines
6.1 KiB
C
247 lines
6.1 KiB
C
/*
|
|
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
|
*/
|
|
|
|
#include "time_source.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <stdbool.h>
|
|
#include <errno.h>
|
|
|
|
#include "common/utils/assertions.h"
|
|
#include "common/utils/system.h"
|
|
#include "common/utils/utils.h"
|
|
|
|
typedef void (*callback_function_t)(void *callback_data);
|
|
typedef void * callback_data_t;
|
|
|
|
typedef struct {
|
|
time_source_type_t type;
|
|
volatile callback_function_t callback;
|
|
volatile callback_data_t callback_data;
|
|
_Atomic bool exit;
|
|
void (*terminate)(void *this);
|
|
pthread_t thread_id;
|
|
pthread_mutex_t mutex;
|
|
pthread_cond_t cond;
|
|
} time_source_common_t;
|
|
|
|
typedef struct {
|
|
time_source_common_t common;
|
|
} time_source_realtime_t;
|
|
|
|
typedef struct {
|
|
time_source_common_t common;
|
|
volatile uint64_t iq_samples_count;
|
|
volatile uint64_t iq_samples_per_second;
|
|
} time_source_iq_t;
|
|
|
|
static void init_mutex(time_source_common_t *t)
|
|
{
|
|
int ret = pthread_mutex_init(&t->mutex, NULL);
|
|
DevAssert(ret == 0);
|
|
}
|
|
|
|
static void init_cond(time_source_common_t *t)
|
|
{
|
|
int ret = pthread_cond_init(&t->cond, NULL);
|
|
DevAssert(ret == 0);
|
|
}
|
|
|
|
static void lock(time_source_common_t *t)
|
|
{
|
|
int ret = pthread_mutex_lock(&t->mutex);
|
|
DevAssert(ret == 0);
|
|
}
|
|
|
|
static void unlock(time_source_common_t *t)
|
|
{
|
|
int ret = pthread_mutex_unlock(&t->mutex);
|
|
DevAssert(ret == 0);
|
|
}
|
|
|
|
static void condwait(time_source_common_t *t)
|
|
{
|
|
int ret = pthread_cond_wait(&t->cond, &t->mutex);
|
|
DevAssert(ret == 0);
|
|
}
|
|
|
|
static void condsignal(time_source_common_t *t)
|
|
{
|
|
int ret = pthread_cond_signal(&t->cond);
|
|
DevAssert(ret == 0);
|
|
}
|
|
|
|
static void wait_thread_termination(time_source_common_t *t)
|
|
{
|
|
void *retval;
|
|
int ret = pthread_join(t->thread_id, &retval);
|
|
DevAssert(ret == 0);
|
|
}
|
|
|
|
static struct timespec next_ms(struct timespec t)
|
|
{
|
|
/* go to next millisecond */
|
|
t.tv_nsec += 1000000;
|
|
if (t.tv_nsec > 999999999) {
|
|
t.tv_nsec -= 1000000000;
|
|
t.tv_sec++;
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
static void *time_source_realtime_thread(void *ts)
|
|
{
|
|
time_source_realtime_t *time_source = ts;
|
|
|
|
struct timespec now;
|
|
if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
|
|
AssertFatal(0, "clock_gettime failed\n");
|
|
|
|
/* go to next millisecond */
|
|
now = next_ms(now);
|
|
|
|
while (!time_source->common.exit) {
|
|
/* sleep */
|
|
if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &now, NULL) == -1) {
|
|
/* accept interruption by signals and then simply restart the sleep */
|
|
if (errno == EINTR)
|
|
continue;
|
|
AssertFatal(0, "clock_nanosleep failed\n");
|
|
}
|
|
|
|
if (time_source->common.exit)
|
|
break;
|
|
|
|
/* tick */
|
|
lock(&time_source->common);
|
|
void (*callback)(void *callback_data) = time_source->common.callback;
|
|
void *callback_data = time_source->common.callback_data;
|
|
unlock(&time_source->common);
|
|
if (callback != NULL)
|
|
callback(callback_data);
|
|
|
|
/* go to next millisecond */
|
|
now = next_ms(now);
|
|
}
|
|
|
|
return (void *)0;
|
|
}
|
|
|
|
static void *time_source_iq_samples_thread(void *ts)
|
|
{
|
|
time_source_iq_t *time_source = ts;
|
|
|
|
while (!time_source->common.exit) {
|
|
/* wait until we have at least 1ms of IQ samples */
|
|
lock(&time_source->common);
|
|
while (!time_source->common.exit
|
|
&& (time_source->iq_samples_per_second == 0
|
|
|| time_source->iq_samples_count < time_source->iq_samples_per_second / 1000))
|
|
condwait(&time_source->common);
|
|
time_source->iq_samples_count -= time_source->iq_samples_per_second / 1000;
|
|
unlock(&time_source->common);
|
|
|
|
if (time_source->common.exit)
|
|
break;
|
|
|
|
/* tick */
|
|
lock(&time_source->common);
|
|
void (*callback)(void *callback_data) = time_source->common.callback;
|
|
void *callback_data = time_source->common.callback_data;
|
|
unlock(&time_source->common);
|
|
if (callback != NULL)
|
|
callback(callback_data);
|
|
}
|
|
|
|
return (void *)0;
|
|
}
|
|
|
|
static void time_source_iq_samples_terminate(void *ts)
|
|
{
|
|
time_source_iq_t *time_source = ts;
|
|
condsignal(&time_source->common);
|
|
}
|
|
|
|
time_source_t *new_time_source(time_source_type_t type)
|
|
{
|
|
time_source_t *ret = NULL;
|
|
void *(*thread_function)(void *) = NULL;
|
|
void (*terminate_function)(void *) = NULL;
|
|
char *thread_function_name = NULL;
|
|
|
|
switch (type) {
|
|
case TIME_SOURCE_REALTIME: {
|
|
time_source_realtime_t *ts = calloc_or_fail(1, sizeof(*ts));
|
|
thread_function = time_source_realtime_thread;
|
|
thread_function_name = "time source realtime";
|
|
ret = ts;
|
|
break;
|
|
}
|
|
case TIME_SOURCE_IQ_SAMPLES: {
|
|
time_source_iq_t *ts = calloc_or_fail(1, sizeof(*ts));
|
|
thread_function = time_source_iq_samples_thread;
|
|
terminate_function = time_source_iq_samples_terminate;
|
|
thread_function_name = "time source iq samples";
|
|
ret = ts;
|
|
break;
|
|
}
|
|
}
|
|
|
|
time_source_common_t *c = ret;
|
|
c->type = type;
|
|
init_mutex(c);
|
|
init_cond(c);
|
|
c->terminate = terminate_function;
|
|
threadCreate(&c->thread_id, thread_function, c, thread_function_name, -1, SCHED_OAI);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void free_time_source(time_source_t *ts)
|
|
{
|
|
time_source_common_t *time_source = ts;
|
|
time_source->exit = true;
|
|
if (time_source->terminate != NULL)
|
|
time_source->terminate(time_source);
|
|
wait_thread_termination(time_source);
|
|
free(time_source);
|
|
}
|
|
|
|
void time_source_set_callback(time_source_t *ts,
|
|
void (*callback)(void *),
|
|
void *callback_data)
|
|
{
|
|
time_source_common_t *time_source = ts;
|
|
lock(time_source);
|
|
time_source->callback = callback;
|
|
time_source->callback_data = callback_data;
|
|
unlock(time_source);
|
|
}
|
|
|
|
void time_source_iq_add(time_source_t *ts,
|
|
uint64_t iq_samples_count,
|
|
uint64_t iq_samples_per_second)
|
|
{
|
|
if (ts == NULL)
|
|
return;
|
|
|
|
time_source_iq_t *time_source = ts;
|
|
|
|
if (time_source->common.type != TIME_SOURCE_IQ_SAMPLES)
|
|
return;
|
|
|
|
lock(&time_source->common);
|
|
|
|
time_source->iq_samples_count += iq_samples_count;
|
|
|
|
if (time_source->iq_samples_per_second == 0)
|
|
time_source->iq_samples_per_second = iq_samples_per_second;
|
|
AssertFatal(time_source->iq_samples_per_second == iq_samples_per_second, "unsupported change of value 'IQ samples per second'\n");
|
|
|
|
condsignal(&time_source->common);
|
|
unlock(&time_source->common);
|
|
}
|