summaryrefslogtreecommitdiffstats
path: root/src/host/virt_phy
diff options
context:
space:
mode:
authorSebastian Stumpf <sebastian.stumpf87@googlemail.com>2017-02-25 16:17:12 +0100
committerHarald Welte <laforge@gnumonks.org>2017-07-12 23:26:26 +0200
commit63f3ce248295d945923b37a2add8165297854a76 (patch)
treef89f4a89ede2756ab4d9583304704aeea271c6c0 /src/host/virt_phy
parent0a68f6884c052b9f8d9f8bda0316c79405436851 (diff)
VIRT-PHY: Uplink scheduler implementation.
Implemented simple scheduler depending on frame number in downlink. It will be executed each time we receive a msg on downlink and send out all scheduled uplink msgs with a sched_fn smaller than the one of this received downlink msg. Further refactored l1ctl_sap by extracting rach and fbsb logic and putting it to own files virt_prim_fbsb.c and virt_prim_rach.c Added simple states to the ms layer 1 model, indicating if the ms is currently searching for bts, syncing to or camping on a bts. Downlink will be handled differently dependent of the state. Change-Id: I8937b1d6568f5d3750bbdc5d77fa283074d5365e
Diffstat (limited to 'src/host/virt_phy')
-rw-r--r--src/host/virt_phy/Makefile.am4
-rw-r--r--src/host/virt_phy/include/virtphy/gsmtapl1_if.h5
-rw-r--r--src/host/virt_phy/include/virtphy/l1ctl_sap.h2
-rw-r--r--src/host/virt_phy/include/virtphy/virt_l1_model.h19
-rw-r--r--src/host/virt_phy/include/virtphy/virt_l1_sched.h32
-rw-r--r--src/host/virt_phy/src/Makefile.am7
-rw-r--r--src/host/virt_phy/src/gsmtapl1_if.c29
-rw-r--r--src/host/virt_phy/src/l1ctl_sap.c159
-rw-r--r--src/host/virt_phy/src/virt_l1_sched_simple.c135
-rw-r--r--src/host/virt_phy/src/virt_prim_fbsb.c113
-rw-r--r--src/host/virt_phy/src/virt_prim_rach.c140
-rw-r--r--src/host/virt_phy/src/virtphy.c2
12 files changed, 492 insertions, 155 deletions
diff --git a/src/host/virt_phy/Makefile.am b/src/host/virt_phy/Makefile.am
index 515d51b5..57b4571a 100644
--- a/src/host/virt_phy/Makefile.am
+++ b/src/host/virt_phy/Makefile.am
@@ -1,2 +1,4 @@
SUBDIRS = src
-dist_doc_DATA = README \ No newline at end of file
+dist_doc_DATA = README
+
+CFLAGS = "-g -O0" \ No newline at end of file
diff --git a/src/host/virt_phy/include/virtphy/gsmtapl1_if.h b/src/host/virt_phy/include/virtphy/gsmtapl1_if.h
index c5115608..34cd9c82 100644
--- a/src/host/virt_phy/include/virtphy/gsmtapl1_if.h
+++ b/src/host/virt_phy/include/virtphy/gsmtapl1_if.h
@@ -10,8 +10,7 @@ void gsmtapl1_init(struct l1_model_ms *model);
void gsmtapl1_rx_from_virt_um_inst_cb(struct virt_um_inst *vui,
struct msgb *msg);
void gsmtapl1_rx_from_virt_um(struct msgb *msg);
-void gsmtapl1_tx_to_virt_um_inst(struct virt_um_inst *vui, uint32_t fn,
- struct msgb *msg);
-void gsmtapl1_tx_to_virt_um(uint32_t fn, struct msgb *msg);
+void gsmtapl1_tx_to_virt_um_inst(struct virt_um_inst *vui, struct msgb *msg);
+void gsmtapl1_tx_to_virt_um(struct msgb *msg);
void chantype_gsmtap2rsl(uint8_t gsmtap_chantype, uint8_t *rsl_chantype,
uint8_t *link_id);
diff --git a/src/host/virt_phy/include/virtphy/l1ctl_sap.h b/src/host/virt_phy/include/virtphy/l1ctl_sap.h
index 902eb177..c11c5fe4 100644
--- a/src/host/virt_phy/include/virtphy/l1ctl_sap.h
+++ b/src/host/virt_phy/include/virtphy/l1ctl_sap.h
@@ -19,6 +19,8 @@
#define LID_DEDIC 0x00
void l1ctl_sap_init(struct l1_model_ms *model);
+void prim_rach_init(struct l1_model_ms *model);
+void prim_fbsb_init(struct l1_model_ms *model);
void l1ctl_sap_tx_to_l23_inst(struct l1ctl_sock_inst *lsi, struct msgb *msg);
void l1ctl_sap_tx_to_l23(struct msgb *msg);
void l1ctl_sap_rx_from_l23_inst_cb(struct l1ctl_sock_inst *lsi,
diff --git a/src/host/virt_phy/include/virtphy/virt_l1_model.h b/src/host/virt_phy/include/virtphy/virt_l1_model.h
index 561025e0..69115f26 100644
--- a/src/host/virt_phy/include/virtphy/virt_l1_model.h
+++ b/src/host/virt_phy/include/virtphy/virt_l1_model.h
@@ -2,10 +2,19 @@
#include <virtphy/virtual_um.h>
#include <virtphy/l1ctl_sock.h>
+#include <osmocom/gsm/gsm_utils.h>
#define L1S_NUM_NEIGH_CELL 6
#define A5_KEY_LEN 8
+enum ms_state {
+ MS_STATE_IDLE_SEARCHING = 0,
+ MS_STATE_IDLE_SYNCING,
+ MS_STATE_IDLE_CAMPING,
+ MS_STATE_DEDICATED,
+};
+
+
struct l1_model_ms {
struct l1ctl_sock_inst *lsi;
struct virt_um_inst *vui;
@@ -38,7 +47,10 @@ struct crypto_info_ms {
struct l1_state_ms {
- uint8_t camping; // are we currently camping on a cell
+ struct gsm_time downlink_time; /* current GSM time received on downlink */
+ struct gsm_time current_time; /* GSM time used internally for scheduling */
+
+ uint8_t state; // the ms state like in ms_state
/* the cell on which we are camping right now */
struct l1_cell_info serving_cell;
@@ -60,6 +72,11 @@ struct l1_state_ms {
uint8_t tsc; // training sequence code (ununsed in virtual um)
uint8_t h; // hopping enabled flag (ununsed in virtual um)
} dedicated;
+
+ /* fbsb state */
+ struct {
+ uint32_t arfcn;
+ } fbsb;
};
struct l1_model_ms *l1_model_ms_init(void *ctx);
diff --git a/src/host/virt_phy/include/virtphy/virt_l1_sched.h b/src/host/virt_phy/include/virtphy/virt_l1_sched.h
new file mode 100644
index 00000000..a68c58d6
--- /dev/null
+++ b/src/host/virt_phy/include/virtphy/virt_l1_sched.h
@@ -0,0 +1,32 @@
+#pragma once
+#include <osmocom/core/msgb.h>
+#include <virtphy/virt_l1_model.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <virtphy/virt_l1_sched.h>
+
+typedef void virt_l1_sched_cb(struct msgb * msg);
+
+/* bucket containing items to be executed for a specific mframe number */
+struct virt_l1_sched_mframe_item {
+ struct llist_head mframe_item_entry;
+ struct llist_head tdma_item_list; /* list of tdma sched items */
+ uint32_t fn; /* frame number of execution */
+};
+
+/* item to be be executed for a specific tdma timeslot of a framenumber */
+struct virt_l1_sched_tdma_item {
+ struct llist_head tdma_item_entry;
+ struct msgb * msg; /* the msg to be handled */
+ uint8_t ts; /* tdma timeslot of execution */
+ virt_l1_sched_cb * handler_cb; /* handler callback */
+};
+
+void virt_l1_sched_init(struct l1_model_ms * model);
+int virt_l1_sched_start(struct gsm_time time);
+int virt_l1_sched_restart(struct gsm_time time);
+void virt_l1_sched_sync_time(struct gsm_time time, uint8_t hard_reset);
+void virt_l1_sched_stop();
+void virt_l1_sched_execute(uint32_t fn);
+void virt_l1_sched_schedule(struct msgb * msg, uint32_t fn, uint8_t ts,
+ virt_l1_sched_cb * handler_cb);
diff --git a/src/host/virt_phy/src/Makefile.am b/src/host/virt_phy/src/Makefile.am
index 85085092..fd8d5d97 100644
--- a/src/host/virt_phy/src/Makefile.am
+++ b/src/host/virt_phy/src/Makefile.am
@@ -1,9 +1,12 @@
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS)
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_srcdir)/../layer23/include
+CFLAGS = "-g -O0"
+
sbin_PROGRAMS = virtphy
-virtphy_SOURCES = virtphy.c l1ctl_sock.c l1ctl_sap.c gsmtapl1_if.c logging.c virt_l1_model.c shared/virtual_um.c shared/osmo_mcast_sock.c
-virtphy_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS)
+virtphy_SOURCES = virtphy.c l1ctl_sock.c gsmtapl1_if.c l1ctl_sap.c virt_prim_fbsb.c virt_prim_rach.c virt_l1_sched_simple.c logging.c virt_l1_model.c shared/virtual_um.c shared/osmo_mcast_sock.c
+virtphy_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS)
+virtphy_LDFLAGS = -pthread
# debug output
all:
diff --git a/src/host/virt_phy/src/gsmtapl1_if.c b/src/host/virt_phy/src/gsmtapl1_if.c
index ebfb5b62..3762cdc2 100644
--- a/src/host/virt_phy/src/gsmtapl1_if.c
+++ b/src/host/virt_phy/src/gsmtapl1_if.c
@@ -23,6 +23,7 @@
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/rsl.h>
+#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/core/msgb.h>
#include <stddef.h>
@@ -35,6 +36,7 @@
#include <virtphy/l1ctl_sap.h>
#include <virtphy/gsmtapl1_if.h>
#include <virtphy/logging.h>
+#include <virtphy/virt_l1_sched.h>
static struct l1_model_ms *l1_model_ms = NULL;
@@ -88,13 +90,13 @@ void gsmtapl1_init(struct l1_model_ms *model)
/**
* Replace l11 header of given msgb by a gsmtap header and send it over the virt um.
*/
-void gsmtapl1_tx_to_virt_um_inst(struct virt_um_inst *vui, uint32_t fn,
- struct msgb *msg)
+void gsmtapl1_tx_to_virt_um_inst(struct virt_um_inst *vui, struct msgb *msg)
{
struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *)l1h->data;
struct gsmtap_hdr *gh;
struct msgb *outmsg; // msg to send with gsmtap header prepended
+ uint32_t fn = gsm_gsmtime2fn(&l1_model_ms->state->downlink_time);
uint16_t arfcn = l1_model_ms->state->serving_cell.arfcn; // arfcn of the cell we currently camp on
uint8_t signal_dbm = 63; // signal strength, 63 is best
uint8_t snr = 63; // signal noise ratio, 63 is best
@@ -138,9 +140,9 @@ void gsmtapl1_tx_to_virt_um_inst(struct virt_um_inst *vui, uint32_t fn,
/**
* @see void gsmtapl1_tx_to_virt_um(struct virt_um_inst *vui, uint32_t fn, struct msgb *msg).
*/
-void gsmtapl1_tx_to_virt_um(uint32_t fn, struct msgb *msg)
+void gsmtapl1_tx_to_virt_um(struct msgb *msg)
{
- gsmtapl1_tx_to_virt_um_inst(l1_model_ms->vui, fn, msg);
+ gsmtapl1_tx_to_virt_um_inst(l1_model_ms->vui, msg);
}
/**
@@ -152,19 +154,25 @@ void gsmtapl1_rx_from_virt_um_inst_cb(struct virt_um_inst *vui,
if (!msg) {
return;
}
- // we assume we only receive msgs if we actually camp on a cell
- if (!l1_model_ms->state->camping) {
+ // we do not forward messages to l23 if we are in network search state
+ if (l1_model_ms->state->state == MS_STATE_IDLE_SEARCHING) {
talloc_free(msg);
return;
}
+ // forward msg to fbsb sync routine if we are in sync state
+ if (l1_model_ms->state->state == MS_STATE_IDLE_SYNCING) {
+ prim_fbsb_sync(msg);
+ return;
+ }
+
struct gsmtap_hdr *gh = msgb_l1(msg);
struct msgb *l1ctl_msg = NULL;
struct l1ctl_info_dl *l1dl;
struct l1ctl_traffic_ind * l1ti;
struct l1ctl_data_ind * l1di;
uint32_t fn = ntohl(gh->frame_number); // frame number of the rcv msg
- uint16_t arfcn = ntohs(gh->arfcn); // arfcn of the cell we currently camp on
+ uint16_t arfcn = ntohs(gh->arfcn); // arfcn of the received msg
uint8_t gsmtap_chantype = gh->sub_type; // gsmtap channel type
uint8_t signal_dbm = gh->signal_dbm; // signal strength, 63 is best
uint8_t snr = gh->snr_db; // signal noise ratio, 63 is best
@@ -179,18 +187,23 @@ void gsmtapl1_rx_from_virt_um_inst_cb(struct virt_um_inst *vui,
// see GSM 8.58 -> 9.3.1 for channel number encoding
chan_nr = rsl_enc_chan_nr(rsl_chantype, subslot, timeslot);
+ gsm_fn2gsmtime(&l1_model_ms->state->downlink_time, fn);
+ virt_l1_sched_sync_time(l1_model_ms->state->downlink_time, 0);
+ virt_l1_sched_execute(fn);
+
DEBUGP(DVIRPHY,
"Receiving gsmtap msg from virt um - (arfcn=%u, framenumber=%u, type=%s, subtype=%s, timeslot=%u, subslot=%u, rsl_chan_type=0x%2x, link_id=0x%2x, chan_nr=0x%2x)\n",
arfcn, fn, get_value_string(gsmtap_types, gh->type),
get_value_string(gsmtap_channels, gsmtap_chantype),
timeslot, subslot, rsl_chantype, link_id, chan_nr);
- // generally ignore all messages coming from another arfcn than the synced one
+ // generally ignore all messages coming from another arfcn than the camped one
if (l1_model_ms->state->serving_cell.arfcn != (arfcn & GSMTAP_ARFCN_MASK)) {
LOGP(DVIRPHY, LOGL_NOTICE,
"Ignoring gsmtap msg from virt um - msg arfcn=%d not equal synced arfcn=%d!\n", arfcn & GSMTAP_ARFCN_MASK, l1_model_ms->state->serving_cell.arfcn);
goto nomessage;
}
+
// generally ignore all uplink messages received
if (arfcn & GSMTAP_ARFCN_F_UPLINK) {
LOGP(DVIRPHY, LOGL_NOTICE,
diff --git a/src/host/virt_phy/src/l1ctl_sap.c b/src/host/virt_phy/src/l1ctl_sap.c
index b89d963c..6e42e5d9 100644
--- a/src/host/virt_phy/src/l1ctl_sap.c
+++ b/src/host/virt_phy/src/l1ctl_sap.c
@@ -17,6 +17,7 @@
#include <virtphy/l1ctl_sap.h>
#include <virtphy/gsmtapl1_if.h>
#include <virtphy/logging.h>
+#include <virtphy/virt_l1_sched.h>
static struct l1_model_ms *l1_model_ms = NULL;
@@ -38,6 +39,8 @@ static void l1_model_tch_mode_set(uint8_t tch_mode)
void l1ctl_sap_init(struct l1_model_ms *model)
{
l1_model_ms = model;
+ prim_rach_init(model);
+ prim_fbsb_init(model);
}
/**
@@ -193,11 +196,9 @@ void l1ctl_sap_handler(struct msgb *msg)
break;
case L1CTL_RACH_REQ:
l1ctl_rx_rach_req(msg);
- // msg is freed by rx routine
goto exit_nofree;
case L1CTL_DATA_REQ:
l1ctl_rx_data_req(msg);
- /* we have to keep the msgb, not free it! */
goto exit_nofree;
case L1CTL_PM_REQ:
l1ctl_rx_pm_req(msg);
@@ -216,7 +217,6 @@ void l1ctl_sap_handler(struct msgb *msg)
break;
case L1CTL_TRAFFIC_REQ:
l1ctl_rx_traffic_req(msg);
- /* we have to keep the msgb, not free it! */
goto exit_nofree;
case L1CTL_SIM_REQ:
l1ctl_rx_sim_req(msg);
@@ -224,51 +224,16 @@ void l1ctl_sap_handler(struct msgb *msg)
}
exit_msgbfree: msgb_free(msg);
- exit_nofree: return;
+ exit_nofree: return; /* msg is scheduled for uplink and mustn't be freed here */
}
/***************************************************************
* L1CTL RX ROUTINES *******************************************
+ * For more routines check the respective handler classes ******
+ * like virt_prim_rach.c ***************************************
***************************************************************/
/**
- * @brief Handler for received L1CTL_FBSB_REQ from L23.
- *
- * -- frequency burst synchronisation burst request --
- *
- * @param [in] msg the received message.
- *
- * Transmit frequency control and synchronisation bursts on FCCH and SCH to calibrate transceiver and search for base stations.
- * Sync to a given arfcn.
- *
- * Note: ms will start receiving msgs on virtual um only after this req was received.
- * Note: virt bts does not broadcast freq and sync bursts.
- *
- * TODO: Could be used to bind/connect to different virtual_bts sockets with a arfcn-socket mapping.
- * TODO: Check flags if this is a sync or freq request and handle it accordingly.
- */
-void l1ctl_rx_fbsb_req(struct msgb *msg)
-{
- struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
- struct l1ctl_fbsb_req *sync_req = (struct l1ctl_fbsb_req *)l1h->data;
-
- DEBUGP(DL1C,
- "Received and handled from l23 - L1CTL_FBSB_REQ (arfcn=%u, flags=0x%x)\n",
- ntohs(sync_req->band_arfcn), sync_req->flags);
-
- l1_model_ms->state->camping = 1;
- l1_model_ms->state->serving_cell.arfcn = ntohs(sync_req->band_arfcn); // freq req
-
- // not needed in virt um
- l1_model_ms->state->serving_cell.ccch_mode = sync_req->ccch_mode; // sync req
- l1_model_ms->state->serving_cell.fn_offset = 0; // sync req
- l1_model_ms->state->serving_cell.bsic = 0; // sync req
- l1_model_ms->state->serving_cell.time_alignment = 0; // sync req
-
- l1ctl_tx_fbsb_conf(0, l1_model_ms->state->serving_cell.arfcn);
-}
-
-/**
* @brief Handler for received L1CTL_DM_EST_REQ from L23.
*
* -- dedicated mode established request --
@@ -410,47 +375,6 @@ void l1ctl_rx_param_req(struct msgb *msg)
}
/**
- * @brief Handler for received L1CTL_RACH_REQ from L23.
- *
- * -- random access channel request --
- *
- * @param [in] msg the received message.
- *
- * Transmit RACH request on RACH. Refer to 04.08 - 9.1.8 - Channel request.
- *
- */
-void l1ctl_rx_rach_req(struct msgb *msg)
-{
- struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
- struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *)l1h->data;
- struct l1ctl_rach_req *rach_req = (struct l1ctl_rach_req *)ul->payload;
- // FIXME: proper frame number
- uint32_t fn_sched = 42;
-
- DEBUGP(DL1C,
- "Received and handled from l23 - L1CTL_RACH_REQ (ra=0x%02x, offset=%d combined=%d)\n",
- rach_req->ra, ntohs(rach_req->offset),
- rach_req->combined);
-
- // for the rach channel request, there is no layer2 header, but only the one bit ra content to submit
- // replace l1ctl_rach_req with ra data that rly shall be submitted
- // ra on peer side is decoded as uint16_t, but we do not use the 11bit option and thus 8bits must be sufficient
- msg->l2h = msgb_put(msg, sizeof(uint8_t));
- *msg->l2h = rach_req->ra;
-
- // chan_nr is not specified in info_ul for rach request coming from l23, but needed in gsmtapl1_tx_to_virt_um()
- ul->chan_nr = rsl_enc_chan_nr(RSL_CHAN_RACH, 0, 0);
- ul->link_id = LID_DEDIC;
-
- // send rach over virt um
- gsmtapl1_tx_to_virt_um(fn_sched, msg);
-
- // send confirm to layer23
- l1ctl_tx_rach_conf(fn_sched, l1_model_ms->state->serving_cell.arfcn);
-
-}
-
-/**
* @brief Handler for received L1CTL_DATA_REQ from L23.
*
* -- data request --
@@ -466,8 +390,8 @@ void l1ctl_rx_data_req(struct msgb *msg)
struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *)l1h->data;
struct l1ctl_data_ind *data_ind = (struct l1ctl_data_ind *)ul->payload;
- // FIXME: proper frame number
- uint32_t fn_sched = 42;
+ // TODO: calc the scheduled fn
+ uint32_t fn_sched = l1_model_ms->state->downlink_time.fn;
DEBUGP(DL1C,
"Received and handled from l23 - L1CTL_DATA_REQ (link_id=0x%02x, ul=%p, ul->payload=%p, data_ind=%p, data_ind->data=%p l3h=%p)\n",
@@ -476,8 +400,8 @@ void l1ctl_rx_data_req(struct msgb *msg)
msg->l2h = data_ind->data;
- // send msg over virt um
- gsmtapl1_tx_to_virt_um(fn_sched, msg);
+ // TODO: append to scheduler queue instead of sending here directly
+ gsmtapl1_tx_to_virt_um(msg);
// send confirm to layer23
msg = l1ctl_create_l2_msg(L1CTL_DATA_CONF, fn_sched, 0, 0);
@@ -560,11 +484,12 @@ void l1ctl_rx_reset_req(struct msgb *msg)
case L1CTL_RES_T_FULL:
DEBUGP(DL1C,
"Received and handled from l23 - L1CTL_RESET_REQ (type=FULL)\n");
- l1_model_ms->state->camping = 0;
- // TODO: check if we also need to reset the dedicated channel state
+ l1_model_ms->state->state = MS_STATE_IDLE_SEARCHING;
+ virt_l1_sched_stop();
l1ctl_tx_reset(L1CTL_RESET_CONF, reset_req->type);
break;
case L1CTL_RES_T_SCHED:
+ virt_l1_sched_restart(l1_model_ms->state->downlink_time);
DEBUGP(DL1C,
"Received and handled from l23 - L1CTL_RESET_REQ (type=SCHED)\n");
l1ctl_tx_reset(L1CTL_RESET_CONF, reset_req->type);
@@ -673,14 +598,15 @@ void l1ctl_rx_traffic_req(struct msgb *msg)
struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *)l1h->data;
struct l1ctl_traffic_req *tr = (struct l1ctl_traffic_req *)ul->payload;
- uint32_t fn_sched = 42;
+ // TODO: calc the scheduled fn
+ uint32_t fn_sched = l1_model_ms->state->downlink_time.fn;
DEBUGP(DL1C, "Received and handled from l23 - L1CTL_TRAFFIC_REQ\n");
msg->l2h = tr->data;
- // send msg over virt um
- gsmtapl1_tx_to_virt_um(fn_sched, msg);
+ // TODO: append to scheduler queue instead of sending here directly
+ gsmtapl1_tx_to_virt_um(msg);
// send confirm to layer23
msg = l1ctl_create_l2_msg(L1CTL_TRAFFIC_CONF, fn_sched, 0, 0);
@@ -719,6 +645,8 @@ void l1ctl_rx_sim_req(struct msgb *msg)
/***************************************************************
* L1CTL TX ROUTINES *******************************************
+ * For more routines check the respective handler classes ******
+ * like virt_prim_rach.c ***************************************
***************************************************************/
/**
@@ -742,23 +670,6 @@ void l1ctl_tx_reset(uint8_t msg_type, uint8_t reset_type)
}
/**
- * @brief Transmit L1CTL_RESET_IND or L1CTL_RESET_CONF to layer 23.
- *
- * -- reset indication / confirm --
- *
- * @param [in] msg_type L1CTL primitive message type.
- * @param [in] reset_type reset type (full, boot or just scheduler reset).
- */
-void l1ctl_tx_rach_conf(uint32_t fn, uint16_t arfcn)
-{
- struct msgb * msg = l1ctl_create_l2_msg(L1CTL_RACH_CONF, fn, 0, arfcn);
-
- DEBUGP(DL1C, "Sending to l23 - %s (fn: %u, arfcn: %u)\n",
- getL1ctlPrimName(L1CTL_RACH_CONF), fn, arfcn);
- l1ctl_sap_tx_to_l23(msg);
-}
-
-/**
* @brief Transmit L1CTL msg of a given type to layer 23.
*
* @param [in] msg_type L1CTL primitive message type.
@@ -771,38 +682,6 @@ void l1ctl_tx_msg(uint8_t msg_type)
}
/**
- * @brief Transmit L1CTL_FBSB_CONF to l23.
- *
- * -- frequency burst synchronisation burst confirm --
- *
- * @param [in] res 0 -> success, 255 -> error.
- * @param [in] arfcn the arfcn we are synced to.
- *
- * No calculation needed for virtual pyh -> uses dummy values for a good link quality.
- */
-void l1ctl_tx_fbsb_conf(uint8_t res, uint16_t arfcn)
-{
- struct msgb *msg;
- struct l1ctl_fbsb_conf *resp;
- uint32_t fn = 0; // 0 should be okay here
- uint16_t snr = 40; // signal noise ratio > 40db is best signal (unused in virt)
- int16_t initial_freq_err = 0; // 0 means no error (unused in virt)
- uint8_t bsic = 0; // bsci can be read from sync burst (unused in virt)
-
- msg = l1ctl_create_l2_msg(L1CTL_FBSB_CONF, fn, snr, arfcn);
-
- resp = (struct l1ctl_fbsb_conf *)msgb_put(msg, sizeof(*resp));
- resp->initial_freq_err = htons(initial_freq_err);
- resp->result = res;
- resp->bsic = bsic;
-
- DEBUGP(DL1C, "Sending to l23 - %s (res: %u)\n",
- getL1ctlPrimName(L1CTL_FBSB_CONF), res);
-
- l1ctl_sap_tx_to_l23(msg);
-}
-
-/**
* @brief Transmit L1CTL_CCCH_MODE_CONF to layer 23.
*
* -- common control channel mode confirm --
diff --git a/src/host/virt_phy/src/virt_l1_sched_simple.c b/src/host/virt_phy/src/virt_l1_sched_simple.c
new file mode 100644
index 00000000..8df88ec4
--- /dev/null
+++ b/src/host/virt_phy/src/virt_l1_sched_simple.c
@@ -0,0 +1,135 @@
+#include <virtphy/virt_l1_sched.h>
+#include <osmocom/core/linuxlist.h>
+#include <virtphy/virt_l1_model.h>
+#include <virtphy/logging.h>
+#include <time.h>
+#include <talloc.h>
+
+static struct l1_model_ms *l1_model_ms = NULL;
+
+static LLIST_HEAD(mframe_item_list);
+
+/**
+ * @brief Initialize schedulers data structures.
+ */
+void virt_l1_sched_init(struct l1_model_ms * model)
+{
+ l1_model_ms = model;
+}
+
+/**
+ * @brief Clear scheduler queue and completely restart scheduler.
+ */
+int virt_l1_sched_restart(struct gsm_time time)
+{
+ virt_l1_sched_stop();
+ return virt_l1_sched_start(time);
+}
+
+/**
+ * @brief Start scheduler thread based on current gsm time from model
+ */
+int virt_l1_sched_start(struct gsm_time time)
+{
+ virt_l1_sched_sync_time(time, 1);
+ return 0;
+}
+
+/**
+ * @brief Sync scheduler with given time.
+ */
+void virt_l1_sched_sync_time(struct gsm_time time, uint8_t hard_reset)
+{
+ l1_model_ms->state->current_time = time;
+}
+
+/**
+ * @brief Stop the scheduler thread and cleanup mframe items queue
+ */
+void virt_l1_sched_stop()
+{
+ struct virt_l1_sched_mframe_item *mi_next, *mi_tmp;
+
+ /* Empty tdma and mframe sched items lists */
+ llist_for_each_entry_safe(mi_next, mi_tmp, &mframe_item_list, mframe_item_entry)
+ {
+ struct virt_l1_sched_tdma_item *ti_next, *ti_tmp;
+ llist_for_each_entry_safe(ti_next, ti_tmp, &mi_next->tdma_item_list, tdma_item_entry)
+ {
+ talloc_free(ti_next->msg);
+ llist_del(&ti_next->tdma_item_entry);
+ }
+ llist_del(&mi_next->mframe_item_entry);
+ talloc_free(mi_next);
+ }
+}
+
+/**
+ * @brief Handle all pending scheduled items for the current frame number.
+ */
+void virt_l1_sched_execute(uint32_t fn)
+{
+ struct virt_l1_sched_mframe_item *mi_next, *mi_tmp;
+ // FIXME: change of hyperframe and thus restarting fn at 0 may cause messages in the queue that are never handled
+ llist_for_each_entry_safe(mi_next, mi_tmp, &mframe_item_list, mframe_item_entry)
+ {
+ if (mi_next->fn <= fn) {
+ struct virt_l1_sched_tdma_item *ti_next, *ti_tmp;
+ // run through all scheduled tdma sched items for that frame number
+ llist_for_each_entry_safe(ti_next, ti_tmp, &mi_next->tdma_item_list, tdma_item_entry)
+ {
+ // exec tdma sched item's handler callback
+ // TODO: we do not have a tdma scheduler currently and execute alle scheduled tdma items here at once
+ ti_next->handler_cb(ti_next->msg);
+ // remove handled tdma sched item
+ llist_del(&ti_next->tdma_item_entry);
+ }
+ // remove handled mframe sched item
+ llist_del(&mi_next->mframe_item_entry);
+ talloc_free(mi_next);
+ } else if (mi_next->fn > fn) {
+ /* break the loop as our list is ordered */
+ break;
+ }
+ }
+}
+
+/**
+ * @brief Schedule a msg to the given framenumber and timeslot.
+ */
+void virt_l1_sched_schedule(struct msgb * msg, uint32_t fn, uint8_t ts,
+ virt_l1_sched_cb * handler_cb)
+{
+ struct virt_l1_sched_mframe_item *mi_next = NULL, *mi_tmp = NULL,
+ *mi_fn = NULL;
+ struct virt_l1_sched_tdma_item *ti_new = NULL;
+
+ llist_for_each_entry_safe(mi_next, mi_tmp, &mframe_item_list, mframe_item_entry)
+ {
+ if (mi_next->fn == fn) {
+ mi_fn = mi_next;
+ break;
+ } else if (mi_next->fn > fn) {
+ break;
+ }
+ }
+ if (!mi_fn) {
+ // list did not contain mframe item with needed fn
+ mi_fn = talloc_zero(NULL, struct virt_l1_sched_mframe_item);
+ mi_fn->fn = fn;
+ // need to manually init the struct content.... no so happy
+ mi_fn->tdma_item_list.prev = &mi_fn->tdma_item_list;
+ mi_fn->tdma_item_list.next = &mi_fn->tdma_item_list;
+
+ // TODO: check if we get an error if list is empty...
+ llist_add(&mi_fn->mframe_item_entry,
+ mi_next->mframe_item_entry.prev);
+
+ }
+ ti_new = talloc_zero(mi_fn, struct virt_l1_sched_tdma_item);
+ ti_new->msg = msg;
+ ti_new->handler_cb = handler_cb;
+ ti_new->ts = ts;
+ // simply add at end, no ordering for tdma sched items currently
+ llist_add_tail(&ti_new->tdma_item_entry, &mi_fn->tdma_item_list); // TODO: ordered insert needed if tdma scheduler should be implemented
+}
diff --git a/src/host/virt_phy/src/virt_prim_fbsb.c b/src/host/virt_phy/src/virt_prim_fbsb.c
new file mode 100644
index 00000000..6a678d08
--- /dev/null
+++ b/src/host/virt_phy/src/virt_prim_fbsb.c
@@ -0,0 +1,113 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+#include <osmocom/core/msgb.h>
+#include <virtphy/l1ctl_sap.h>
+#include <virtphy/virt_l1_sched.h>
+#include <osmocom/core/gsmtap.h>
+#include <virtphy/logging.h>
+#include <l1ctl_proto.h>
+
+static struct l1_model_ms *l1_model_ms = NULL;
+
+/**
+ * @brief Handler for received L1CTL_FBSB_REQ from L23.
+ *
+ * -- frequency burst synchronisation burst request --
+ *
+ * @param [in] msg the received message.
+ *
+ * Transmit frequency control and synchronisation bursts on FCCH and SCH to calibrate transceiver and search for base stations.
+ * Sync to a given arfcn.
+ *
+ * Note: ms will start receiving msgs on virtual um only after this req was received.
+ * Note: virt bts does not broadcast freq and sync bursts.
+ *
+ */
+void l1ctl_rx_fbsb_req(struct msgb *msg)
+{
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
+ struct l1ctl_fbsb_req *sync_req = (struct l1ctl_fbsb_req *)l1h->data;
+
+ DEBUGP(DL1C,
+ "Received and handled from l23 - L1CTL_FBSB_REQ (arfcn=%u, flags=0x%x)\n",
+ ntohs(sync_req->band_arfcn), sync_req->flags);
+
+ l1_model_ms->state->state = MS_STATE_IDLE_SYNCING;
+ l1_model_ms->state->fbsb.arfcn = ntohs(sync_req->band_arfcn);
+}
+
+/**
+ * @brief A msg was received on l1 that can be used for synchronization.
+ *
+ * Note: for virtual layer 1 this can be a random downlink message, as we can parse the fn from the gsmtap header.
+ */
+void prim_fbsb_sync(struct msgb *msg)
+{
+ struct gsmtap_hdr *gh = msgb_l1(msg);
+ uint32_t fn = ntohl(gh->frame_number); // frame number of the rcv msg
+ uint16_t arfcn = ntohs(gh->arfcn); // arfcn of the received msg
+
+ // ignore messages from other arfcns as the one requested to sync to by l23
+ if (l1_model_ms->state->fbsb.arfcn != (arfcn & GSMTAP_ARFCN_MASK)) {
+ talloc_free(msg);
+ return;
+ }
+ l1_model_ms->state->serving_cell.arfcn = (arfcn & GSMTAP_ARFCN_MASK);
+ l1_model_ms->state->state = MS_STATE_IDLE_CAMPING;
+ /* Not needed in virtual phy */
+ l1_model_ms->state->serving_cell.fn_offset = 0;
+ l1_model_ms->state->serving_cell.time_alignment = 0;
+ l1_model_ms->state->serving_cell.bsic = 0;
+ /* Update current gsm time each time we receive a message on the virt um */
+ gsm_fn2gsmtime(&l1_model_ms->state->downlink_time, fn);
+ /* Restart scheduler */
+ virt_l1_sched_restart(l1_model_ms->state->downlink_time);
+ talloc_free(msg);
+ l1ctl_tx_fbsb_conf(0, (arfcn & GSMTAP_ARFCN_MASK));
+}
+
+/**
+ * @brief Transmit L1CTL_FBSB_CONF to l23.
+ *
+ * -- frequency burst synchronisation burst confirm --
+ *
+ * @param [in] res 0 -> success, 255 -> error.
+ * @param [in] arfcn the arfcn we are synced to.
+ *
+ * No calculation needed for virtual pyh -> uses dummy values for a good link quality.
+ */
+void l1ctl_tx_fbsb_conf(uint8_t res, uint16_t arfcn)
+{
+ struct msgb *msg;
+ struct l1ctl_fbsb_conf *resp;
+ uint32_t fn = 0; // 0 should be okay here
+ uint16_t snr = 40; // signal noise ratio > 40db is best signal (unused in virt)
+ int16_t initial_freq_err = 0; // 0 means no error (unused in virt)
+ uint8_t bsic = 0; // bsci can be read from sync burst (unused in virt)
+
+ msg = l1ctl_create_l2_msg(L1CTL_FBSB_CONF, fn, snr, arfcn);
+
+ resp = (struct l1ctl_fbsb_conf *)msgb_put(msg, sizeof(*resp));
+ resp->initial_freq_err = htons(initial_freq_err);
+ resp->result = res;
+ resp->bsic = bsic;
+
+ DEBUGP(DL1C, "Sending to l23 - %s (res: %u)\n",
+ getL1ctlPrimName(L1CTL_FBSB_CONF), res);
+
+ l1ctl_sap_tx_to_l23(msg);
+}
+/**
+ * @brief Initialize virtual prim rach.
+ *
+ * @param [in] model the l1 model instance
+ */
+void prim_fbsb_init(struct l1_model_ms *model)
+{
+ l1_model_ms = model;
+}
diff --git a/src/host/virt_phy/src/virt_prim_rach.c b/src/host/virt_phy/src/virt_prim_rach.c
new file mode 100644
index 00000000..c558034d
--- /dev/null
+++ b/src/host/virt_phy/src/virt_prim_rach.c
@@ -0,0 +1,140 @@
+/* Layer 1 Random Access Channel Burst */
+
+/* (C) 2010 by Dieter Spaar <spaar@mirider.augusta.de>
+ * (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+#include <osmocom/core/msgb.h>
+#include <virtphy/l1ctl_sap.h>
+#include <virtphy/virt_l1_sched.h>
+#include <virtphy/logging.h>
+#include <virtphy/gsmtapl1_if.h>
+
+#include <l1ctl_proto.h>
+
+static struct l1_model_ms *l1_model_ms = NULL;
+static void virt_l1_sched_handler_cb(struct msgb * msg);
+
+// use if we have a combined uplink (RACH, SDCCH, ...) (see http://www.rfwireless-world.com/Terminology/GSM-combined-channel-configuration.html)
+// if we have no combined channel config, uplink consists of only RACH
+static uint8_t t3_to_rach_comb[51] = {
+ 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 26, 27, 27, 27, 27};
+static uint8_t rach_to_t3_comb[27] = {
+ 4, 5, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 45, 46};
+
+/**
+ * @brief Handler callback function for RACH request.
+ *
+ * @param [in] msg the msg to sent over virtual um.
+ */
+static void virt_l1_sched_handler_cb(struct msgb * msg)
+{
+ gsmtapl1_tx_to_virt_um(msg);
+ l1ctl_tx_rach_conf(l1_model_ms->state->current_time.fn,
+ l1_model_ms->state->serving_cell.arfcn);
+}
+
+/**
+ * @brief Handler for received L1CTL_RACH_REQ from L23.
+ *
+ * -- random access channel request --
+ *
+ * @param [in] msg the received message.
+ *
+ * Transmit RACH request on RACH. Refer to 04.08 - 9.1.8 - Channel request.
+ *
+ */
+void l1ctl_rx_rach_req(struct msgb *msg)
+{
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
+ struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *)l1h->data;
+ struct l1ctl_rach_req *rach_req = (struct l1ctl_rach_req *)ul->payload;
+ uint32_t fn_sched;
+ uint8_t ts = 1; //FIXME mostly, ts 1 is used for rach, where can i get that info? System info?
+ uint16_t offset = ntohs(rach_req->offset);
+
+ DEBUGP(DL1C,
+ "Received and handled from l23 - L1CTL_RACH_REQ (ra=0x%02x, offset=%d combined=%d)\n",
+ rach_req->ra, offset, rach_req->combined);
+
+ if (rach_req->ra == 0x03) {
+ fn_sched = 42;
+ }
+
+ // set ra data to msg (8bits, the 11bit option is not used)
+ msg->l2h = msgb_put(msg, sizeof(uint8_t));
+ *msg->l2h = rach_req->ra;
+
+ // chan_nr need to be encoded here, as it is not set by l23 for the rach request, but needed by virt um
+ ul->chan_nr = rsl_enc_chan_nr(RSL_CHAN_RACH, 0, ts);
+ ul->link_id = LID_DEDIC;
+
+ // sched fn calculation if we have a combined ccch channel configuration
+ if (rach_req->combined) {
+ /* add elapsed RACH slots to offset */
+ offset += t3_to_rach_comb[l1_model_ms->state->current_time.t3];
+ /* offset is the number of RACH slots in the future */
+ fn_sched = l1_model_ms->state->current_time.fn - l1_model_ms->state->current_time.t3;
+ fn_sched += offset / 27 * 51;
+ fn_sched += rach_to_t3_comb[offset % 27];
+ } else {
+ fn_sched = l1_model_ms->state->current_time.fn + offset;
+ }
+
+ virt_l1_sched_schedule(msg, fn_sched, ts, &virt_l1_sched_handler_cb);
+
+}
+
+/**
+ * @brief Transmit L1CTL_RACH_CONF to layer 23.
+ *
+ * -- rach confirm --
+ *
+ * @param [in] fn the fn on which the rach was sent
+ * @param [in] arfcn arfcn on which the rach was sent
+ */
+void l1ctl_tx_rach_conf(uint32_t fn, uint16_t arfcn)
+{
+ struct msgb * msg = l1ctl_create_l2_msg(L1CTL_RACH_CONF, fn, 0, arfcn);
+
+ DEBUGP(DL1C, "Sending to l23 - %s (fn: %u, arfcn: %u)\n",
+ getL1ctlPrimName(L1CTL_RACH_CONF), fn, arfcn);
+ l1ctl_sap_tx_to_l23(msg);
+}
+
+/**
+ * @brief Initialize virtual prim rach.
+ *
+ * @param [in] model the l1 model instance
+ */
+void prim_rach_init(struct l1_model_ms *model)
+{
+ l1_model_ms = model;
+}
diff --git a/src/host/virt_phy/src/virtphy.c b/src/host/virt_phy/src/virtphy.c
index 977a3589..c9e7bdd3 100644
--- a/src/host/virt_phy/src/virtphy.c
+++ b/src/host/virt_phy/src/virtphy.c
@@ -11,6 +11,7 @@
#include <virtphy/l1ctl_sap.h>
#include <virtphy/gsmtapl1_if.h>
#include <virtphy/logging.h>
+#include <virtphy/virt_l1_sched.h>
int main( int argc, char *argv[] )
{
@@ -40,6 +41,7 @@ int main( int argc, char *argv[] )
gsmtapl1_init(model);
l1ctl_sap_init(model);
+ virt_l1_sched_init(model);
LOGP(DVIRPHY, LOGL_INFO, "Virtual physical layer ready...\n \
Waiting for l23 app on", l1ctl_sock_path);