aboutsummaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
authorMax <msuraev@sysmocom.de>2016-10-03 17:37:45 +0200
committerHarald Welte <laforge@gnumonks.org>2016-10-13 06:58:06 +0000
commitbabd05661d13b12234e848acf9c4bff909ef05f4 (patch)
tree656168076a94b5a430d2b0c1d856011e3b4c6a4d /src/common
parentc09e5a44c3c1c2882339fe8822f373b1e12839ae (diff)
DTX DL: use FSM for AMR
Use dedicated FSM to handle all DTX DL related events: - add explicit checks if DTX DL is enabled (fixes regression for non-DTX setup introduced in 654175f33bd412671e3ef8cdd65c0689d10f278c) - fix handling of AMR CMI for SPEECH frames - add FSM for DTX DL - sync with corresponding changes in OpenBSC's - handle FACCH-related DTX ONSET events This affects both lc15 and sysmobts and requires corresponding change in OpenBSC (Change-Id: Idac8609faf9b5ced818fde899ccfc6ed0c42e8fd). Change-Id: I74a0b42cb34d525b8a70d264135e82994ca70d31
Diffstat (limited to 'src/common')
-rw-r--r--src/common/Makefile.am3
-rw-r--r--src/common/dtx_dl_amr_fsm.c320
-rw-r--r--src/common/l1sap.c24
-rw-r--r--src/common/msg_utils.c135
-rw-r--r--src/common/rsl.c4
5 files changed, 430 insertions, 56 deletions
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 856f7412..dd368d03 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -7,6 +7,7 @@ libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \
rsl.c vty.c paging.c measurement.c amr.c lchan.c \
load_indication.c pcu_sock.c handover.c msg_utils.c \
tx_power.c bts_ctrl_commands.c bts_ctrl_lookup.c \
- l1sap.c cbch.c power_control.c main.c phy_link.c
+ l1sap.c cbch.c power_control.c main.c phy_link.c \
+ dtx_dl_amr_fsm.c
libl1sched_a_SOURCES = scheduler.c
diff --git a/src/common/dtx_dl_amr_fsm.c b/src/common/dtx_dl_amr_fsm.c
new file mode 100644
index 00000000..b110cf21
--- /dev/null
+++ b/src/common/dtx_dl_amr_fsm.c
@@ -0,0 +1,320 @@
+/* DTX DL AMR FSM */
+
+/* (C) 2016 by sysmocom s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmo-bts/dtx_dl_amr_fsm.h>
+#include <osmo-bts/logging.h>
+
+void dtx_fsm_voice(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_VOICE:
+ case E_FACCH:
+ break;
+ case E_SID_F:
+ case E_SID_U:
+ osmo_fsm_inst_state_chg(fi, ST_SID_F1, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Inexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_sid_f1(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_SID_F:
+/* FIXME: what shall we do if we get SID-FIRST _again_ (twice in a row)?
+ Was observed during testing, let's just ignore it for now */
+ break;
+ case E_SID_U:
+ osmo_fsm_inst_state_chg(fi, ST_SID_U, 0, 0);
+ break;
+ case E_VOICE:
+ osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
+ break;
+ case E_FACCH:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_F, 0, 0);
+ break;
+ case E_COMPL:
+ osmo_fsm_inst_state_chg(fi, ST_SID_F2, 0, 0);
+ break;
+ case E_INHIB:
+ osmo_fsm_inst_state_chg(fi, ST_F1_INH, 0, 0);
+ break;
+ case E_ONSET:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_V, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_sid_f2(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_SID_U:
+ osmo_fsm_inst_state_chg(fi, ST_SID_U, 0, 0);
+ break;
+ case E_VOICE:
+ osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
+ break;
+ case E_FACCH:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_F, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_f1_inh(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_VOICE:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_V, 0, 0);
+ break;
+ case E_FACCH:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_F, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_u_inh(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_VOICE:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_V, 0, 0);
+ break;
+ case E_FACCH:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_F, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_sid_upd(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_FACCH:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_F, 0, 0);
+ break;
+ case E_VOICE:
+ osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
+ break;
+ case E_INHIB:
+ osmo_fsm_inst_state_chg(fi, ST_U_INH, 0, 0);
+ break;
+ case E_SID_U:
+ case E_SID_F:
+/* FIXME: what shall we do if we get SID-FIRST _after_ sending SID-UPDATE?
+ Was observed during testing, let's just ignore it for now */
+ break;
+ case E_ONSET:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_V, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_onset_v(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_SID_U:
+ case E_VOICE:
+ osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
+ break;
+ case E_FACCH:
+ osmo_fsm_inst_state_chg(fi, ST_FACCH_V, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_onset_f(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_SID_U:
+ break;
+ case E_VOICE:
+ osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
+ break;
+ case E_FACCH:
+ osmo_fsm_inst_state_chg(fi, ST_FACCH, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_facch_v(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_FACCH:
+ break;
+ case E_VOICE:
+ osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
+ break;
+ case E_SID_U:
+ break;
+ case E_SID_F:
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_facch(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_FACCH:
+ break;
+ case E_VOICE:
+ osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
+ break;
+ case E_SID_U:
+ case E_SID_F:
+ osmo_fsm_inst_state_chg(fi, ST_SID_F1, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+static struct osmo_fsm_state dtx_dl_amr_fsm_states[] = {
+ /* default state for non-DTX and DTX when SPEECH is in progress */
+ [ST_VOICE] = {
+ .in_event_mask = X(E_SID_F) | X(E_SID_U) | X(E_VOICE) | X(E_FACCH),
+ .out_state_mask = X(ST_SID_F1),
+ .name = "Voice",
+ .action = dtx_fsm_voice,
+ },
+ /* SID-FIRST or SID-FIRST-P1 in case of AMR HR:
+ start of silence period (might be interrupted in case of AMR HR) */
+ [ST_SID_F1]= {
+ .in_event_mask = X(E_SID_F) | X(E_SID_U) | X(E_VOICE) | X(E_FACCH) | X(E_COMPL) | X(E_INHIB) | X(E_ONSET),
+ .out_state_mask = X(ST_SID_U) | X(ST_VOICE) | X(ST_ONSET_F) | X(ST_SID_F2) | X(ST_F1_INH) | X(ST_ONSET_V),
+ .name = "SID-FIRST (P1)",
+ .action = dtx_fsm_sid_f1,
+ },
+ /* SID-FIRST P2 (only for AMR HR):
+ actual start of silence period in case of AMR HR*/
+ [ST_SID_F2]= {
+ .in_event_mask = X(E_SID_U) | X(E_VOICE) | X(E_FACCH),
+ .out_state_mask = X(ST_SID_U) | X(ST_VOICE) | X(ST_ONSET_F),
+ .name = "SID-FIRST (P2)",
+ .action = dtx_fsm_sid_f2,
+ },
+ /* SID-FIRST Inhibited:
+ incoming SPEECH or FACCH (only for AMR HR) */
+ [ST_F1_INH]= {
+ .in_event_mask = X(E_VOICE) | X(E_FACCH),
+ .out_state_mask = X(ST_VOICE) | X(ST_FACCH_V),
+ .name = "SID-FIRST (Inh)",
+ .action = dtx_fsm_f1_inh,
+ },
+ /* SID-UPDATE Inhibited:
+ incoming SPEECH or FACCH (only for AMR HR) */
+ [ST_U_INH]= {
+ .in_event_mask = X(E_VOICE) | X(E_FACCH),
+ .out_state_mask = X(ST_VOICE) | X(ST_FACCH_V),
+ .name = "SID-UPDATE (Inh)",
+ .action = dtx_fsm_u_inh,
+ },
+ /* Silence period with periodic comfort noise data updates */
+ [ST_SID_U]= {
+ .in_event_mask = X(E_FACCH) | X(E_VOICE) | X(E_INHIB) | X(E_SID_U) | X(E_SID_F) | X(E_ONSET),
+ .out_state_mask = X(ST_ONSET_F) | X(ST_VOICE) | X(ST_U_INH) | X(ST_SID_U) | X(ST_ONSET_V),
+ .name = "SID-UPDATE",
+ .action = dtx_fsm_sid_upd,
+ },
+ /* ONSET - end of silent period due to incoming SPEECH frame */
+ [ST_ONSET_V]= {
+ .in_event_mask = X(E_VOICE) | X(E_FACCH),
+ .out_state_mask = X(ST_VOICE) | X(ST_FACCH_V),
+ .name = "ONSET (SPEECH)",
+ .action = dtx_fsm_onset_v,
+ },
+ /* ONSET - end of silent period due to incoming FACCH frame */
+ [ST_ONSET_F]= {
+ .in_event_mask = X(E_VOICE) | X(E_FACCH) | X(E_SID_U),
+ .out_state_mask = X(ST_VOICE) | X(ST_FACCH),
+ .name = "ONSET (FACCH)",
+ .action = dtx_fsm_onset_f,
+ },
+ /* FACCH sending state: SPEECH was observed before so once we're done
+ FSM should get back to VOICE state */
+ [ST_FACCH_V]= {
+ .in_event_mask = X(E_FACCH) | X(E_VOICE) | X(E_SID_U) | X(E_SID_F),
+ .out_state_mask = X(ST_FACCH_V) | X(ST_VOICE) | X(ST_SID_F1),
+ .name = "FACCH (SPEECH)",
+ .action = dtx_fsm_facch_v,
+ },
+ /* FACCH sending state: no SPEECH was observed before so once we're done
+ FSM should get back to silent period via SID-FIRST */
+ [ST_FACCH]= {
+ .in_event_mask = X(E_FACCH) | X(E_VOICE) | X(E_SID_U) | X(E_SID_F),
+ .out_state_mask = X(ST_FACCH_V) | X(ST_VOICE) | X(ST_SID_F1),
+ .name = "FACCH",
+ .action = dtx_fsm_facch,
+ },
+};
+
+const struct value_string dtx_dl_amr_fsm_event_names[] = {
+ { E_VOICE, "Voice" },
+ { E_FACCH, "FACCH" },
+ { E_COMPL, "Complete P1 -> P2" },
+ { E_INHIB, "Inhibit" },
+ { E_SID_F, "SID-FIRST" },
+ { E_SID_U, "SID-UPDATE" },
+ { 0, NULL }
+};
+
+struct osmo_fsm dtx_dl_amr_fsm = {
+ .name = "DTX DL AMR FSM",
+ .states = dtx_dl_amr_fsm_states,
+ .num_states = ARRAY_SIZE(dtx_dl_amr_fsm_states),
+ .event_names = dtx_dl_amr_fsm_event_names,
+ .log_subsys = DL1C,
+};
diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index dd234eec..f3e620ee 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -39,6 +39,7 @@
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/l1sap.h>
+#include <osmo-bts/dtx_dl_amr_fsm.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/measurement.h>
#include <osmo-bts/bts.h>
@@ -524,6 +525,7 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
uint8_t *p, *si;
struct lapdm_entity *le;
struct osmo_phsap_prim pp;
+ bool dtxd_facch = false;
int rc;
chan_nr = rts_ind->chan_nr;
@@ -578,8 +580,11 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
p[0] = lchan->ms_power_ctrl.current;
p[1] = lchan->rqd_ta;
le = &lchan->lapdm_ch.lapdm_acch;
- } else
+ } else {
+ if (lchan->ts->trx->bts->dtxd)
+ dtxd_facch = true;
le = &lchan->lapdm_ch.lapdm_dcch;
+ }
rc = lapdm_phsap_dequeue_prim(le, &pp);
if (rc < 0) {
if (L1SAP_IS_LINK_SACCH(link_id)) {
@@ -605,6 +610,10 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
memcpy(p, pp.oph.msg->data, GSM_MACBLOCK_LEN);
/* check if it is a RR CIPH MODE CMD. if yes, enable RX ciphering */
check_for_ciph_cmd(pp.oph.msg, lchan, chan_nr);
+ if (dtxd_facch && lchan->tch.dtx.dl_amr_fsm)
+ osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
+ E_FACCH,
+ (void *)lchan);
}
msgb_free(pp.oph.msg);
}
@@ -1132,14 +1141,27 @@ int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *
rc = l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_ACTIVATE, 0);
if (rc)
return -RSL_ERR_EQUIPMENT_FAIL;
+
+ /* Init DTX DL FSM if necessary */
+ //FIXME: only do it for AMR TCH/*
+ osmo_fsm_register(&dtx_dl_amr_fsm);
+ lchan->tch.dtx.dl_amr_fsm = osmo_fsm_inst_alloc(&dtx_dl_amr_fsm,
+ tall_bts_ctx, lchan,
+ LOGL_DEBUG, lchan->name);
return 0;
}
int l1sap_chan_rel(struct gsm_bts_trx *trx, uint8_t chan_nr)
{
+ struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
LOGP(DL1P, LOGL_INFO, "deactivating channel chan_nr=0x%02x trx=%d\n",
chan_nr, trx->nr);
+ if (lchan->tch.dtx.dl_amr_fsm) {
+ osmo_fsm_inst_free(lchan->tch.dtx.dl_amr_fsm);
+ lchan->tch.dtx.dl_amr_fsm = NULL;
+ }
+
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE,
0);
}
diff --git a/src/common/msg_utils.c b/src/common/msg_utils.c
index 99a211fd..f9853c57 100644
--- a/src/common/msg_utils.c
+++ b/src/common/msg_utils.c
@@ -17,6 +17,7 @@
*
*/
+#include <osmo-bts/dtx_dl_amr_fsm.h>
#include <osmo-bts/msg_utils.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/oml.h>
@@ -26,6 +27,7 @@
#include <osmocom/gsm/protocol/gsm_12_21.h>
#include <osmocom/gsm/abis_nm.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/fsm.h>
#include <osmocom/trau/osmo_ortp.h>
#include <arpa/inet.h>
@@ -92,90 +94,119 @@ static int check_manuf(struct msgb *msg, struct abis_om_hdr *omh, size_t msg_siz
void lchan_set_marker(bool t, struct gsm_lchan *lchan)
{
if (t)
- lchan->tch.ul_sid = true;
- else if (lchan->tch.ul_sid) {
- lchan->tch.ul_sid = false;
+ lchan->tch.dtx.ul_sid = true;
+ else if (lchan->tch.dtx.ul_sid) {
+ lchan->tch.dtx.ul_sid = false;
lchan->rtp_tx_marker = true;
}
}
-
/*! \brief Store the last SID frame in lchan context
* \param[in] lchan Logical channel on which we check scheduling
* \param[in] l1_payload buffer with SID data
* \param[in] length length of l1_payload
* \param[in] fn Frame Number for which we check scheduling
* \param[in] update 0 if SID_FIRST, 1 if SID_UPDATE, -1 if not AMR SID
- * \param[in] cmr Codec Mode Request (AMR-specific, ignored otherwise)
- * \param[in] cmi Codec Mode Indication (AMR-specific, ignored otherwise)
*/
-void save_last_sid(struct gsm_lchan *lchan, const uint8_t *l1_payload,
- size_t length, uint32_t fn, int update, uint8_t cmr,
- int8_t cmi)
+void dtx_cache_payload(struct gsm_lchan *lchan, const uint8_t *l1_payload,
+ size_t length, uint32_t fn, int update)
{
size_t amr = (update < 0) ? 0 : 2,
- copy_len = OSMO_MIN(length + 1, ARRAY_SIZE(lchan->tch.last_sid.buf));
+ copy_len = OSMO_MIN(length + 1, ARRAY_SIZE(lchan->tch.dtx.cache));
- lchan->tch.last_sid.len = copy_len + amr;
- lchan->tch.last_sid.fn = fn;
- lchan->tch.last_sid.is_update = update;
+ lchan->tch.dtx.len = copy_len + amr;
+ lchan->tch.dtx.fn = fn;
+ lchan->tch.dtx.is_update = update;
- if (amr)
- amr_set_mode_pref(lchan->tch.last_sid.buf, &lchan->tch.amr_mr,
- cmi, cmr);
- memcpy(lchan->tch.last_sid.buf + amr, l1_payload, copy_len);
+ memcpy(lchan->tch.dtx.cache + amr, l1_payload, copy_len);
}
-/*! \brief Check current and cached SID to decide if talkspurt takes place
+/*! \brief Check current state of DTX DL AMR FSM and dispatch necessary events
* \param[in] lchan Logical channel on which we check scheduling
* \param[in] rtp_pl buffer with RTP data
* \param[in] rtp_pl_len length of rtp_pl
* \param[in] fn Frame Number for which we check scheduling
* \param[in] l1_payload buffer where CMR and CMI prefix should be added
+ * \param[out] len Length of expected L1 payload
* \param[out] ft_out Frame Type to be populated after decoding
- * \returns 0 if frame should be send immediately (2 byte CMR,CMI prefix added:
- * caller must adjust length as necessary),
- * 1 if ONSET event is detected
- * negative if no sending is necessary (either error or cached SID
- * UPDATE)
+ * \returns 0 in case of success; negative on error
*/
-int dtx_amr_check_onset(struct gsm_lchan *lchan, const uint8_t *rtp_pl,
+int dtx_dl_amr_fsm_step(struct gsm_lchan *lchan, const uint8_t *rtp_pl,
size_t rtp_pl_len, uint32_t fn, uint8_t *l1_payload,
- uint8_t *ft_out)
+ bool marker, uint8_t *len, uint8_t *ft_out)
{
uint8_t cmr;
enum osmo_amr_type ft;
enum osmo_amr_quality bfi;
int8_t sti, cmi;
- osmo_amr_rtp_dec(rtp_pl, rtp_pl_len, &cmr, &cmi, &ft, &bfi, &sti);
- *ft_out = ft; /* only needed for old sysmo firmware */
+ int rc;
- if (ft == AMR_SID) {
- save_last_sid(lchan, rtp_pl, rtp_pl_len, fn, sti, cmr, cmi);
- if (sti) /* SID_UPDATE should be cached and send later */
- return -EAGAIN;
- else { /* SID_FIRST - cached and send right away */
- amr_set_mode_pref(l1_payload, &lchan->tch.amr_mr, cmi,
- cmr);
- return 0;
- }
+ if (rtp_pl == NULL) { /* SID-FIRST P1 -> P2 */
+ *len = 3;
+ memcpy(l1_payload, lchan->tch.dtx.cache, 2);
+ osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, E_COMPL,
+ (void *)lchan);
+ return 0;
}
- if (ft != AMR_NO_DATA && !osmo_amr_is_speech(ft)) {
- LOGP(DRTP, LOGL_ERROR, "unsupported AMR FT 0x%02x\n", ft);
- return -ENOTSUP;
+ rc = osmo_amr_rtp_dec(rtp_pl, rtp_pl_len, &cmr, &cmi, &ft, &bfi, &sti);
+ if (rc < 0) {
+ LOGP(DRTP, LOGL_ERROR, "failed to decode AMR RTP (length %zu)\n",
+ rtp_pl_len);
+ return rc;
}
+ /* only needed for old sysmo firmware: */
+ *ft_out = ft;
+
+ /* CMI in downlink tells the L1 encoder which encoding function
+ * it will use, so we have to use the frame type */
+ if (osmo_amr_is_speech(ft))
+ cmi = ft;
+
+ /* populate L1 payload with CMR/CMI - might be ignored by caller: */
+ amr_set_mode_pref(l1_payload, &lchan->tch.amr_mr, cmi, cmr);
+
+ /* populate DTX cache with CMR/CMI - overwrite cache which will be
+ either updated or invalidated by caller anyway: */
+ amr_set_mode_pref(lchan->tch.dtx.cache, &lchan->tch.amr_mr, cmi, cmr);
+ *len = 3 + rtp_pl_len;
+
+ /* DTX DL is not enabled, move along */
+ if (!lchan->ts->trx->bts->dtxd)
+ return 0;
+
if (osmo_amr_is_speech(ft)) {
- if (lchan->tch.last_sid.len) { /* force ONSET */
- lchan->tch.last_sid.len = 0;
- return 1;
- }
- /* We received AMR SPEECH frame - invalidate saved SID */
- lchan->tch.last_sid.len = 0;
- amr_set_mode_pref(l1_payload, &lchan->tch.amr_mr, cmi, cmr);
+ if (lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1 ||
+ lchan->tch.dtx.dl_amr_fsm->state == ST_SID_U) /* AMR HR */
+ if (lchan->type == GSM_LCHAN_TCH_H && marker)
+ return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
+ E_INHIB,
+ (void *)lchan);
+ /* AMR FR */
+ if (marker && lchan->tch.dtx.dl_amr_fsm->state == ST_SID_U)
+ return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
+ E_ONSET, (void *)lchan);
+ return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, E_VOICE,
+ (void *)lchan);
+ }
+
+ if (ft == AMR_SID) {
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, sti);
+ return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
+ sti ? E_SID_U : E_SID_F,
+ (void *)lchan);
+ }
+
+ if (ft != AMR_NO_DATA) {
+ LOGP(DRTP, LOGL_ERROR, "unsupported AMR FT 0x%02x\n", ft);
+ return -ENOTSUP;
}
+ if (marker)
+ osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, E_VOICE,
+ (void *)lchan);
+ *len = 0;
return 0;
}
@@ -188,10 +219,10 @@ static inline bool dtx_amr_sid_optional(const struct gsm_lchan *lchan,
uint32_t fn)
{
/* Compute approx. time delta based on Fn duration */
- uint32_t delta = GSM_FN_TO_MS(fn - lchan->tch.last_sid.fn);
+ uint32_t delta = GSM_FN_TO_MS(fn - lchan->tch.dtx.fn);
/* according to 3GPP TS 26.093 A.5.1.1: */
- if (lchan->tch.last_sid.is_update) {
+ if (lchan->tch.dtx.is_update) {
/* SID UPDATE should be repeated every 8th RTP frame */
if (delta < GSM_RTP_FRAME_DURATION_MS * 8)
return true;
@@ -252,10 +283,10 @@ uint8_t repeat_last_sid(struct gsm_lchan *lchan, uint8_t *dst, uint32_t fn)
if (dtx_amr_sid_optional(lchan, fn))
return 0;
- if (lchan->tch.last_sid.len) {
- memcpy(dst, lchan->tch.last_sid.buf, lchan->tch.last_sid.len);
- lchan->tch.last_sid.fn = fn;
- return lchan->tch.last_sid.len + 1;
+ if (lchan->tch.dtx.len) {
+ memcpy(dst, lchan->tch.dtx.cache, lchan->tch.dtx.len);
+ lchan->tch.dtx.fn = fn;
+ return lchan->tch.dtx.len + 1;
}
LOGP(DL1C, LOGL_DEBUG, "Have to send %s frame on TCH but SID buffer "
diff --git a/src/common/rsl.c b/src/common/rsl.c
index d2fea120..6c8f5cc2 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -2199,9 +2199,9 @@ static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len)
msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr++);
size_t ie_len = gsm0858_rsl_ul_meas_enc(&lchan->meas.ul_res,
- lchan->tch.dtxd_active,
+ lchan->tch.dtx.dl_active,
meas_res);
- lchan->tch.dtxd_active = false;
+ lchan->tch.dtx.dl_active = false;
if (ie_len >= 3) {
msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, ie_len, meas_res);
lchan->meas.flags &= ~LC_UL_M_F_RES_VALID;