From 58d79e88ad58887c92ecc98024f537e3efbb9469 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 7 Mar 2020 20:32:08 +0100 Subject: osmo-bts-virtual: implement GSMTAP_CHANNEL_VOICE GSMTAP_CHANNEL_VOICE is the mechanism by which GSMTAP can [finally!] be used to transport circuit-switched voice codec payload, and not just signalling. Original patch by Neels Hofmeyr, heavily extended by Harald Welte. Depends: libosmocore.git I952044a17334f35712e087dc41781805000aebc1 Change-Id: I1cd9a251ce0b87181a0822d7940bbfc9f1428543 --- src/osmo-bts-virtual/l1_if.c | 20 ++++++--- src/osmo-bts-virtual/scheduler_virtbts.c | 71 +++++++++++++++++++++++++++++--- 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/src/osmo-bts-virtual/l1_if.c b/src/osmo-bts-virtual/l1_if.c index ab2cb762..b6a3507d 100644 --- a/src/osmo-bts-virtual/l1_if.c +++ b/src/osmo-bts-virtual/l1_if.c @@ -128,12 +128,7 @@ static void virt_um_rcv_cb(struct virt_um_inst *vui, struct msgb *msg) break; case GSMTAP_CHANNEL_TCH_F: case GSMTAP_CHANNEL_TCH_H: -#if 0 - /* TODO: handle voice messages */ - if (!facch && ! tch_acch) { - osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_INDICATION, msg); - } -#endif + /* This is TCH signalling, for voice frames see GSMTAP_CHANNEL_VOICE */ case GSMTAP_CHANNEL_SDCCH4: case GSMTAP_CHANNEL_SDCCH8: case GSMTAP_CHANNEL_PACCH: @@ -151,6 +146,19 @@ static void virt_um_rcv_cb(struct virt_um_inst *vui, struct msgb *msg) l1sap.u.data.pdch_presence_info = PRES_INFO_BOTH; l1if_process_meas_res(pinst->trx, timeslot, fn, chan_nr, 0, 0, 0, 0); break; + case GSMTAP_CHANNEL_VOICE_F: + case GSMTAP_CHANNEL_VOICE_H: + /* the first byte indicates the type of voice codec (gsmtap_um_voice_type) */ + msg->l2h = msgb_pull(msg, 1); + osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_INDICATION, msg); + l1sap.u.tch.chan_nr = chan_nr; + l1sap.u.tch.fn = fn; + l1sap.u.tch.rssi = 0; /* Radio Signal Strength Indicator. Best -> 0 */ + l1sap.u.tch.ber10k = 0; /* Bit Error Rate in 0.01%. Best -> 0 */ + l1sap.u.tch.ta_offs_256bits = 0; /* Burst time of arrival in quarter bits. Probably used for Timing Advance calc. Best -> 0 */ + l1sap.u.tch.lqual_cb = 10 * signal_dbm; /* Link quality in centiBel = 10 * dB. */ + l1if_process_meas_res(pinst->trx, timeslot, fn, chan_nr, 0, 0, 0, 0); + break; case GSMTAP_CHANNEL_AGCH: case GSMTAP_CHANNEL_PCH: case GSMTAP_CHANNEL_BCCH: diff --git a/src/osmo-bts-virtual/scheduler_virtbts.c b/src/osmo-bts-virtual/scheduler_virtbts.c index 90288d1a..72a68744 100644 --- a/src/osmo-bts-virtual/scheduler_virtbts.c +++ b/src/osmo-bts-virtual/scheduler_virtbts.c @@ -52,8 +52,8 @@ * This will at first wrap the msg with a GSMTAP header and then write it to the declared multicast socket. * TODO: we might want to remove unused argument uint8_t tn */ -static void tx_to_virt_um(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, struct msgb *msg) +static void _tx_to_virt_um(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, struct msgb *msg, bool is_voice_frame) { const struct trx_chan_desc *chdesc = &trx_chan_desc[chan]; struct msgb *outmsg; /* msg to send with gsmtap header prepended */ @@ -76,7 +76,7 @@ static void tx_to_virt_um(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, l1sap_fn2ccch_block(fn) >= num_agch(l1t->trx, "PH-DATA-REQ")) gsmtap_chantype = GSMTAP_CHANNEL_PCH; else - gsmtap_chantype = chantype_rsl2gsmtap(rsl_chantype, chdesc->link_id); /* the logical channel type */ + gsmtap_chantype = chantype_rsl2gsmtap2(rsl_chantype, chdesc->link_id, is_voice_frame); /* the logical channel type */ #if MODULO_HYPERFRAME /* Restart fn after every superframe (26 * 51 frames) to simulate hyperframe overflow each 6 seconds. */ @@ -105,6 +105,65 @@ static void tx_to_virt_um(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, msgb_free(msg); } +static void tx_to_virt_um(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, struct msgb *msg) +{ + _tx_to_virt_um(l1t, tn, fn, chan, msg, false); +} + + +static struct gsm_lchan *lchan_from_l1t(struct l1sched_trx *l1t, uint8_t tn, enum trx_chan_type chan) +{ + struct gsm_bts_trx_ts *ts; + uint8_t subslot = 0; + + OSMO_ASSERT(l1t && l1t->trx); + + if (chan == TRXC_TCHH_1) + subslot = 1; + + ts = &l1t->trx->ts[tn]; + return &ts->lchan[subslot]; +} + +/* Determine the gsmtap_um_voice_type of a gsm_lchan */ +static int get_um_voice_type(const struct gsm_lchan *lchan) +{ + switch (lchan->tch_mode) { + case GSM48_CMODE_SPEECH_V1: + if (lchan->type == GSM_LCHAN_TCH_H) + return GSMTAP_UM_VOICE_HR; + else + return GSMTAP_UM_VOICE_FR; + case GSM48_CMODE_SPEECH_EFR: + return GSMTAP_UM_VOICE_EFR; + case GSM48_CMODE_SPEECH_AMR: + return GSMTAP_UM_VOICE_AMR; + default: + return -1; + } +} + +static void tx_to_virt_um_voice_frame(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, struct msgb *msg) +{ + struct gsm_lchan *lchan = lchan_from_l1t(l1t, tn, chan); + int um_voice_type; + + OSMO_ASSERT(lchan); + um_voice_type = get_um_voice_type(lchan); + if (um_voice_type < 0) { + LOGPLCHAN(lchan, DL1P, LOGL_ERROR, "Cannot determine Um voice type from lchan\n"); + um_voice_type = 0xff; + } + + /* the first byte indicates the type of voice codec (gsmtap_um_voice_type) */ + msgb_pull_to_l2(msg); + msgb_push_u8(msg, um_voice_type); + msg->l2h = msg->data; + _tx_to_virt_um(l1t, tn, fn, chan, msg, true); +} + /* * TX on downlink */ @@ -414,8 +473,8 @@ ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, if (msg_facch) { tx_to_virt_um(l1t, tn, fn, chan, msg_facch); msgb_free(msg_tch); - } else - tx_to_virt_um(l1t, tn, fn, chan, msg_tch); + } else if (msg_tch) + tx_to_virt_um_voice_frame(l1t, tn, fn, chan, msg_tch); send_burst: @@ -456,7 +515,7 @@ ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, tx_to_virt_um(l1t, tn, fn, chan, msg_facch); msgb_free(msg_tch); } else if (msg_tch) - tx_to_virt_um(l1t, tn, fn, chan, msg_tch); + tx_to_virt_um_voice_frame(l1t, tn, fn, chan, msg_tch); send_burst: return NULL; -- cgit v1.2.3