[PATCH] osmocom-bb[master]: host/trxcon/scheduler: add basic clock counter

Harald Welte gerrit-no-reply at lists.osmocom.org
Thu Feb 22 15:32:32 UTC 2018


Review at  https://gerrit.osmocom.org/6688

host/trxcon/scheduler: add basic clock counter

The core of scheduler is a simple clock counter, which relays
on system time for now. One was a bit simplified and migrated
from OsmoBTS.

Due to system time is not an ideal clock source, the counter
should be periodically corrected by clock indications from BTS.

Change-Id: I27d85bd3e2c8bca3f876f73517027b9fe43c9825
---
M src/host/layer23/.gitignore
M src/host/trxcon/.gitignore
M src/host/trxcon/Makefile.am
M src/host/trxcon/logging.c
M src/host/trxcon/logging.h
A src/host/trxcon/sched_clck.c
A src/host/trxcon/scheduler.h
M src/host/trxcon/trxcon.c
M src/host/trxcon/trxcon.h
9 files changed, 269 insertions(+), 3 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/88/6688/1

diff --git a/src/host/layer23/.gitignore b/src/host/layer23/.gitignore
index 8fb93f7..59601be 100644
--- a/src/host/layer23/.gitignore
+++ b/src/host/layer23/.gitignore
@@ -19,7 +19,6 @@
 
 # build by-products
 *.o
-*.a
 
 # various
 *.sw?
diff --git a/src/host/trxcon/.gitignore b/src/host/trxcon/.gitignore
index d6b28ee..fe90e43 100644
--- a/src/host/trxcon/.gitignore
+++ b/src/host/trxcon/.gitignore
@@ -18,6 +18,7 @@
 
 # build by-products
 *.o
+*.a
 
 trxcon
 
diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am
index 869ed8b..de12029 100644
--- a/src/host/trxcon/Makefile.am
+++ b/src/host/trxcon/Makefile.am
@@ -28,6 +28,11 @@
 	trxcon.c \
 	$(NULL)
 
+# Scheduler
+trxcon_SOURCES += \
+	sched_clck.c \
+	$(NULL)
+
 trxcon_LDADD = \
 	$(LIBOSMOCORE_LIBS) \
 	$(LIBOSMOGSM_LIBS) \
diff --git a/src/host/trxcon/logging.c b/src/host/trxcon/logging.c
index 28e6776..3381c6a 100644
--- a/src/host/trxcon/logging.c
+++ b/src/host/trxcon/logging.c
@@ -46,6 +46,12 @@
 		.color = "\033[1;33m",
 		.enabled = 1, .loglevel = LOGL_NOTICE,
 	},
+	[DSCH] = {
+		.name = "DSCH",
+		.description = "Scheduler",
+		.color = "\033[1;36m",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
 };
 
 static const struct log_info trx_log_info = {
diff --git a/src/host/trxcon/logging.h b/src/host/trxcon/logging.h
index 4d7cea0..52afd4b 100644
--- a/src/host/trxcon/logging.h
+++ b/src/host/trxcon/logging.h
@@ -2,12 +2,13 @@
 
 #include <osmocom/core/logging.h>
 
-#define DEBUG_DEFAULT "DAPP:DL1C:DTRX"
+#define DEBUG_DEFAULT "DAPP:DL1C:DTRX:DSCH"
 
 enum {
 	DAPP,
 	DL1C,
 	DTRX,
+	DSCH,
 };
 
 int trx_log_init(const char *category_mask);
diff --git a/src/host/trxcon/sched_clck.c b/src/host/trxcon/sched_clck.c
new file mode 100644
index 0000000..31f3ef2
--- /dev/null
+++ b/src/host/trxcon/sched_clck.c
@@ -0,0 +1,209 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: clock synchronization
+ *
+ * (C) 2013 by Andreas Eversberg <jolly at eversberg.eu>
+ * (C) 2015 by Alexander Chemeris <Alexander.Chemeris at fairwaves.co>
+ * (C) 2015 by Harald Welte <laforge at gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/gsm/a5.h>
+
+#include "scheduler.h"
+#include "logging.h"
+#include "trx_if.h"
+#include "trxcon.h"
+
+#define FRAME_DURATION_uS	4615
+#define MAX_FN_SKEW		50
+#define TRX_LOSS_FRAMES	400
+
+extern struct osmo_fsm_inst *trxcon_fsm;
+
+static void sched_clck_tick(void *data)
+{
+	struct trx_sched *sched = (struct trx_sched *) data;
+	struct trx_instance *trx = (struct trx_instance *) sched->data;
+
+	struct timeval tv_now, *tv_clock;
+	int32_t elapsed;
+
+	/* Check if transceiver is still alive */
+	if (sched->fn_counter_lost++ == TRX_LOSS_FRAMES) {
+		LOGP(DSCH, LOGL_NOTICE, "No more clock from transceiver\n");
+
+		osmo_fsm_inst_dispatch(trxcon_fsm, SCH_EVENT_CLCK_LOSS, trx);
+		sched->state = SCH_CLCK_STATE_WAIT;
+
+		return;
+	}
+
+	/* Get actual / previous frame time */
+	gettimeofday(&tv_now, NULL);
+	tv_clock = &sched->clock;
+
+	elapsed = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000
+		+ (tv_now.tv_usec - tv_clock->tv_usec);
+
+	/* If someone played with clock, or if the process stalled */
+	if (elapsed > FRAME_DURATION_uS * MAX_FN_SKEW || elapsed < 0) {
+		LOGP(DSCH, LOGL_NOTICE, "PC clock skew: "
+			"elapsed uS %d\n", elapsed);
+
+		osmo_fsm_inst_dispatch(trxcon_fsm, SCH_EVENT_CLCK_LOSS, trx);
+		sched->state = SCH_CLCK_STATE_WAIT;
+
+		return;
+	}
+
+	/* Schedule next FN clock */
+	while (elapsed > FRAME_DURATION_uS / 2) {
+		tv_clock->tv_usec += FRAME_DURATION_uS;
+		elapsed -= FRAME_DURATION_uS;
+
+		if (tv_clock->tv_usec >= 1000000) {
+			tv_clock->tv_sec++;
+			tv_clock->tv_usec -= 1000000;
+		}
+
+		sched->fn_counter_proc = (sched->fn_counter_proc + 1)
+			% GSM_HYPERFRAME;
+
+		/* Call frame callback */
+		if (sched->clock_cb)
+			sched->clock_cb(sched);
+	}
+
+	osmo_timer_schedule(&sched->clock_timer, 0,
+		FRAME_DURATION_uS - elapsed);
+}
+
+static void sched_clck_correct(struct trx_sched *sched,
+	struct timeval *tv_now, uint32_t fn)
+{
+	sched->fn_counter_proc = fn;
+
+	/* Call frame callback */
+	if (sched->clock_cb)
+		sched->clock_cb(sched);
+
+	/* Schedule first FN clock */
+	memcpy(&sched->clock, tv_now, sizeof(struct timeval));
+	memset(&sched->clock_timer, 0, sizeof(sched->clock_timer));
+
+	sched->clock_timer.cb = sched_clck_tick;
+	sched->clock_timer.data = sched;
+	osmo_timer_schedule(&sched->clock_timer, 0, FRAME_DURATION_uS);
+}
+
+int sched_clck_handle(struct trx_sched *sched, uint32_t fn)
+{
+	struct trx_instance *trx = (struct trx_instance *) sched->data;
+	struct timeval tv_now, *tv_clock;
+	int32_t elapsed, elapsed_fn;
+
+	/* Reset lost counter */
+	sched->fn_counter_lost = 0;
+
+	/* Get actual / previous frame time */
+	gettimeofday(&tv_now, NULL);
+	tv_clock = &sched->clock;
+
+	/* If this is the first CLCK IND */
+	if (sched->state == SCH_CLCK_STATE_WAIT) {
+		sched_clck_correct(sched, &tv_now, fn);
+
+		LOGP(DSCH, LOGL_NOTICE, "Initial clock received: fn=%u\n", fn);
+		osmo_fsm_inst_dispatch(trxcon_fsm, SCH_EVENT_CLCK_IND, trx);
+		sched->state = SCH_CLCK_STATE_OK;
+
+		return 0;
+	}
+
+	osmo_timer_del(&sched->clock_timer);
+
+	/* Calculate elapsed time / frames since last processed fn */
+	elapsed = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000
+		+ (tv_now.tv_usec - tv_clock->tv_usec);
+	elapsed_fn = (fn + GSM_HYPERFRAME - sched->fn_counter_proc)
+		% GSM_HYPERFRAME;
+
+	if (elapsed_fn >= 135774)
+		elapsed_fn -= GSM_HYPERFRAME;
+
+	/* Check for max clock skew */
+	if (elapsed_fn > MAX_FN_SKEW || elapsed_fn < -MAX_FN_SKEW) {
+		LOGP(DSCH, LOGL_NOTICE, "GSM clock skew: old fn=%u, "
+			"new fn=%u\n", sched->fn_counter_proc, fn);
+
+		sched_clck_correct(sched, &tv_now, fn);
+		return 0;
+	}
+
+	LOGP(DSCH, LOGL_INFO, "GSM clock jitter: %d\n",
+		elapsed_fn * FRAME_DURATION_uS - elapsed);
+
+	/* Too many frames have been processed already */
+	if (elapsed_fn < 0) {
+		/**
+		 * Set clock to the time or last FN should
+		 * have been transmitted
+		 */
+		tv_clock->tv_sec = tv_now.tv_sec;
+		tv_clock->tv_usec = tv_now.tv_usec +
+			(0 - elapsed_fn) * FRAME_DURATION_uS;
+
+		if (tv_clock->tv_usec >= 1000000) {
+			tv_clock->tv_sec++;
+			tv_clock->tv_usec -= 1000000;
+		}
+
+		/* Set time to the time our next FN has to be transmitted */
+		osmo_timer_schedule(&sched->clock_timer, 0,
+			FRAME_DURATION_uS * (1 - elapsed_fn));
+
+		return 0;
+	}
+
+	/* Transmit what we still need to transmit */
+	while (fn != sched->fn_counter_proc) {
+		sched->fn_counter_proc = (sched->fn_counter_proc + 1)
+			% GSM_HYPERFRAME;
+
+		/* Call frame callback */
+		if (sched->clock_cb)
+			sched->clock_cb(sched);
+	}
+
+	/* Schedule next FN to be transmitted */
+	memcpy(tv_clock, &tv_now, sizeof(struct timeval));
+	osmo_timer_schedule(&sched->clock_timer, 0, FRAME_DURATION_uS);
+
+	return 0;
+}
diff --git a/src/host/trxcon/scheduler.h b/src/host/trxcon/scheduler.h
new file mode 100644
index 0000000..0783e40
--- /dev/null
+++ b/src/host/trxcon/scheduler.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <stdint.h>
+#include <time.h>
+
+#include <osmocom/core/timer.h>
+
+#define GSM_SUPERFRAME		(26 * 51)
+#define GSM_HYPERFRAME		(2048 * GSM_SUPERFRAME)
+
+enum tdma_sched_clck_state {
+	SCH_CLCK_STATE_WAIT,
+	SCH_CLCK_STATE_OK,
+};
+
+/* Forward structure declaration */
+struct trx_sched;
+
+/*! \brief One scheduler instance */
+struct trx_sched {
+	/*! \brief Clock state */
+	uint8_t state;
+	/*! \brief Local clock source */
+	struct timeval clock;
+	/*! \brief Count of processed frames */
+	uint32_t fn_counter_proc;
+	/*! \brief Frame counter */
+	uint32_t fn_counter_lost;
+	/*! \brief Frame callback timer */
+	struct osmo_timer_list clock_timer;
+	/*! \brief Frame callback */
+	void (*clock_cb)(struct trx_sched *sched);
+	/*! \brief Private data (e.g. pointer to trx instance) */
+	void *data;
+};
+
+int sched_clck_handle(struct trx_sched *sched, uint32_t fn);
diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c
index a90d038..ace2f79 100644
--- a/src/host/trxcon/trxcon.c
+++ b/src/host/trxcon/trxcon.c
@@ -104,6 +104,8 @@
 		break;
 	case TRX_EVENT_RSP_ERROR:
 	case TRX_EVENT_OFFLINE:
+	case SCH_EVENT_CLCK_IND:
+	case SCH_EVENT_CLCK_LOSS:
 		/* TODO: notify L2 & L3 about that */
 		break;
 	default:
@@ -125,7 +127,9 @@
 			GEN_MASK(L1CTL_EVENT_RESET_REQ) |
 			GEN_MASK(TRX_EVENT_RESET_IND) |
 			GEN_MASK(TRX_EVENT_RSP_ERROR) |
-			GEN_MASK(TRX_EVENT_OFFLINE)),
+			GEN_MASK(TRX_EVENT_OFFLINE) |
+			GEN_MASK(SCH_EVENT_CLCK_IND) |
+			GEN_MASK(SCH_EVENT_CLCK_LOSS)),
 		.out_state_mask = GEN_MASK(TRXCON_STATE_IDLE),
 		.name = "MANAGED",
 		.action = trxcon_fsm_managed_action,
diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h
index 9535578..c266eae 100644
--- a/src/host/trxcon/trxcon.h
+++ b/src/host/trxcon/trxcon.h
@@ -18,4 +18,8 @@
 	TRX_EVENT_RESET_IND,
 	TRX_EVENT_RSP_ERROR,
 	TRX_EVENT_OFFLINE,
+
+	/* Scheduler specific events */
+	SCH_EVENT_CLCK_IND,
+	SCH_EVENT_CLCK_LOSS,
 };

-- 
To view, visit https://gerrit.osmocom.org/6688
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I27d85bd3e2c8bca3f876f73517027b9fe43c9825
Gerrit-PatchSet: 1
Gerrit-Project: osmocom-bb
Gerrit-Branch: master
Gerrit-Owner: Harald Welte <laforge at gnumonks.org>


More information about the gerrit-log mailing list