aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSebastian Stumpf <sebastian.stumpf87@googlemail.com>2017-01-08 16:31:50 +0100
committerSebastian Stumpf <sebastian.stumpf87@googlemail.com>2017-01-23 13:54:58 +0100
commitd1c904c4a6d3a862a37d62fdffbb74284a44b24d (patch)
treec49fab8ca640e77b09fae56943adafc5036974c1 /src
parentbf286abc409d04dc1f317a2d74f050235958dac4 (diff)
VIRT-PHY: Added functionality to cooperate with osmocom-bb virt-phy.
This patch improves the virtual physical layer designed to replace the air interface. The purpose is to get rid of the hardware requirements and be able to start testing and implementing layer 2 communication functionality on one machine. Multicast sockets are used to enable bidirectional communication between the BTS and the MS process. The GSMTAP protocol designed for wireshark capturing is used to encapsulate the payload on the virtual physical layer. * Working mcast socket communication and extraction of its functionality. * Fixed OML and RSL startup sequences. * Icludes tests for mcast socket and virtual UM. * Ecapsulation and parsing methods to and from GSMTAP messages. * Basic handlers for file descriptor callbacks from incoming mcast messages. * Multiplexing to different channels based on GSMTAP header channel type.
Diffstat (limited to 'src')
-rw-r--r--src/osmo-bts-virtual/Makefile.am2
-rw-r--r--src/osmo-bts-virtual/bts_model.c65
-rw-r--r--src/osmo-bts-virtual/l1_if.c199
-rw-r--r--src/osmo-bts-virtual/l1_if.h6
-rw-r--r--src/osmo-bts-virtual/main.c27
-rw-r--r--src/osmo-bts-virtual/osmo_mcast_sock.c192
-rw-r--r--src/osmo-bts-virtual/osmo_mcast_sock.h46
-rw-r--r--src/osmo-bts-virtual/scheduler_virtbts.c318
-rw-r--r--src/osmo-bts-virtual/virtual_um.c176
-rw-r--r--src/osmo-bts-virtual/virtual_um.h16
-rw-r--r--src/osmo-bts-virtual/virtualbts_vty.c92
11 files changed, 752 insertions, 387 deletions
diff --git a/src/osmo-bts-virtual/Makefile.am b/src/osmo-bts-virtual/Makefile.am
index 07c6d9d3..1492d1cf 100644
--- a/src/osmo-bts-virtual/Makefile.am
+++ b/src/osmo-bts-virtual/Makefile.am
@@ -6,5 +6,5 @@ EXTRA_DIST = virtual_um.h
bin_PROGRAMS = osmo-bts-virtual
-osmo_bts_virtual_SOURCES = main.c bts_model.c virtualbts_vty.c scheduler_virtbts.c l1_if.c virtual_um.c
+osmo_bts_virtual_SOURCES = main.c bts_model.c virtualbts_vty.c scheduler_virtbts.c l1_if.c virtual_um.c osmo_mcast_sock.c
osmo_bts_virtual_LDADD = $(top_builddir)/src/common/libbts.a $(top_builddir)/src/common/libl1sched.a $(COMMON_LDADD)
diff --git a/src/osmo-bts-virtual/bts_model.c b/src/osmo-bts-virtual/bts_model.c
index 1eea26b1..fbeafa3c 100644
--- a/src/osmo-bts-virtual/bts_model.c
+++ b/src/osmo-bts-virtual/bts_model.c
@@ -22,6 +22,7 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
+#include <osmocom/codec/codec.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/phy_link.h>
@@ -34,6 +35,20 @@
#include <osmo-bts/handover.h>
#include <osmo-bts/l1sap.h>
+// TODO: check if dummy method is sufficient, else implement
+int bts_model_lchan_deactivate(struct gsm_lchan *lchan)
+{
+ return -1;
+}
+
+// TODO: check if dummy method is sufficient, else implement
+int osmo_amr_rtp_dec(const uint8_t *rtppayload, int payload_len, uint8_t *cmr,
+ int8_t *cmi, enum osmo_amr_type *ft,
+ enum osmo_amr_quality *bfi, int8_t *sti)
+{
+ return -1;
+}
+
int bts_model_trx_close(struct gsm_bts_trx *trx)
{
return 0;
@@ -45,14 +60,29 @@ int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan)
}
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
- struct tlv_parsed *old_attr, struct tlv_parsed *new_attr,
- void *obj)
+ struct tlv_parsed *old_attr,
+ struct tlv_parsed *new_attr, void *obj)
{
return 0;
}
static uint8_t vbts_set_bts(struct gsm_bts *bts)
{
+ struct gsm_bts_trx *trx;
+ uint8_t tn;
+
+ llist_for_each_entry(trx, &bts->trx_list, list)
+ {
+ oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(&trx->bb_transc.mo, -1, NM_AVSTATE_OK);
+ for (tn = 0; tn < TRX_NR_TS; tn++) {
+ oml_mo_state_chg(&trx->ts[tn].mo, NM_OPSTATE_DISABLED,
+ NM_AVSTATE_DEPENDENCY);
+ }
+ // report availability of trx to the bts. this will trigger the rsl connection
+ oml_mo_tx_sw_act_rep(&trx->mo);
+ oml_mo_tx_sw_act_rep(&trx->bb_transc.mo);
+ }
return 0;
}
@@ -74,51 +104,43 @@ static uint8_t vbts_set_ts(struct gsm_bts_trx_ts *ts)
}
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
- struct tlv_parsed *new_attr, int kind, void *obj)
+ struct tlv_parsed *new_attr, int kind, void *obj)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
int cause = 0;
switch (foh->msg_type) {
- case NM_MT_SET_BTS_ATTR:
+ case NM_MT_SET_BTS_ATTR: // 0x41
cause = vbts_set_bts(obj);
break;
case NM_MT_SET_RADIO_ATTR:
- cause = vbts_set_trx(obj);
+ cause = vbts_set_trx(obj); // 0x44
break;
- case NM_MT_SET_CHAN_ATTR:
+ case NM_MT_SET_CHAN_ATTR: // 0x47
cause = vbts_set_ts(obj);
break;
}
return oml_fom_ack_nack(msg, cause);
}
-int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
- void *obj)
+// mo: the maintenance object
+int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo, void *obj)
{
int rc;
switch (mo->obj_class) {
case NM_OC_RADIO_CARRIER:
case NM_OC_CHANNEL:
+ case NM_OC_SITE_MANAGER:
+ case NM_OC_BASEB_TRANSC:
+ case NM_OC_BTS:
oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
rc = oml_mo_opstart_ack(mo);
break;
- case NM_OC_BTS:
- case NM_OC_SITE_MANAGER:
- case NM_OC_BASEB_TRANSC:
+ // TODO: gprs support
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
- rc = oml_mo_opstart_ack(mo);
- if (mo->obj_class == NM_OC_BTS) {
- oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_OK);
- }
- break;
default:
rc = oml_mo_opstart_nack(mo, NM_NACK_OBJCLASS_NOTSUPP);
}
@@ -126,7 +148,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
}
int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
- void *obj, uint8_t adm_state)
+ void *obj, uint8_t adm_state)
{
mo->nm_state.administrative = adm_state;
return oml_mo_statechg_ack(mo);
@@ -137,7 +159,6 @@ int bts_model_trx_deact_rf(struct gsm_bts_trx *trx)
return 0;
}
-
int bts_model_change_power(struct gsm_bts_trx *trx, int p_trxout_mdBm)
{
return 0;
diff --git a/src/osmo-bts-virtual/l1_if.c b/src/osmo-bts-virtual/l1_if.c
index 0e407bba..cc9e6712 100644
--- a/src/osmo-bts-virtual/l1_if.c
+++ b/src/osmo-bts-virtual/l1_if.c
@@ -26,6 +26,7 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/gsmtap.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/bts.h>
@@ -40,11 +41,73 @@
#include "virtual_um.h"
+/**
+ * Callback to handle incoming messages from the MS.
+ * The incoming message should be GSM_TAP encapsulated.
+ * TODO: implement all channels
+ */
static void virt_um_rcv_cb(struct virt_um_inst *vui, struct msgb *msg)
{
- /* FIXME: Handle msg from MS */
-}
+ struct osmo_phsap_prim l1sap;
+ struct gsmtap_hdr *gh = (struct gsmtap_hdr *)msg->l1h;
+ struct phy_link *plink = (struct phy_link *)vui->priv;
+ // TODO: is there more than one physical instance? Where do i get the corresponding pinst number? Maybe gsmtap_hdr->antenna?
+ struct phy_instance *pinst = phy_instance_by_num(plink, 0);
+
+ memset(&l1sap, 0, sizeof(l1sap));
+
+ switch (gh->sub_type) {
+ case GSMTAP_CHANNEL_RACH:
+ // generate primitive for upper layer
+ osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_PH_RACH,
+ PRIM_OP_INDICATION,
+ NULL);
+ l1sap.u.rach_ind.chan_nr = 0; // TODO: fill with proper value
+ l1sap.u.rach_ind.ra = 0; // TODO: fill with proper value
+ l1sap.u.rach_ind.fn = gh->frame_number;
+ // todo: schedule the uplink burst
+ //trx_sched_ul_burst(&l1h->l1s, tn, fn, bits, rssi, toa);
+ break;
+ case GSMTAP_CHANNEL_SDCCH:
+ case GSMTAP_CHANNEL_SDCCH4:
+ case GSMTAP_CHANNEL_SDCCH8:
+ // TODO: implement channel handling
+ break;
+ case GSMTAP_CHANNEL_TCH_F:
+ // TODO: implement channel handling
+ break;
+ case GSMTAP_CHANNEL_TCH_H:
+ // TODO: implement channel handling
+ break;
+ case GSMTAP_CHANNEL_AGCH:
+ case GSMTAP_CHANNEL_PCH:
+ case GSMTAP_CHANNEL_BCCH:
+ LOGP(LOGL_NOTICE, DL1P,
+ "Ignore incoming msg - channel type downlink only!");
+ goto nomessage;
+ case GSMTAP_CHANNEL_CCCH:
+ case GSMTAP_CHANNEL_PACCH:
+ case GSMTAP_CHANNEL_PDCH:
+ case GSMTAP_CHANNEL_PTCCH:
+ case GSMTAP_CHANNEL_CBCH51:
+ case GSMTAP_CHANNEL_CBCH52:
+ LOGP(LOGL_NOTICE, DL1P,
+ "Ignore incoming msg - channel type not supported!");
+ goto nomessage;
+ default:
+ LOGP(LOGL_NOTICE, DL1P,
+ "Ignore incoming msg - channel type unknown.");
+ goto nomessage;
+ }
+ /* forward primitive */
+ l1sap_up(pinst->trx, &l1sap);
+ DEBUGP(DL1P, "Message forwarded to layer 2.");
+ return;
+
+ // handle memory deallocation
+ nomessage: free(msg);
+}
/* called by common part once OML link is established */
int bts_model_oml_estab(struct gsm_bts *bts)
@@ -52,6 +115,7 @@ int bts_model_oml_estab(struct gsm_bts *bts)
return 0;
}
+/* called by bts_main to initialize physical link */
int bts_model_phy_link_open(struct phy_link *plink)
{
struct phy_instance *pinst;
@@ -63,10 +127,24 @@ int bts_model_phy_link_open(struct phy_link *plink)
phy_link_state_set(plink, PHY_LINK_CONNECTING);
- plink->u.virt.virt_um = virt_um_init(plink, plink->u.virt.mcast_group,
- plink->u.virt.mcast_port,
- plink->u.virt.mcast_dev, plink,
- virt_um_rcv_cb);
+ if (!plink->u.virt.bts_mcast_group) {
+ plink->u.virt.bts_mcast_group = DEFAULT_BTS_MCAST_GROUP;
+ }
+ if (!plink->u.virt.bts_mcast_port) {
+ plink->u.virt.bts_mcast_port = DEFAULT_BTS_MCAST_PORT;
+ }
+ if (!plink->u.virt.ms_mcast_group) {
+ plink->u.virt.ms_mcast_group = DEFAULT_MS_MCAST_GROUP;
+ }
+ if (!plink->u.virt.ms_mcast_port) {
+ plink->u.virt.ms_mcast_port = DEFAULT_MS_MCAST_PORT;
+ }
+
+ plink->u.virt.virt_um = virt_um_init(plink,
+ plink->u.virt.ms_mcast_group,
+ plink->u.virt.ms_mcast_port,
+ plink->u.virt.bts_mcast_group,
+ plink->u.virt.bts_mcast_port, virt_um_rcv_cb);
if (!plink->u.virt.virt_um) {
phy_link_state_set(plink, PHY_LINK_SHUTDOWN);
return -1;
@@ -74,8 +152,11 @@ int bts_model_phy_link_open(struct phy_link *plink)
/* iterate over list of PHY instances and initialize the
* scheduler */
- llist_for_each_entry(pinst, &plink->instances, list) {
+ llist_for_each_entry(pinst, &plink->instances, list)
+ {
trx_sched_init(&pinst->u.virt.sched, pinst->trx);
+ /* TODO: why only start scheduler for CCCH */
+ /* Only start the scheduler for the transceiver on c0. CCCH is on C0 */
if (pinst->trx == pinst->trx->bts->c0)
vbts_sched_start(pinst->trx->bts);
}
@@ -87,13 +168,13 @@ int bts_model_phy_link_open(struct phy_link *plink)
return 0;
}
-
/*
* primitive handling
*/
/* enable ciphering */
-static int l1if_set_ciphering(struct gsm_lchan *lchan, uint8_t chan_nr, int downlink)
+static int l1if_set_ciphering(struct gsm_lchan *lchan, uint8_t chan_nr,
+ int downlink)
{
struct gsm_bts_trx *trx = lchan->ts->trx;
struct phy_instance *pinst = trx_phy_instance(trx);
@@ -106,17 +187,17 @@ static int l1if_set_ciphering(struct gsm_lchan *lchan, uint8_t chan_nr, int down
if (!downlink) {
/* set uplink */
trx_sched_set_cipher(sched, chan_nr, 0, lchan->encr.alg_id - 1,
- lchan->encr.key, lchan->encr.key_len);
+ lchan->encr.key, lchan->encr.key_len);
lchan->ciph_state = LCHAN_CIPH_RX_CONF;
} else {
/* set downlink and also set uplink, if not already */
if (lchan->ciph_state != LCHAN_CIPH_RX_CONF) {
trx_sched_set_cipher(sched, chan_nr, 0,
- lchan->encr.alg_id - 1, lchan->encr.key,
- lchan->encr.key_len);
+ lchan->encr.alg_id - 1, lchan->encr.key,
+ lchan->encr.key_len);
}
trx_sched_set_cipher(sched, chan_nr, 1, lchan->encr.alg_id - 1,
- lchan->encr.key, lchan->encr.key_len);
+ lchan->encr.key, lchan->encr.key_len);
lchan->ciph_state = LCHAN_CIPH_RXTX_CONF;
}
@@ -124,13 +205,13 @@ static int l1if_set_ciphering(struct gsm_lchan *lchan, uint8_t chan_nr, int down
}
static int mph_info_chan_confirm(struct gsm_bts_trx *trx, uint8_t chan_nr,
- enum osmo_mph_info_type type, uint8_t cause)
+ enum osmo_mph_info_type type, uint8_t cause)
{
struct osmo_phsap_prim l1sap;
memset(&l1sap, 0, sizeof(l1sap));
osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_CONFIRM,
- NULL);
+ NULL);
l1sap.u.info.type = type;
l1sap.u.info.u.act_cnf.chan_nr = chan_nr;
l1sap.u.info.u.act_cnf.cause = cause;
@@ -144,7 +225,7 @@ int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn)
memset(&l1sap, 0, sizeof(l1sap));
osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO,
- PRIM_OP_INDICATION, NULL);
+ PRIM_OP_INDICATION, NULL);
l1sap.u.info.type = PRIM_INFO_TIME;
l1sap.u.info.u.time_ind.fn = fn;
@@ -154,40 +235,42 @@ int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn)
return l1sap_up(bts->c0, &l1sap);
}
-
-static void l1if_fill_meas_res(struct osmo_phsap_prim *l1sap, uint8_t chan_nr, float ta,
- float ber, float rssi)
+static void l1if_fill_meas_res(struct osmo_phsap_prim *l1sap, uint8_t chan_nr,
+ float ta, float ber, float rssi)
{
memset(l1sap, 0, sizeof(*l1sap));
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_MPH_INFO,
- PRIM_OP_INDICATION, NULL);
+ PRIM_OP_INDICATION, NULL);
l1sap->u.info.type = PRIM_INFO_MEAS;
l1sap->u.info.u.meas_ind.chan_nr = chan_nr;
- l1sap->u.info.u.meas_ind.ta_offs_qbits = (int16_t)(ta*4);
- l1sap->u.info.u.meas_ind.ber10k = (unsigned int) (ber * 10000);
- l1sap->u.info.u.meas_ind.inv_rssi = (uint8_t) (rssi * -1);
+ l1sap->u.info.u.meas_ind.ta_offs_qbits = (int16_t)(ta * 4);
+ l1sap->u.info.u.meas_ind.ber10k = (unsigned int)(ber * 10000);
+ l1sap->u.info.u.meas_ind.inv_rssi = (uint8_t)(rssi * -1);
}
-int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn, uint8_t chan_nr,
- int n_errors, int n_bits_total, float rssi, float toa)
+int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn,
+ uint8_t chan_nr, int n_errors, int n_bits_total,
+ float rssi, float toa)
{
struct gsm_lchan *lchan = &trx->ts[tn].lchan[l1sap_chan2ss(chan_nr)];
struct osmo_phsap_prim l1sap;
/* 100% BER is n_bits_total is 0 */
- float ber = n_bits_total==0 ? 1.0 : (float)n_errors / (float)n_bits_total;
+ float ber = n_bits_total == 0 ?
+ 1.0 : (float)n_errors / (float)n_bits_total;
- LOGP(DMEAS, LOGL_DEBUG, "RX L1 frame %s fn=%u chan_nr=0x%02x MS pwr=%ddBm rssi=%.1f dBFS "
- "ber=%.2f%% (%d/%d bits) L1_ta=%d rqd_ta=%d toa=%.2f\n",
- gsm_lchan_name(lchan), fn, chan_nr, ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power),
- rssi, ber*100, n_errors, n_bits_total, lchan->meas.l1_info[1], lchan->rqd_ta, toa);
+ LOGP(DMEAS, LOGL_DEBUG,
+ "RX L1 frame %s fn=%u chan_nr=0x%02x MS pwr=%ddBm rssi=%.1f dBFS "
+ "ber=%.2f%% (%d/%d bits) L1_ta=%d rqd_ta=%d toa=%.2f\n",
+ gsm_lchan_name(lchan), fn, chan_nr,
+ ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power),
+ rssi, ber * 100, n_errors, n_bits_total,
+ lchan->meas.l1_info[1], lchan->rqd_ta, toa);
l1if_fill_meas_res(&l1sap, chan_nr, lchan->rqd_ta + toa, ber, rssi);
return l1sap_up(trx, &l1sap);
}
-
-
/* primitive from common part */
int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
{
@@ -232,7 +315,8 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
if (l1sap->u.info.type == PRIM_INFO_ACTIVATE) {
if ((chan_nr & 0x80)) {
LOGP(DL1C, LOGL_ERROR, "Cannot activate"
- " chan_nr 0x%02x\n", chan_nr);
+ " chan_nr 0x%02x\n",
+ chan_nr);
break;
}
/* activate dedicated channel */
@@ -241,14 +325,15 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
trx_sched_set_lchan(sched, chan_nr, 0x40, 1);
/* set mode */
trx_sched_set_mode(sched, chan_nr,
- lchan->rsl_cmode, lchan->tch_mode,
- lchan->tch.amr_mr.num_modes,
- lchan->tch.amr_mr.bts_mode[0].mode,
- lchan->tch.amr_mr.bts_mode[1].mode,
- lchan->tch.amr_mr.bts_mode[2].mode,
- lchan->tch.amr_mr.bts_mode[3].mode,
- amr_get_initial_mode(lchan),
- (lchan->ho.active == 1));
+ lchan->rsl_cmode,
+ lchan->tch_mode,
+ lchan->tch.amr_mr.num_modes,
+ lchan->tch.amr_mr.bts_mode[0].mode,
+ lchan->tch.amr_mr.bts_mode[1].mode,
+ lchan->tch.amr_mr.bts_mode[2].mode,
+ lchan->tch.amr_mr.bts_mode[3].mode,
+ amr_get_initial_mode(lchan),
+ (lchan->ho.active == 1));
/* init lapdm */
lchan_init_lapdm(lchan);
/* set lchan active */
@@ -257,31 +342,32 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
l1if_set_ciphering(lchan, chan_nr, 0);
l1if_set_ciphering(lchan, chan_nr, 1);
if (lchan->encr.alg_id)
- lchan->ciph_state = LCHAN_CIPH_RXTX_CONF;
+ lchan->ciph_state =
+ LCHAN_CIPH_RXTX_CONF;
else
lchan->ciph_state = LCHAN_CIPH_NONE;
/* confirm */
mph_info_chan_confirm(trx, chan_nr,
- PRIM_INFO_ACTIVATE, 0);
+ PRIM_INFO_ACTIVATE, 0);
break;
}
if (l1sap->u.info.type == PRIM_INFO_MODIFY) {
/* change mode */
trx_sched_set_mode(sched, chan_nr,
- lchan->rsl_cmode, lchan->tch_mode,
- lchan->tch.amr_mr.num_modes,
- lchan->tch.amr_mr.bts_mode[0].mode,
- lchan->tch.amr_mr.bts_mode[1].mode,
- lchan->tch.amr_mr.bts_mode[2].mode,
- lchan->tch.amr_mr.bts_mode[3].mode,
- amr_get_initial_mode(lchan),
- 0);
+ lchan->rsl_cmode,
+ lchan->tch_mode,
+ lchan->tch.amr_mr.num_modes,
+ lchan->tch.amr_mr.bts_mode[0].mode,
+ lchan->tch.amr_mr.bts_mode[1].mode,
+ lchan->tch.amr_mr.bts_mode[2].mode,
+ lchan->tch.amr_mr.bts_mode[3].mode,
+ amr_get_initial_mode(lchan), 0);
break;
}
if ((chan_nr & 0x80)) {
LOGP(DL1C, LOGL_ERROR, "Cannot deactivate "
- "chan_nr 0x%02x\n", chan_nr);
+ "chan_nr 0x%02x\n", chan_nr);
break;
}
/* deactivate associated channel */
@@ -293,26 +379,25 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
trx_sched_set_lchan(sched, chan_nr, 0x00, 0);
/* confirm only on dedicated channel */
mph_info_chan_confirm(trx, chan_nr,
- PRIM_INFO_DEACTIVATE, 0);
+ PRIM_INFO_DEACTIVATE, 0);
lchan->ciph_state = 0; /* FIXME: do this in common/\*.c */
}
break;
default:
LOGP(DL1C, LOGL_NOTICE, "unknown MPH-INFO.req %d\n",
- l1sap->u.info.type);
+ l1sap->u.info.type);
rc = -EINVAL;
goto done;
}
break;
default:
LOGP(DL1C, LOGL_NOTICE, "unknown prim %d op %d\n",
- l1sap->oph.primitive, l1sap->oph.operation);
+ l1sap->oph.primitive, l1sap->oph.operation);
rc = -EINVAL;
goto done;
}
-done:
- if (msg)
+ done: if (msg)
msgb_free(msg);
return rc;
}
diff --git a/src/osmo-bts-virtual/l1_if.h b/src/osmo-bts-virtual/l1_if.h
index 8f6d3a86..b11602f6 100644
--- a/src/osmo-bts-virtual/l1_if.h
+++ b/src/osmo-bts-virtual/l1_if.h
@@ -6,9 +6,9 @@
#include "virtual_um.h"
struct vbts_l1h {
- struct gsm_bts_trx *trx;
- struct l1sched_trx l1s;
- struct virt_um_inst *virt_um;
+ struct gsm_bts_trx *trx;
+ struct l1sched_trx l1s;
+ struct virt_um_inst *virt_um;
};
struct vbts_l1h *l1if_open(struct gsm_bts_trx *trx);
diff --git a/src/osmo-bts-virtual/main.c b/src/osmo-bts-virtual/main.c
index a27ee453..d4900d61 100644
--- a/src/osmo-bts-virtual/main.c
+++ b/src/osmo-bts-virtual/main.c
@@ -76,12 +76,10 @@ int bts_model_handle_options(int argc, char **argv)
while (1) {
int option_idx = 0, c;
static const struct option long_options[] = {
- /* specific to this hardware */
- { 0, 0, 0, 0 }
- };
+ /* specific to this hardware */
+ {0, 0, 0, 0}};
- c = getopt_long(argc, argv, "",
- long_options, &option_idx);
+ c = getopt_long(argc, argv, "", long_options, &option_idx);
if (c == -1)
break;
@@ -101,6 +99,25 @@ void bts_model_abis_close(struct gsm_bts *bts)
bts_shutdown(bts, "Abis close");
}
+void bts_model_phy_link_set_defaults(struct phy_link *plink)
+{
+}
+
+void bts_model_phy_instance_set_defaults(struct phy_instance *pinst)
+{
+}
+
+int bts_model_ts_disconnect(struct gsm_bts_trx_ts *ts)
+{
+ return -ENOTSUP;
+}
+
+int bts_model_ts_connect(struct gsm_bts_trx_ts *ts,
+ enum gsm_phys_chan_config as_pchan)
+{
+ return -ENOTSUP;
+}
+
int main(int argc, char **argv)
{
return bts_main(argc, argv);
diff --git a/src/osmo-bts-virtual/osmo_mcast_sock.c b/src/osmo-bts-virtual/osmo_mcast_sock.c
new file mode 100644
index 00000000..c1777347
--- /dev/null
+++ b/src/osmo-bts-virtual/osmo_mcast_sock.c
@@ -0,0 +1,192 @@
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/select.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <talloc.h>
+#include <unistd.h>
+
+#include "osmo_mcast_sock.h"
+
+struct mcast_server_sock *mcast_server_sock_setup(void *ctx,
+ char* tx_mcast_group,
+ int tx_mcast_port,
+ int loopback)
+{
+ struct mcast_server_sock *serv_sock = talloc_zero(ctx,
+ struct mcast_server_sock);
+
+ serv_sock->osmo_fd = talloc_zero(ctx, struct osmo_fd);
+ serv_sock->sock_conf = talloc_zero(ctx, struct sockaddr_in);
+
+ // setup mcast server socket
+ serv_sock->osmo_fd->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (serv_sock->osmo_fd->fd == -1) {
+ perror("Failed to create Multicast Server Socket");
+ return NULL;
+ }
+
+ serv_sock->sock_conf->sin_family = AF_INET;
+ serv_sock->sock_conf->sin_addr.s_addr = inet_addr(tx_mcast_group);
+ serv_sock->sock_conf->sin_port = htons(tx_mcast_port);
+
+ // determines whether sent mcast packets should be looped back to the local sockets.
+ // loopback must be enabled if the mcast client is on the same machine
+ if (setsockopt(serv_sock->osmo_fd->fd, IPPROTO_IP,
+ IP_MULTICAST_LOOP, &loopback, sizeof(loopback)) < 0) {
+ perror("Failed to disable loopback.\n");
+ return NULL;
+ }
+
+ return serv_sock;
+}
+
+struct mcast_client_sock *mcast_client_sock_setup(
+ void *ctx, char* mcast_group, int mcast_port,
+ int (*fd_rx_cb)(struct osmo_fd *ofd, unsigned int what),
+ void *osmo_fd_data)
+{
+ struct mcast_client_sock *client_sock = talloc_zero(ctx,
+ struct mcast_client_sock);
+ struct sockaddr_in *rx_sock_conf = talloc_zero(NULL,
+ struct sockaddr_in);
+ int rc, reuseaddr = 1, loopback = 1;
+
+ client_sock->osmo_fd = talloc_zero(ctx, struct osmo_fd);
+ client_sock->mcast_group = talloc_zero(ctx, struct ip_mreq);
+
+ // Create mcast client socket
+ client_sock->osmo_fd->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (client_sock->osmo_fd->fd == -1) {
+ perror("Could not create mcast client socket");
+ return NULL;
+ }
+
+ // Enable SO_REUSEADDR to allow multiple instances of this application to receive copies of the multicast datagrams.
+ rc = setsockopt(client_sock->osmo_fd->fd,
+ SOL_SOCKET,
+ SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr));
+ if (rc < 0) {
+ perror("Failed to configure REUSEADDR option");
+ return NULL;
+ }
+
+ // Bind to the proper port number with the IP address specified as INADDR_ANY.
+ rx_sock_conf->sin_family = AF_INET;
+ rx_sock_conf->sin_addr.s_addr = htonl(INADDR_ANY);
+ rx_sock_conf->sin_port = htons(mcast_port);
+ rc = bind(client_sock->osmo_fd->fd, (struct sockaddr *)rx_sock_conf,
+ sizeof(*rx_sock_conf));
+ talloc_free(rx_sock_conf);
+ if (rc < 0) {
+ perror("Could not bind mcast client socket");
+ return NULL;
+ }
+
+ // Enable loopback of msgs to the host.
+ // Loopback must be enabled for the client, so multiple processes are able to recevie a mcast package.
+ rc = setsockopt(client_sock->osmo_fd->fd,
+ IPPROTO_IP,
+ IP_MULTICAST_LOOP, &loopback, sizeof(loopback));
+ if (rc < 0) {
+ perror("Failed to enable IP_MULTICAST_LOOP");
+ return NULL;
+ }
+
+ // Configure and join the multicast group
+ client_sock->mcast_group->imr_multiaddr.s_addr = inet_addr(mcast_group);
+ client_sock->mcast_group->imr_interface.s_addr = htonl(INADDR_ANY);
+ rc = setsockopt(client_sock->osmo_fd->fd,
+ IPPROTO_IP,
+ IP_ADD_MEMBERSHIP, client_sock->mcast_group,
+ sizeof(*client_sock->mcast_group));
+ if (rc < 0) {
+ perror("Failed to join to mcast goup");
+ return NULL;
+ }
+
+ // configure and register the osmocom filedescriptor
+ client_sock->osmo_fd->cb = fd_rx_cb;
+ client_sock->osmo_fd->when = BSC_FD_READ;
+ client_sock->osmo_fd->data = osmo_fd_data;
+
+ osmo_fd_register(client_sock->osmo_fd);
+
+ return client_sock;
+}
+
+struct mcast_bidir_sock *mcast_bidir_sock_setup(
+ void *ctx, char* tx_mcast_group, int tx_mcast_port,
+ char* rx_mcast_group, int rx_mcast_port, int loopback,
+ int (*fd_rx_cb)(struct osmo_fd *ofd, unsigned int what),
+ void *osmo_fd_data)
+{
+ struct mcast_bidir_sock *bidir_sock = talloc(ctx,
+ struct mcast_bidir_sock);
+ bidir_sock->rx_sock = mcast_client_sock_setup(ctx, rx_mcast_group,
+ rx_mcast_port, fd_rx_cb, osmo_fd_data);
+ bidir_sock->tx_sock = mcast_server_sock_setup(ctx, tx_mcast_group,
+ tx_mcast_port, loopback);
+ if (!bidir_sock->rx_sock || !bidir_sock->tx_sock) {
+ return NULL;
+ }
+ return bidir_sock;
+
+}
+
+int mcast_client_sock_rx(struct mcast_client_sock *client_sock, void* buf,
+ int buf_len)
+{
+ return recv(client_sock->osmo_fd->fd, buf, buf_len, 0);
+}
+
+int mcast_server_sock_tx(struct mcast_server_sock *serv_sock, void* data,
+ int data_len)
+{
+ return sendto(serv_sock->osmo_fd->fd, data, data_len, 0,
+ (struct sockaddr *)serv_sock->sock_conf,
+ sizeof(*serv_sock->sock_conf));
+}
+
+int mcast_bidir_sock_tx(struct mcast_bidir_sock *bidir_sock, void* data,
+ int data_len)
+{
+ return mcast_server_sock_tx(bidir_sock->tx_sock, data, data_len);
+}
+int mcast_bidir_sock_rx(struct mcast_bidir_sock *bidir_sock, void* buf,
+ int buf_len)
+{
+ return mcast_client_sock_rx(bidir_sock->rx_sock, buf, buf_len);
+}
+
+void mcast_client_sock_close(struct mcast_client_sock *client_sock)
+{
+ setsockopt(client_sock->osmo_fd->fd,
+ IPPROTO_IP,
+ IP_DROP_MEMBERSHIP, client_sock->mcast_group,
+ sizeof(*client_sock->mcast_group));
+ osmo_fd_unregister(client_sock->osmo_fd);
+ client_sock->osmo_fd->fd = -1;
+ client_sock->osmo_fd->when = 0;
+ close(client_sock->osmo_fd->fd);
+ talloc_free(client_sock->mcast_group);
+ talloc_free(client_sock->osmo_fd);
+ talloc_free(client_sock);
+
+}
+void mcast_server_sock_close(struct mcast_server_sock *serv_sock)
+{
+ close(serv_sock->osmo_fd->fd);
+ talloc_free(serv_sock->sock_conf);
+ talloc_free(serv_sock);
+}
+
+void mcast_bidir_sock_close(struct mcast_bidir_sock *bidir_sock)
+{
+ mcast_client_sock_close(bidir_sock->rx_sock);
+ mcast_server_sock_close(bidir_sock->tx_sock);
+ talloc_free(bidir_sock);
+}
diff --git a/src/osmo-bts-virtual/osmo_mcast_sock.h b/src/osmo-bts-virtual/osmo_mcast_sock.h
new file mode 100644
index 00000000..f318ffea
--- /dev/null
+++ b/src/osmo-bts-virtual/osmo_mcast_sock.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <netinet/in.h>
+#include <osmocom/core/select.h>
+
+struct mcast_server_sock {
+ struct osmo_fd *osmo_fd;
+ struct sockaddr_in *sock_conf;
+};
+
+struct mcast_client_sock {
+ struct osmo_fd *osmo_fd;
+ struct ip_mreq *mcast_group;
+};
+
+struct mcast_bidir_sock {
+ struct mcast_server_sock *tx_sock;
+ struct mcast_client_sock *rx_sock;
+};
+
+struct mcast_bidir_sock *mcast_bidir_sock_setup(
+ void *ctx, char* tx_mcast_group, int tx_mcast_port,
+ char* rx_mcast_group, int rx_mcast_port, int loopback,
+ int (*fd_rx_cb)(struct osmo_fd *ofd, unsigned int what),
+ void *osmo_fd_data);
+
+struct mcast_server_sock *mcast_server_sock_setup(void *ctx,
+ char* tx_mcast_group,
+ int tx_mcast_port,
+ int loopback);
+struct mcast_client_sock *mcast_client_sock_setup(
+ void *ctx, char* mcast_group, int mcast_port,
+ int (*fd_rx_cb)(struct osmo_fd *ofd, unsigned int what),
+ void *osmo_fd_data);
+int mcast_client_sock_rx(struct mcast_client_sock *client_sock, void* buf,
+ int buf_len);
+int mcast_server_sock_tx(struct mcast_server_sock *serv_sock, void* data,
+ int data_len);
+int mcast_bidir_sock_tx(struct mcast_bidir_sock *bidir_sock, void* data,
+ int data_len);
+int mcast_bidir_sock_rx(struct mcast_bidir_sock *bidir_sock, void* buf,
+ int buf_len);
+void mcast_client_sock_close(struct mcast_client_sock* client_sock);
+void mcast_server_sock_close(struct mcast_server_sock* server_sock);
+void mcast_bidir_sock_close(struct mcast_bidir_sock* bidir_sock);
+
diff --git a/src/osmo-bts-virtual/scheduler_virtbts.c b/src/osmo-bts-virtual/scheduler_virtbts.c
index c23632c5..0531ef61 100644
--- a/src/osmo-bts-virtual/scheduler_virtbts.c
+++ b/src/osmo-bts-virtual/scheduler_virtbts.c
@@ -28,6 +28,8 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/core/gsmtap.h>
+#include <osmocom/gsm/rsl.h>
#include <osmocom/netif/rtp.h>
@@ -44,23 +46,48 @@
extern void *tall_bts_ctx;
-
-
+/**
+ * Send a message over the virtual um interface.
+ * This will at first wrap the msg with a gsmtap header and then write it to the declared multicast socket.
+ */
static void tx_to_virt_um(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, struct msgb *msg)
+ enum trx_chan_type chan, struct msgb *msg)
{
const struct trx_chan_desc *chdesc = &trx_chan_desc[chan];
- uint8_t ss = 0; //FIXME(chdesc);
- uint8_t gsmtap_chan;
+ uint8_t chan_type, timeslot, subslot, signal_dbm, signal_snr;
+ rsl_dec_chan_nr(chdesc->chan_nr, &chan_type, &subslot, &timeslot);
+ if (chan_type == RSL_CHAN_PCH_AGCH) {
+ if (L1SAP_FN2CCCHBLOCK(fn) >= 1) {
+ chan_type = GSMTAP_CHANNEL_PCH;
+ } else {
+ chan_type = GSMTAP_CHANNEL_AGCH;
+ }
+ } else {
+ chan_type = chantype_rsl2gsmtap(chan_type, chdesc->link_id); // the logical channel type
+ }
+ //uint8_t timeslot = tn; // indicates the physical channel
+ //uint8_t subslot = 0; // indicates the logical channel subslot on the physical channel FIXME: calculate
+ signal_dbm = 63; // the signal strength is not needed in virt phy
+ signal_snr = 40; // the signal to noice ratio is not needed in virt phy
+ uint8_t *data = msgb_l2(msg); // data bits to transmit (whole message without l1 header)
+ uint8_t data_len = msgb_l2len(msg);
struct msgb *outmsg;
- gsmtap_chan = chantype_rsl2gsmtap(chdesc->chan_nr, chdesc->link_id);
- outmsg = gsmtap_makemsg(l1t->trx->arfcn, tn, gsmtap_chan, ss, fn,
- 0, 0, msgb_l2(msg), msgb_l2len(msg));
+ // TODO: encrypt and encode message data
+
+ outmsg = gsmtap_makemsg(l1t->trx->arfcn, timeslot, chan_type, subslot,
+ fn, signal_dbm, signal_snr, data, data_len);
if (outmsg) {
struct phy_instance *pinst = trx_phy_instance(l1t->trx);
struct virt_um_inst *virt_um = pinst->phy_link->u.virt.virt_um;
- virt_um_write_msg(virt_um, outmsg);
+ if (virt_um_write_msg(virt_um, outmsg) == -1) {
+ struct gsmtap_hdr *gh = (struct gsmtap_hdr *)msgb_data(
+ outmsg);
+ LOGP(DL1C, LOGL_ERROR,
+ "Message could not be written to virtual UM! arfcn = %u gsmtap_chan=%u fn=%u ts=%u\n",
+ gh->arfcn, gh->type, gh->frame_number,
+ gh->timeslot);
+ }
}
/* free message */
@@ -73,62 +100,67 @@ static void tx_to_virt_um(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
/* an IDLE burst returns nothing. on C0 it is replaced by dummy burst */
ubit_t *tx_idle_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
{
return NULL;
}
ubit_t *tx_fcch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
{
return NULL;
}
ubit_t *tx_sch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
{
return NULL;
}
ubit_t *tx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
{
struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
struct msgb *msg;
+ if (fn == 57) {
+ DEBUGP(DL1P, "lol");
+ }
+
if (bid > 0)
return NULL;
/* get mac block from queue */
+ // what queue is that and who does fill it?
msg = _sched_dequeue_prim(l1t, tn, fn, chan);
if (msg)
goto got_msg;
LOGP(DL1P, LOGL_INFO, "%s has not been served !! No prim for "
- "trx=%u ts=%u at fn=%u to transmit.\n",
- trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
+ "trx=%u ts=%u at fn=%u to transmit.\n",
+ trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
-no_msg:
- return NULL;
+ no_msg: return NULL;
-got_msg:
+ got_msg:
/* check validity of message */
if (msgb_l2len(msg) != GSM_MACBLOCK_LEN) {
LOGP(DL1P, LOGL_FATAL, "Prim not 23 bytes, please FIX! "
- "(len=%d)\n", msgb_l2len(msg));
+ "(len=%d)\n", msgb_l2len(msg));
/* free message */
msgb_free(msg);
goto no_msg;
}
+ // transmit the msg received on dl from bsc to layer1 (virt um)
tx_to_virt_um(l1t, tn, fn, chan, msg);
return NULL;
}
ubit_t *tx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
{
struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
@@ -144,21 +176,20 @@ ubit_t *tx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
goto got_msg;
LOGP(DL1P, LOGL_INFO, "%s has not been served !! No prim for "
- "trx=%u ts=%u at fn=%u to transmit.\n",
- trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
+ "trx=%u ts=%u at fn=%u to transmit.\n",
+ trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
-no_msg:
- return NULL;
+ no_msg: return NULL;
-got_msg:
- tx_to_virt_um(l1t, tn, fn, chan, msg);
+ got_msg: tx_to_virt_um(l1t, tn, fn, chan, msg);
return NULL;
}
static void tx_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, struct msgb **_msg_tch,
- struct msgb **_msg_facch, int codec_mode_request)
+ enum trx_chan_type chan, uint8_t bid,
+ struct msgb **_msg_tch, struct msgb **_msg_facch,
+ int codec_mode_request)
{
struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL;
@@ -169,16 +200,16 @@ static void tx_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
#if 0
/* handle loss detection of received TCH frames */
if (rsl_cmode == RSL_CMOD_SPD_SPEECH
- && ++(chan_state->lost) > 5) {
+ && ++(chan_state->lost) > 5) {
uint8_t tch_data[GSM_FR_BYTES];
int len;
LOGP(DL1P, LOGL_NOTICE, "Missing TCH bursts detected, sending "
- "BFI for %s\n", trx_chan_desc[chan].name);
+ "BFI for %s\n", trx_chan_desc[chan].name);
/* indicate bad frame */
switch (tch_mode) {
- case GSM48_CMODE_SPEECH_V1: /* FR / HR */
+ case GSM48_CMODE_SPEECH_V1: /* FR / HR */
if (chan != TRXC_TCHF) { /* HR */
tch_data[0] = 0x70; /* F = 0, FT = 111 */
memset(tch_data + 1, 0, 14);
@@ -188,29 +219,29 @@ static void tx_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
memset(tch_data, 0, GSM_FR_BYTES);
len = GSM_FR_BYTES;
break;
- case GSM48_CMODE_SPEECH_EFR: /* EFR */
+ case GSM48_CMODE_SPEECH_EFR: /* EFR */
if (chan != TRXC_TCHF)
- goto inval_mode1;
+ goto inval_mode1;
memset(tch_data, 0, GSM_EFR_BYTES);
len = GSM_EFR_BYTES;
break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
len = amr_compose_payload(tch_data,
- chan_state->codec[chan_state->dl_cmr],
- chan_state->codec[chan_state->dl_ft], 1);
+ chan_state->codec[chan_state->dl_cmr],
+ chan_state->codec[chan_state->dl_ft], 1);
if (len < 2)
- break;
+ break;
memset(tch_data + 2, 0, len - 2);
_sched_compose_tch_ind(l1t, tn, 0, chan, tch_data, len);
break;
- default:
-inval_mode1:
+ default:
+ inval_mode1:
LOGP(DL1P, LOGL_ERROR, "TCH mode invalid, please "
- "fix!\n");
+ "fix!\n");
len = 0;
}
if (len)
- _sched_compose_tch_ind(l1t, tn, 0, chan, tch_data, len);
+ _sched_compose_tch_ind(l1t, tn, 0, chan, tch_data, len);
}
#endif
@@ -225,7 +256,7 @@ inval_mode1:
l1sap = msgb_l1sap_prim(msg2);
if (l1sap->oph.primitive == PRIM_TCH) {
LOGP(DL1P, LOGL_FATAL, "TCH twice, "
- "please FIX! ");
+ "please FIX! ");
msgb_free(msg2);
} else
msg_facch = msg2;
@@ -236,7 +267,7 @@ inval_mode1:
l1sap = msgb_l1sap_prim(msg2);
if (l1sap->oph.primitive != PRIM_TCH) {
LOGP(DL1P, LOGL_FATAL, "FACCH twice, "
- "please FIX! ");
+ "please FIX! ");
msgb_free(msg2);
} else
msg_tch = msg2;
@@ -253,7 +284,7 @@ inval_mode1:
/* check validity of message */
if (msg_facch && msgb_l2len(msg_facch) != GSM_MACBLOCK_LEN) {
LOGP(DL1P, LOGL_FATAL, "Prim not 23 bytes, please FIX! "
- "(len=%d)\n", msgb_l2len(msg_facch));
+ "(len=%d)\n", msgb_l2len(msg_facch));
/* free message */
msgb_free(msg_facch);
msg_facch = NULL;
@@ -266,10 +297,12 @@ inval_mode1:
int cmr, ft, i;
if (rsl_cmode != RSL_CMOD_SPD_SPEECH) {
- LOGP(DL1P, LOGL_NOTICE, "%s Dropping speech frame, "
- "because we are not in speech mode trx=%u "
- "ts=%u at fn=%u.\n", trx_chan_desc[chan].name,
- l1t->trx->nr, tn, fn);
+ LOGP(DL1P, LOGL_NOTICE,
+ "%s Dropping speech frame, "
+ "because we are not in speech mode trx=%u "
+ "ts=%u at fn=%u.\n",
+ trx_chan_desc[chan].name, l1t->trx->nr,
+ tn, fn);
goto free_bad_msg;
}
@@ -278,24 +311,27 @@ inval_mode1:
if (chan != TRXC_TCHF) { /* HR */
len = 15;
if (msgb_l2len(msg_tch) >= 1
- && (msg_tch->l2h[0] & 0xf0) != 0x00) {
- LOGP(DL1P, LOGL_NOTICE, "%s "
- "Transmitting 'bad "
- "HR frame' trx=%u ts=%u at "
- "fn=%u.\n",
- trx_chan_desc[chan].name,
- l1t->trx->nr, tn, fn);
+ && (msg_tch->l2h[0] & 0xf0)
+ != 0x00) {
+ LOGP(DL1P, LOGL_NOTICE,
+ "%s "
+ "Transmitting 'bad "
+ "HR frame' trx=%u ts=%u at "
+ "fn=%u.\n",
+ trx_chan_desc[chan].name,
+ l1t->trx->nr, tn, fn);
goto free_bad_msg;
}
break;
}
len = GSM_FR_BYTES;
if (msgb_l2len(msg_tch) >= 1
- && (msg_tch->l2h[0] >> 4) != 0xd) {
- LOGP(DL1P, LOGL_NOTICE, "%s Transmitting 'bad "
- "FR frame' trx=%u ts=%u at fn=%u.\n",
- trx_chan_desc[chan].name,
- l1t->trx->nr, tn, fn);
+ && (msg_tch->l2h[0] >> 4) != 0xd) {
+ LOGP(DL1P, LOGL_NOTICE,
+ "%s Transmitting 'bad "
+ "FR frame' trx=%u ts=%u at fn=%u.\n",
+ trx_chan_desc[chan].name,
+ l1t->trx->nr, tn, fn);
goto free_bad_msg;
}
break;
@@ -304,26 +340,27 @@ inval_mode1:
goto inval_mode2;
len = GSM_EFR_BYTES;
if (msgb_l2len(msg_tch) >= 1
- && (msg_tch->l2h[0] >> 4) != 0xc) {
- LOGP(DL1P, LOGL_NOTICE, "%s Transmitting 'bad "
- "EFR frame' trx=%u ts=%u at fn=%u.\n",
- trx_chan_desc[chan].name,
- l1t->trx->nr, tn, fn);
+ && (msg_tch->l2h[0] >> 4) != 0xc) {
+ LOGP(DL1P, LOGL_NOTICE,
+ "%s Transmitting 'bad "
+ "EFR frame' trx=%u ts=%u at fn=%u.\n",
+ trx_chan_desc[chan].name,
+ l1t->trx->nr, tn, fn);
goto free_bad_msg;
}
break;
case GSM48_CMODE_SPEECH_AMR: /* AMR */
#if 0
len = amr_decompose_payload(msg_tch->l2h,
- msgb_l2len(msg_tch), &cmr_codec, &ft_codec,
- &bfi);
+ msgb_l2len(msg_tch), &cmr_codec, &ft_codec,
+ &bfi);
cmr = -1;
ft = -1;
for (i = 0; i < chan_state->codecs; i++) {
if (chan_state->codec[i] == cmr_codec)
- cmr = i;
+ cmr = i;
if (chan_state->codec[i] == ft_codec)
- ft = i;
+ ft = i;
}
if (cmr >= 0) { /* new request */
chan_state->dl_cmr = cmr;
@@ -335,46 +372,47 @@ inval_mode1:
}
if (ft < 0) {
LOGP(DL1P, LOGL_ERROR, "%s Codec (FT = %d) "
- " of RTP frame not in list. "
- "trx=%u ts=%u\n",
- trx_chan_desc[chan].name, ft_codec,
- l1t->trx->nr, tn);
+ " of RTP frame not in list. "
+ "trx=%u ts=%u\n",
+ trx_chan_desc[chan].name, ft_codec,
+ l1t->trx->nr, tn);
goto free_bad_msg;
}
if (codec_mode_request && chan_state->dl_ft != ft) {
LOGP(DL1P, LOGL_NOTICE, "%s Codec (FT = %d) "
- " of RTP cannot be changed now, but in "
- "next frame. trx=%u ts=%u\n",
- trx_chan_desc[chan].name, ft_codec,
- l1t->trx->nr, tn);
+ " of RTP cannot be changed now, but in "
+ "next frame. trx=%u ts=%u\n",
+ trx_chan_desc[chan].name, ft_codec,
+ l1t->trx->nr, tn);
goto free_bad_msg;
}
chan_state->dl_ft = ft;
if (bfi) {
LOGP(DL1P, LOGL_NOTICE, "%s Transmitting 'bad "
- "AMR frame' trx=%u ts=%u at fn=%u.\n",
- trx_chan_desc[chan].name,
- l1t->trx->nr, tn, fn);
+ "AMR frame' trx=%u ts=%u at fn=%u.\n",
+ trx_chan_desc[chan].name,
+ l1t->trx->nr, tn, fn);
goto free_bad_msg;
}
#endif
break;
default:
-inval_mode2:
+ inval_mode2:
LOGP(DL1P, LOGL_ERROR, "TCH mode invalid, please "
- "fix!\n");
+ "fix!\n");
goto free_bad_msg;
}
if (len < 0) {
LOGP(DL1P, LOGL_ERROR, "Cannot send invalid AMR "
- "payload\n");
+ "payload\n");
goto free_bad_msg;
}
if (msgb_l2len(msg_tch) != len) {
- LOGP(DL1P, LOGL_ERROR, "Cannot send payload with "
- "invalid length! (expecing %d, received %d)\n",
- len, msgb_l2len(msg_tch));
-free_bad_msg:
+ LOGP(DL1P, LOGL_ERROR,
+ "Cannot send payload with "
+ "invalid length! (expecing %d, received %d)\n",
+ len, msgb_l2len(msg_tch));
+ free_bad_msg:
/* free message */
msgb_free(msg_tch);
msg_tch = NULL;
@@ -382,13 +420,12 @@ free_bad_msg:
}
}
-send_frame:
- *_msg_tch = msg_tch;
+ send_frame: *_msg_tch = msg_tch;
*_msg_facch = msg_facch;
}
ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
{
struct msgb *msg_tch = NULL, *msg_facch = NULL;
struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
@@ -400,13 +437,13 @@ ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
return NULL;
tx_tch_common(l1t, tn, fn, chan, bid, &msg_tch, &msg_facch,
- (((fn + 4) % 26) >> 2) & 1);
+ (((fn + 4) % 26) >> 2) & 1);
/* no message at all */
if (!msg_tch && !msg_facch) {
LOGP(DL1P, LOGL_INFO, "%s has not been served !! No prim for "
- "trx=%u ts=%u at fn=%u to transmit.\n",
- trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
+ "trx=%u ts=%u at fn=%u to transmit.\n",
+ trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
goto send_burst;
}
@@ -416,13 +453,13 @@ ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
} else
tx_to_virt_um(l1t, tn, fn, chan, msg_tch);
-send_burst:
+ send_burst:
return NULL;
}
ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
{
struct msgb *msg_tch = NULL, *msg_facch = NULL;
struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
@@ -436,13 +473,13 @@ ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
/* get TCH and/or FACCH */
tx_tch_common(l1t, tn, fn, chan, bid, &msg_tch, &msg_facch,
- (((fn + 4) % 26) >> 2) & 1);
+ (((fn + 4) % 26) >> 2) & 1);
/* check for FACCH alignment */
if (msg_facch && ((((fn + 4) % 26) >> 2) & 1)) {
LOGP(DL1P, LOGL_ERROR, "%s Cannot transmit FACCH starting on "
- "even frames, please fix RTS!\n",
- trx_chan_desc[chan].name);
+ "even frames, please fix RTS!\n",
+ trx_chan_desc[chan].name);
msgb_free(msg_facch);
msg_facch = NULL;
}
@@ -450,8 +487,8 @@ ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
/* no message at all */
if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) {
LOGP(DL1P, LOGL_INFO, "%s has not been served !! No prim for "
- "trx=%u ts=%u at fn=%u to transmit.\n",
- trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
+ "trx=%u ts=%u at fn=%u to transmit.\n",
+ trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
goto send_burst;
}
@@ -461,11 +498,9 @@ ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
} else
tx_to_virt_um(l1t, tn, fn, chan, msg_tch);
-send_burst:
- return NULL;
+ send_burst: return NULL;
}
-
/***********************************************************************
* RX on uplink (indication to upper layer)
***********************************************************************/
@@ -475,42 +510,43 @@ send_burst:
* towards receiving bursts */
int rx_rach_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits,
+ uint16_t nbits, int8_t rssi, float toa)
{
return 0;
}
/*! \brief a single burst was received by the PHY, process it */
int rx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits,
+ uint16_t nbits, int8_t rssi, float toa)
{
return 0;
}
int rx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits,
+ uint16_t nbits, int8_t rssi, float toa)
{
return 0;
}
int rx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits,
+ uint16_t nbits, int8_t rssi, float toa)
{
return 0;
}
int rx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits,
+ uint16_t nbits, int8_t rssi, float toa)
{
return 0;
}
-void _sched_act_rach_det(struct l1sched_trx *l1t, uint8_t tn, uint8_t ss, int activate)
+void _sched_act_rach_det(struct l1sched_trx *l1t, uint8_t tn, uint8_t ss,
+ int activate)
{
}
@@ -524,22 +560,36 @@ void _sched_act_rach_det(struct l1sched_trx *l1t, uint8_t tn, uint8_t ss, int ac
static int vbts_sched_fn(struct gsm_bts *bts, uint32_t fn)
{
struct gsm_bts_trx *trx;
+ uint16_t nbits;
/* send time indication */
+ // update model with new frame number, lot of stuff happening, mesurements of timeslots
+ // saving gsm time in bts model, and more
l1if_mph_time_ind(bts, fn);
/* advance the frame number? */
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
+ // is this the time advance functionality that depends of the distance to the ms???
+ llist_for_each_entry(trx, &bts->trx_list, list)
+ {
struct phy_instance *pinst = trx_phy_instance(trx);
struct l1sched_trx *l1t = &pinst->u.virt.sched;
int tn;
-
+ const ubit_t *bits;
+ // do for each of the 8 timeslots
for (tn = 0; tn < ARRAY_SIZE(l1t->ts); tn++) {
- /* Generate RTS.ind to higher layers */
- _sched_rts(l1t, tn, (fn + RTS_ADVANCE) % GSM_HYPERFRAME);
+ /* Generate RTS indication to higher layers */
+ // ST: This will basically do 2 things (check l1_if:bts_model_l1sap_down):
+ // 1) Get pending messages from layer 2 (from the lapdm queue)
+ // 2) Process the messages
+ // --> Handle and process non-transparent RSL-Messages (activate channel, )
+ // --> Forward transparent RSL-DATA-Messages to the ms by appending them to the l1-dl-queue
+ _sched_rts(l1t, tn,
+ (fn + RTS_ADVANCE) % GSM_HYPERFRAME);
/* schedule transmit backend functions */
- _sched_dl_burst(l1t, tn, fn);
+ // ST: Process data in the l1-dlqueue and forward it to ms
+ // the returned bits are not used here, the routines called will directly forward their bits to the virt um
+ bits = _sched_dl_burst(l1t, tn, fn, &nbits);
+
}
}
@@ -556,25 +606,34 @@ static void vbts_fn_timer_cb(void *data)
gettimeofday(&tv_now, NULL);
- elapsed_us = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000 +
- (tv_now.tv_usec - tv_clock->tv_usec);
+ // check how much time elapsed till the last timer callback call
+ // this value should be about 4.615 ms (a bit greater) as this is the scheduling interval
+ elapsed_us = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000
+ + (tv_now.tv_usec - tv_clock->tv_usec);
- if (elapsed_us > 2*FRAME_DURATION_uS)
- LOGP(DL1P, LOGL_NOTICE, "vbts_fn_timer_cb after %d us\n", elapsed_us);
+ // not so good somehow a lot of time passed between two timer callbacks
+ if (elapsed_us > 2 * FRAME_DURATION_uS)
+ LOGP(DL1P, LOGL_NOTICE, "vbts_fn_timer_cb after %d us\n",
+ elapsed_us);
+ // schedule the current frame/s (fn = frame number)
+ // this loop will be called at least once, but can also be executed
+ // multiple times if more than one frame duration (4615us) passed till the last callback
while (elapsed_us > FRAME_DURATION_uS / 2) {
- const struct timeval tv_frame = {
- .tv_sec = 0,
- .tv_usec = FRAME_DURATION_uS,
- };
+ const struct timeval tv_frame = {.tv_sec = 0, .tv_usec =
+ FRAME_DURATION_uS, };
timeradd(tv_clock, &tv_frame, tv_clock);
+ // increment the frame number in the bts model instance
btsb->vbts.last_fn = (btsb->vbts.last_fn + 1) % GSM_HYPERFRAME;
vbts_sched_fn(bts, btsb->vbts.last_fn);
elapsed_us -= FRAME_DURATION_uS;
}
/* re-schedule the timer */
- osmo_timer_schedule(&btsb->vbts.fn_timer, 0, FRAME_DURATION_uS - elapsed_us);
+ // timer is set to frame duration - elapsed time to guarantee that this cb method will be
+ // periodically executed every 4.615ms
+ osmo_timer_schedule(&btsb->vbts.fn_timer, 0,
+ FRAME_DURATION_uS - elapsed_us);
}
int vbts_sched_start(struct gsm_bts *bts)
@@ -588,6 +647,7 @@ int vbts_sched_start(struct gsm_bts *bts)
btsb->vbts.fn_timer.data = bts;
gettimeofday(&btsb->vbts.tv_clock, NULL);
+ // trigger the first timer after 4615us (a frame duration)
osmo_timer_schedule(&btsb->vbts.fn_timer, 0, FRAME_DURATION_uS);
return 0;
diff --git a/src/osmo-bts-virtual/virtual_um.c b/src/osmo-bts-virtual/virtual_um.c
index 2126b092..e2e86e44 100644
--- a/src/osmo-bts-virtual/virtual_um.c
+++ b/src/osmo-bts-virtual/virtual_um.c
@@ -19,118 +19,44 @@
*
*/
-#include <unistd.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <net/if.h>
-
#include <osmocom/core/select.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/gsmtap.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
#include "virtual_um.h"
+#include "osmo_mcast_sock.h"
-#define VIRT_UM_MSGB_SIZE 256
-
-static int mcast_join_group(int fd, const char *group, const char *netdev)
-{
- int ifindex = 0;
- int rc, af;
- socklen_t af_len = sizeof(af);
-
- if (netdev) {
- ifindex = if_nametoindex(netdev);
- if (!ifindex)
- return -1;
- }
-
- rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &af_len);
- if (rc < 0)
- return rc;
-
- switch (af) {
- case AF_INET:
- {
- struct ip_mreqn mr;
- memset(&mr, 0, sizeof(mr));
- inet_pton(AF_INET, group, &mr.imr_multiaddr);
- if (ifindex)
- mr.imr_ifindex = ifindex;
- rc = setsockopt(fd, SOL_SOCKET, IP_ADD_MEMBERSHIP,
- &mr, sizeof(mr));
- }
- break;
- case AF_INET6:
- {
- struct ipv6_mreq mr;
- memset(&mr, 0, sizeof(mr));
- inet_pton(AF_INET6, group, &mr.ipv6mr_multiaddr);
- if (ifindex)
- mr.ipv6mr_interface = ifindex;
- rc = setsockopt(fd, SOL_SOCKET, IPV6_ADD_MEMBERSHIP,
- &mr, sizeof(mr));
-
- }
- break;
- default:
- rc = -1;
- break;
- }
-
- return rc;
-}
-
-static int mcast_connect(int fd, const char *group, uint16_t port)
-{
- int rc, af;
- socklen_t af_len = sizeof(af);
- struct sockaddr_in sin;
- struct sockaddr_in6 sin6;
-
- rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &af_len);
- if (rc < 0)
- return rc;
-
- switch (af) {
- case AF_INET:
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_port = htons(port);
- inet_pton(AF_INET, group, &sin.sin_addr);
- rc = connect(fd, (struct sockaddr *) &sin, sizeof(sin));
- break;
- case AF_INET6:
- memset(&sin6, 0, sizeof(sin6));
- sin6.sin6_family = AF_INET6;
- sin6.sin6_port = htons(port);
- inet_pton(AF_INET6, group, &sin6.sin6_addr);
- rc = connect(fd, (struct sockaddr *) &sin6, sizeof(sin6));
- break;
- default:
- return -1;
- }
-
- return rc;
-}
-
+/**
+ * Virtual UM interface file descriptor callback.
+ * Should be called by select.c when the fd is ready for reading.
+ */
static int virt_um_fd_cb(struct osmo_fd *ofd, unsigned int what)
{
struct virt_um_inst *vui = ofd->data;
+ // check if the read flag is set
if (what & BSC_FD_READ) {
- struct msgb *msg = msgb_alloc(VIRT_UM_MSGB_SIZE, "Virtual UM Rx");
+ // allocate message buffer of specified size
+ struct msgb *msg = msgb_alloc(VIRT_UM_MSGB_SIZE,
+ "Virtual UM Rx");
int rc;
- rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg));
+ // read message from fd in message buffer
+ rc = mcast_bidir_sock_rx(vui->mcast_sock, msgb_data(msg),
+ msgb_tailroom(msg));
+ // rc is number of bytes actually read
if (rc > 0) {
msgb_put(msg, rc);
+ msg->l1h = msgb_data(msg);
+ // call the l1 callback function for a received msg
vui->recv_cb(vui, msg);
} else {
+ // TODO: this kind of error handling might be a bit harsh
vui->recv_cb(vui, NULL);
+ // Unregister fd from select loop
osmo_fd_unregister(ofd);
close(ofd->fd);
ofd->fd = -1;
@@ -141,70 +67,36 @@ static int virt_um_fd_cb(struct osmo_fd *ofd, unsigned int what)
return 0;
}
-struct virt_um_inst *virt_um_init(void *ctx, const char *group, uint16_t port,
- const char *netdev, void *priv,
- void (*recv_cb)(struct virt_um_inst *vui, struct msgb *msg))
+struct virt_um_inst *virt_um_init(
+ void *ctx, const char *tx_mcast_group, uint16_t tx_mcast_port,
+ const char *rx_mcast_group, uint16_t rx_mcast_port,
+ void (*recv_cb)(struct virt_um_inst *vui, struct msgb *msg))
{
- struct virt_um_inst *vui;
- int fd, rc;
-
- if (!port)
- port = GSMTAP_UDP_PORT;
- if (!group)
- group = "239.0.47.29";
-
- /* crate a socked and bind it to the multicast group. Do NOT
- * specify a fixed port locally, to make stack choose a random
- * free UDP port. */
- fd = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, group,
- 0, OSMO_SOCK_F_BIND);
- if (fd < 0)
- return NULL;
-
- /* join the multicast group */
- rc = mcast_join_group(fd, group, netdev);
- if (rc < 0) {
- close(fd);
- return NULL;
- }
-
- /* and finally also connect */
- rc = mcast_connect(fd, group, port);
- if (rc < 0) {
- close(fd);
- return NULL;
- }
-
- vui = talloc_zero(ctx, struct virt_um_inst);
- vui->priv = priv;
+ struct virt_um_inst *vui = talloc_zero(ctx, struct virt_um_inst);
+ vui->mcast_sock = mcast_bidir_sock_setup(ctx, tx_mcast_group,
+ tx_mcast_port, rx_mcast_group, rx_mcast_port, 1,
+ virt_um_fd_cb, vui);
vui->recv_cb = recv_cb;
- vui->ofd.data = vui;
- vui->ofd.fd = fd;
- vui->ofd.when = BSC_FD_READ;
- vui->ofd.cb = virt_um_fd_cb;
-
- osmo_fd_register(&vui->ofd);
return vui;
+
}
void virt_um_destroy(struct virt_um_inst *vui)
{
- struct osmo_fd *ofd = &vui->ofd;
-
- osmo_fd_unregister(ofd);
- close(ofd->fd);
- ofd->fd = -1;
- ofd->when = 0;
-
+ mcast_bidir_sock_close(vui->mcast_sock);
talloc_free(vui);
}
+/**
+ * Write msg to to multicast socket and free msg afterwards
+ */
int virt_um_write_msg(struct virt_um_inst *vui, struct msgb *msg)
{
int rc;
- rc = write(vui->ofd.fd, msgb_data(msg), msgb_length(msg));
+ rc = mcast_bidir_sock_tx(vui->mcast_sock, msgb_data(msg),
+ msgb_length(msg));
msgb_free(msg);
return rc;
diff --git a/src/osmo-bts-virtual/virtual_um.h b/src/osmo-bts-virtual/virtual_um.h
index 65292211..6b782af4 100644
--- a/src/osmo-bts-virtual/virtual_um.h
+++ b/src/osmo-bts-virtual/virtual_um.h
@@ -2,16 +2,24 @@
#include <osmocom/core/select.h>
#include <osmocom/core/msgb.h>
+#include "osmo_mcast_sock.h"
+
+#define VIRT_UM_MSGB_SIZE 256
+#define DEFAULT_MS_MCAST_GROUP "224.0.0.1"
+#define DEFAULT_MS_MCAST_PORT 6666
+#define DEFAULT_BTS_MCAST_GROUP "225.0.0.1"
+#define DEFAULT_BTS_MCAST_PORT 6667
struct virt_um_inst {
void *priv;
- struct osmo_fd ofd;
+ struct mcast_bidir_sock *mcast_sock;
void (*recv_cb)(struct virt_um_inst *vui, struct msgb *msg);
};
-struct virt_um_inst *virt_um_init(void *ctx, const char *group, uint16_t port,
- const char *netdev, void *priv,
- void (*recv_cb)(struct virt_um_inst *vui, struct msgb *msg));
+struct virt_um_inst *virt_um_init(
+ void *ctx, const char *tx_mcast_group, uint16_t tx_mcast_port,
+ const char *rx_mcast_group, uint16_t rx_mcast_port,
+ void (*recv_cb)(struct virt_um_inst *vui, struct msgb *msg));
void virt_um_destroy(struct virt_um_inst *vui);
diff --git a/src/osmo-bts-virtual/virtualbts_vty.c b/src/osmo-bts-virtual/virtualbts_vty.c
index e8c97acc..83a32f67 100644
--- a/src/osmo-bts-virtual/virtualbts_vty.c
+++ b/src/osmo-bts-virtual/virtualbts_vty.c
@@ -63,63 +63,105 @@ void bts_model_config_write_phy(struct vty *vty, struct phy_link *plink)
{
if (plink->u.virt.mcast_dev)
vty_out(vty, " virtual-um net-device %s%s",
- plink->u.virt.mcast_dev, VTY_NEWLINE);
- if (plink->u.virt.mcast_group)
- vty_out(vty, " virtual-um multicast-group %s%s",
- plink->u.virt.mcast_group, VTY_NEWLINE);
- if (plink->u.virt.mcast_port)
- vty_out(vty, " virtual-um udp-port %u%s",
- plink->u.virt.mcast_port, VTY_NEWLINE);
+ plink->u.virt.mcast_dev, VTY_NEWLINE);
+ if (plink->u.virt.ms_mcast_group)
+ vty_out(vty, " virtual-um ms-multicast-group %s%s",
+ plink->u.virt.ms_mcast_group, VTY_NEWLINE);
+ if (plink->u.virt.ms_mcast_port)
+ vty_out(vty, " virtual-um ms-udp-port %u%s",
+ plink->u.virt.ms_mcast_port, VTY_NEWLINE);
+ if (plink->u.virt.bts_mcast_group)
+ vty_out(vty, " virtual-um bts-multicast-group %s%s",
+ plink->u.virt.bts_mcast_group, VTY_NEWLINE);
+ if (plink->u.virt.bts_mcast_port)
+ vty_out(vty, " virtual-um bts-udp-port %u%s",
+ plink->u.virt.bts_mcast_port, VTY_NEWLINE);
+
}
#define VUM_STR "Virtual Um layer\n"
-DEFUN(cfg_phy_mcast_group, cfg_phy_mcast_group_cmd,
- "virtual-um multicast-group GROUP",
- VUM_STR "Configure the multicast group\n")
+DEFUN(cfg_phy_ms_mcast_group, cfg_phy_ms_mcast_group_cmd,
+ "virtual-um ms-multicast-group GROUP",
+ VUM_STR "Configure the MS multicast group\n")
+{
+ struct phy_link *plink = vty->index;
+
+ if (plink->state != PHY_LINK_SHUTDOWN) {
+ vty_out(vty, "Can only reconfigure a PHY link that is down%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (plink->u.virt.ms_mcast_group)
+ talloc_free(plink->u.virt.ms_mcast_group);
+ plink->u.virt.ms_mcast_group = talloc_strdup(plink, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_phy_ms_mcast_port, cfg_phy_ms_mcast_port_cmd,
+ "virtual-um ms-udp-port <0-65535>",
+ VUM_STR "Configure the MS UDP port\n")
{
struct phy_link *plink = vty->index;
if (plink->state != PHY_LINK_SHUTDOWN) {
vty_out(vty, "Can only reconfigure a PHY link that is down%s",
- VTY_NEWLINE);
+ VTY_NEWLINE);
return CMD_WARNING;
}
- if (plink->u.virt.mcast_group)
- talloc_free(plink->u.virt.mcast_group);
- plink->u.virt.mcast_group = talloc_strdup(plink, argv[0]);
+ plink->u.virt.ms_mcast_port = atoi(argv[0]);
return CMD_SUCCESS;
}
+DEFUN(cfg_phy_bts_mcast_group, cfg_phy_bts_mcast_group_cmd,
+ "virtual-um bts-multicast-group GROUP",
+ VUM_STR "Configure the BTS multicast group\n")
+{
+ struct phy_link *plink = vty->index;
+
+ if (plink->state != PHY_LINK_SHUTDOWN) {
+ vty_out(vty, "Can only reconfigure a PHY link that is down%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (plink->u.virt.bts_mcast_group)
+ talloc_free(plink->u.virt.bts_mcast_group);
+ plink->u.virt.bts_mcast_group = talloc_strdup(plink, argv[0]);
+
+ return CMD_SUCCESS;
+}
-DEFUN(cfg_phy_mcast_port, cfg_phy_mcast_port_cmd,
- "virtual-um udp-port <0-65535>",
- VUM_STR "Configure the UDP port\n")
+DEFUN(cfg_phy_bts_mcast_port, cfg_phy_bts_mcast_port_cmd,
+ "virtual-um bts-udp-port <0-65535>",
+ VUM_STR "Configure the BTS UDP port\n")
{
struct phy_link *plink = vty->index;
if (plink->state != PHY_LINK_SHUTDOWN) {
vty_out(vty, "Can only reconfigure a PHY link that is down%s",
- VTY_NEWLINE);
+ VTY_NEWLINE);
return CMD_WARNING;
}
- plink->u.virt.mcast_port = atoi(argv[0]);
+ plink->u.virt.bts_mcast_port = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_phy_mcast_dev, cfg_phy_mcast_dev_cmd,
- "virtual-um net-device NETDEV",
- VUM_STR "Configure the network device\n")
+ "virtual-um net-device NETDEV",
+ VUM_STR "Configure the network device\n")
{
struct phy_link *plink = vty->index;
if (plink->state != PHY_LINK_SHUTDOWN) {
vty_out(vty, "Can only reconfigure a PHY link that is down%s",
- VTY_NEWLINE);
+ VTY_NEWLINE);
return CMD_WARNING;
}
@@ -134,8 +176,10 @@ int bts_model_vty_init(struct gsm_bts *bts)
{
vty_bts = bts;
- install_element(PHY_NODE, &cfg_phy_mcast_group_cmd);
- install_element(PHY_NODE, &cfg_phy_mcast_port_cmd);
+ install_element(PHY_NODE, &cfg_phy_ms_mcast_group_cmd);
+ install_element(PHY_NODE, &cfg_phy_ms_mcast_port_cmd);
+ install_element(PHY_NODE, &cfg_phy_bts_mcast_group_cmd);
+ install_element(PHY_NODE, &cfg_phy_bts_mcast_port_cmd);
install_element(PHY_NODE, &cfg_phy_mcast_dev_cmd);
return 0;