diff options
author | Sebastian Stumpf <sebastian.stumpf87@googlemail.com> | 2017-02-25 16:17:12 +0100 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2017-07-12 23:26:26 +0200 |
commit | 63f3ce248295d945923b37a2add8165297854a76 (patch) | |
tree | f89f4a89ede2756ab4d9583304704aeea271c6c0 /src/host/virt_phy | |
parent | 0a68f6884c052b9f8d9f8bda0316c79405436851 (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.am | 4 | ||||
-rw-r--r-- | src/host/virt_phy/include/virtphy/gsmtapl1_if.h | 5 | ||||
-rw-r--r-- | src/host/virt_phy/include/virtphy/l1ctl_sap.h | 2 | ||||
-rw-r--r-- | src/host/virt_phy/include/virtphy/virt_l1_model.h | 19 | ||||
-rw-r--r-- | src/host/virt_phy/include/virtphy/virt_l1_sched.h | 32 | ||||
-rw-r--r-- | src/host/virt_phy/src/Makefile.am | 7 | ||||
-rw-r--r-- | src/host/virt_phy/src/gsmtapl1_if.c | 29 | ||||
-rw-r--r-- | src/host/virt_phy/src/l1ctl_sap.c | 159 | ||||
-rw-r--r-- | src/host/virt_phy/src/virt_l1_sched_simple.c | 135 | ||||
-rw-r--r-- | src/host/virt_phy/src/virt_prim_fbsb.c | 113 | ||||
-rw-r--r-- | src/host/virt_phy/src/virt_prim_rach.c | 140 | ||||
-rw-r--r-- | src/host/virt_phy/src/virtphy.c | 2 |
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); |