aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmo-bts/Makefile.am3
-rw-r--r--include/osmo-bts/dtx_dl_amr_fsm.h35
-rw-r--r--include/osmo-bts/msg_utils.h9
-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
-rw-r--r--src/osmo-bts-litecell15/l1_if.c14
-rw-r--r--src/osmo-bts-litecell15/l1_if.h4
-rw-r--r--src/osmo-bts-litecell15/tch.c122
-rw-r--r--src/osmo-bts-sysmo/l1_if.c14
-rw-r--r--src/osmo-bts-sysmo/l1_if.h4
-rw-r--r--src/osmo-bts-sysmo/tch.c122
14 files changed, 675 insertions, 138 deletions
diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am
index d0b55ff9..ef4165f3 100644
--- a/include/osmo-bts/Makefile.am
+++ b/include/osmo-bts/Makefile.am
@@ -1,4 +1,5 @@
noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \
oml.h paging.h rsl.h signal.h vty.h amr.h pcu_if.h pcuif_proto.h \
handover.h msg_utils.h tx_power.h control_if.h cbch.h l1sap.h \
- power_control.h scheduler.h scheduler_backend.h phy_link.h
+ power_control.h scheduler.h scheduler_backend.h phy_link.h \
+ dtx_dl_amr_fsm.h
diff --git a/include/osmo-bts/dtx_dl_amr_fsm.h b/include/osmo-bts/dtx_dl_amr_fsm.h
new file mode 100644
index 00000000..8b195953
--- /dev/null
+++ b/include/osmo-bts/dtx_dl_amr_fsm.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+
+/* DTX DL AMR FSM */
+
+#define X(s) (1 << (s))
+
+enum dtx_dl_amr_fsm_states {
+ ST_VOICE,
+ ST_SID_F1,
+ ST_SID_F2,
+ ST_F1_INH,
+ ST_U_INH,
+ ST_SID_U,
+ ST_ONSET_V,
+ ST_ONSET_F,
+ ST_FACCH_V,
+ ST_FACCH,
+};
+
+enum dtx_dl_amr_fsm_events {
+ E_VOICE,
+ E_ONSET,
+ E_FACCH,
+ E_COMPL,
+ E_INHIB,
+ E_SID_F,
+ E_SID_U,
+};
+
+extern const struct value_string dtx_dl_amr_fsm_event_names[];
+extern struct osmo_fsm dtx_dl_amr_fsm;
diff --git a/include/osmo-bts/msg_utils.h b/include/osmo-bts/msg_utils.h
index f99f3c40..31bd1725 100644
--- a/include/osmo-bts/msg_utils.h
+++ b/include/osmo-bts/msg_utils.h
@@ -26,12 +26,11 @@ enum {
};
void lchan_set_marker(bool t, struct gsm_lchan *lchan);
-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);
-int dtx_amr_check_onset(struct gsm_lchan *lchan, const uint8_t *rtp_pl,
+void dtx_cache_payload(struct gsm_lchan *lchan, const uint8_t *l1_payload,
+ size_t length, uint32_t fn, int update);
+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 repeat_last_sid(struct gsm_lchan *lchan, uint8_t *dst, uint32_t fn);
int msg_verify_ipa_structure(struct msgb *msg);
int msg_verify_oml_structure(struct msgb *msg);
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;
diff --git a/src/osmo-bts-litecell15/l1_if.c b/src/osmo-bts-litecell15/l1_if.c
index 1c989518..f47634ed 100644
--- a/src/osmo-bts-litecell15/l1_if.c
+++ b/src/osmo-bts-litecell15/l1_if.c
@@ -426,7 +426,7 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
}
static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
- struct osmo_phsap_prim *l1sap)
+ struct osmo_phsap_prim *l1sap, bool use_cache, bool marker)
{
struct lc15l1_hdl *fl1 = trx_lc15l1_hdl(trx);
struct gsm_lchan *lchan;
@@ -462,7 +462,7 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
rc = l1if_tch_encode(lchan,
l1p->u.phDataReq.msgUnitParam.u8Buffer,
&l1p->u.phDataReq.msgUnitParam.u8Size,
- msg->data, msg->len, u32Fn,
+ msg->data, msg->len, u32Fn, use_cache,
l1sap->u.tch.marker);
if (rc < 0) {
/* no data encoded for L1: smth will be generated below */
@@ -494,16 +494,14 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
} else {
/* empty frame */
if (trx->bts->dtxd && trx != trx->bts->c0)
- lchan->tch.dtxd_active = true;
+ lchan->tch.dtx.dl_active = true;
empty_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr);
}
/* send message to DSP's queue */
osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg);
- if (l1sap->u.tch.marker) { /* DTX: send voice after ONSET was sent */
- l1sap->u.tch.marker = 0;
- return ph_tch_req(trx, l1sap->oph.msg, l1sap);
- }
+ if (rc > 0 && trx->bts->dtxd) /* DTX: send voice after ONSET was sent */
+ return ph_tch_req(trx, l1sap->oph.msg, l1sap, true, false);
return 0;
}
@@ -571,7 +569,7 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
rc = ph_data_req(trx, msg, l1sap);
break;
case OSMO_PRIM(PRIM_TCH, PRIM_OP_REQUEST):
- rc = ph_tch_req(trx, msg, l1sap);
+ rc = ph_tch_req(trx, msg, l1sap, false, l1sap->u.tch.marker);
break;
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST):
rc = mph_info_req(trx, msg, l1sap);
diff --git a/src/osmo-bts-litecell15/l1_if.h b/src/osmo-bts-litecell15/l1_if.h
index adb197d1..7feee560 100644
--- a/src/osmo-bts-litecell15/l1_if.h
+++ b/src/osmo-bts-litecell15/l1_if.h
@@ -90,8 +90,8 @@ struct gsm_lchan *l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer)
/* tch.c */
int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
- const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn,
- bool marker);
+ const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn,
+ bool use_cache, bool marker);
int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg);
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn);
diff --git a/src/osmo-bts-litecell15/tch.c b/src/osmo-bts-litecell15/tch.c
index e246ffb1..b2513887 100644
--- a/src/osmo-bts-litecell15/tch.c
+++ b/src/osmo-bts-litecell15/tch.c
@@ -45,6 +45,7 @@
#include <osmo-bts/measurement.h>
#include <osmo-bts/amr.h>
#include <osmo-bts/l1sap.h>
+#include <osmo-bts/dtx_dl_amr_fsm.h>
#include <nrw/litecell15/litecell15.h>
#include <nrw/litecell15/gsml1prim.h>
@@ -198,8 +199,7 @@ static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_le
* \returns number of \a l1_payload bytes filled
*/
static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
- uint8_t payload_len,
- struct gsm_lchan *lchan, uint8_t ft)
+ uint8_t payload_len, uint8_t ft)
{
memcpy(l1_payload, rtp_payload, payload_len);
return payload_len;
@@ -210,10 +210,11 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
/*! \brief function for incoming RTP via TCH.req
* \param[in] rtp_pl buffer containing RTP payload
* \param[in] rtp_pl_len length of \a rtp_pl
+ * \param[in] use_cache Use cached payload instead of parsing RTP
* \param[in] marker RTP header Marker bit (indicates speech onset)
* \returns 0 if encoding result can be sent further to L1 without extra actions
* positive value if data is ready AND extra actions are required
- * negative value otherwise
+ * negative value otherwise (no data for L1 encoded)
*
* This function prepares a msgb with a L1 PH-DATA.req primitive and
* queues it into lchan->dl_tch_queue.
@@ -223,7 +224,8 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
* pre-fill the primtive.
*/
int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
- const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn, bool marker)
+ const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn,
+ bool use_cache, bool marker)
{
uint8_t *payload_type;
uint8_t *l1_payload, ft;
@@ -242,17 +244,17 @@ int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
*payload_type = GsmL1_TchPlType_Fr;
rc = rtppayload_to_l1_fr(l1_payload,
rtp_pl, rtp_pl_len);
- if (rc)
+ if (rc && lchan->ts->trx->bts->dtxd)
is_sid = osmo_fr_check_sid(rtp_pl, rtp_pl_len);
} else{
*payload_type = GsmL1_TchPlType_Hr;
rc = rtppayload_to_l1_hr(l1_payload,
rtp_pl, rtp_pl_len);
- if (rc)
+ if (rc && lchan->ts->trx->bts->dtxd)
is_sid = osmo_hr_check_sid(rtp_pl, rtp_pl_len);
}
if (is_sid)
- save_last_sid(lchan, rtp_pl, rtp_pl_len, fn, -1, 0, 0);
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, -1);
break;
case GSM48_CMODE_SPEECH_EFR:
*payload_type = GsmL1_TchPlType_Efr;
@@ -261,25 +263,75 @@ int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
/* FIXME: detect and save EFR SID */
break;
case GSM48_CMODE_SPEECH_AMR:
- rc = dtx_amr_check_onset(lchan, rtp_pl, rtp_pl_len, fn,
- l1_payload, &ft);
+ if (use_cache) {
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload, lchan->tch.dtx.cache,
+ lchan->tch.dtx.len, ft);
+ *len = lchan->tch.dtx.len + 1;
+ return 0;
+ }
+
+ rc = dtx_dl_amr_fsm_step(lchan, rtp_pl, rtp_pl_len, fn,
+ l1_payload, marker, len, &ft);
+ if (rc < 0)
+ return rc;
+ if (!lchan->ts->trx->bts->dtxd) {
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ }
- if (marker || rc > 0) {
+ /* DTX DL-specific logic below: */
+ switch (lchan->tch.dtx.dl_amr_fsm->state) {
+ case ST_ONSET_V:
+ case ST_ONSET_F:
*payload_type = GsmL1_TchPlType_Amr_Onset;
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
*len = 1;
- if (rc != 0) {
- LOGP(DRTP, LOGL_NOTICE, "%s SPEECH frame without"
- " Marker: ONSET forced\n",
- get_value_string(osmo_amr_type_names, ft));
- return rc;
+ return 1;
+ case ST_VOICE:
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ case ST_SID_F1:
+ if (lchan->type == GSM_LCHAN_TCH_H) { /* AMR HR */
+ *payload_type = GsmL1_TchPlType_Amr_SidFirstP1;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl,
+ rtp_pl_len, ft);
+ return 0;
}
- LOGP(DRTP, LOGL_DEBUG, "%s SPEECH frame with Marker\n",
- get_value_string(osmo_amr_type_names, ft));
- }
- else {
+ /* AMR FR */
*payload_type = GsmL1_TchPlType_Amr;
- rc = 2 + rtppayload_to_l1_amr(l1_payload + 2, rtp_pl,
- rtp_pl_len, lchan, ft);
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ case ST_SID_F2:
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ case ST_F1_INH:
+ *payload_type = GsmL1_TchPlType_Amr_SidFirstInH;
+ *len = 3;
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
+ return 1;
+ case ST_U_INH:
+ *payload_type = GsmL1_TchPlType_Amr_SidUpdateInH;
+ *len = 3;
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
+ return 1;
+ case ST_SID_U:
+ return -EAGAIN;
+ case ST_FACCH_V:
+ case ST_FACCH:
+ /* FIXME: if this possible at all? */
+ return 0;
+ default:
+ LOGP(DRTP, LOGL_ERROR, "Unhandled DTX DL AMR FSM state "
+ "%d\n", lchan->tch.dtx.dl_amr_fsm->state);
+ return -EINVAL;
}
break;
default:
@@ -442,7 +494,20 @@ struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn)
switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_AMR:
- *payload_type = GsmL1_TchPlType_Amr;
+ if (lchan->type == GSM_LCHAN_TCH_H &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1 &&
+ lchan->ts->trx->bts->dtxd) {
+ *payload_type = GsmL1_TchPlType_Amr_SidFirstP2;
+ rc = dtx_dl_amr_fsm_step(lchan, NULL, 0, fn, l1_payload,
+ false, &(msu_param->u8Size),
+ NULL);
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+ return msg;
+ } else
+ *payload_type = GsmL1_TchPlType_Amr;
break;
case GSM48_CMODE_SPEECH_V1:
if (lchan->type == GSM_LCHAN_TCH_F)
@@ -458,12 +523,13 @@ struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn)
return NULL;
}
- rc = repeat_last_sid(lchan, l1_payload, fn);
- if (!rc) {
- msgb_free(msg);
- return NULL;
+ if (lchan->ts->trx->bts->dtxd) {
+ rc = repeat_last_sid(lchan, l1_payload, fn);
+ if (!rc) {
+ msgb_free(msg);
+ return NULL;
+ }
+ msu_param->u8Size = rc;
}
- msu_param->u8Size = rc;
-
return msg;
}
diff --git a/src/osmo-bts-sysmo/l1_if.c b/src/osmo-bts-sysmo/l1_if.c
index 11a57291..bef2d303 100644
--- a/src/osmo-bts-sysmo/l1_if.c
+++ b/src/osmo-bts-sysmo/l1_if.c
@@ -421,7 +421,7 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
}
static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
- struct osmo_phsap_prim *l1sap)
+ struct osmo_phsap_prim *l1sap, bool use_cache, bool marker)
{
struct femtol1_hdl *fl1 = trx_femtol1_hdl(trx);
struct gsm_lchan *lchan;
@@ -457,7 +457,7 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
rc = l1if_tch_encode(lchan,
l1p->u.phDataReq.msgUnitParam.u8Buffer,
&l1p->u.phDataReq.msgUnitParam.u8Size,
- msg->data, msg->len, u32Fn,
+ msg->data, msg->len, u32Fn, use_cache,
l1sap->u.tch.marker);
if (rc < 0) {
/* no data encoded for L1: smth will be generated below */
@@ -489,16 +489,14 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
} else {
/* empty frame */
if (trx->bts->dtxd && trx != trx->bts->c0)
- lchan->tch.dtxd_active = true;
+ lchan->tch.dtx.dl_active = true;
empty_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr);
}
/* send message to DSP's queue */
osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg);
- if (l1sap->u.tch.marker) { /* DTX: send voice after ONSET was sent */
- l1sap->u.tch.marker = 0;
- return ph_tch_req(trx, l1sap->oph.msg, l1sap);
- }
+ if (rc > 0 && trx->bts->dtxd) /* DTX: send voice after ONSET was sent */
+ return ph_tch_req(trx, l1sap->oph.msg, l1sap, true, false);
return 0;
}
@@ -566,7 +564,7 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
rc = ph_data_req(trx, msg, l1sap);
break;
case OSMO_PRIM(PRIM_TCH, PRIM_OP_REQUEST):
- rc = ph_tch_req(trx, msg, l1sap);
+ rc = ph_tch_req(trx, msg, l1sap, false, l1sap->u.tch.marker);
break;
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST):
rc = mph_info_req(trx, msg, l1sap);
diff --git a/src/osmo-bts-sysmo/l1_if.h b/src/osmo-bts-sysmo/l1_if.h
index ece7a011..0dc919bd 100644
--- a/src/osmo-bts-sysmo/l1_if.h
+++ b/src/osmo-bts-sysmo/l1_if.h
@@ -110,8 +110,8 @@ struct gsm_lchan *l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer)
/* tch.c */
int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
- const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn,
- bool marker);
+ const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn,
+ bool use_cache, bool marker);
int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg);
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn);
diff --git a/src/osmo-bts-sysmo/tch.c b/src/osmo-bts-sysmo/tch.c
index ca9045e1..b08ba7e1 100644
--- a/src/osmo-bts-sysmo/tch.c
+++ b/src/osmo-bts-sysmo/tch.c
@@ -42,6 +42,7 @@
#include <osmo-bts/measurement.h>
#include <osmo-bts/amr.h>
#include <osmo-bts/l1sap.h>
+#include <osmo-bts/dtx_dl_amr_fsm.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h>
@@ -281,8 +282,7 @@ static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_le
* \returns number of \a l1_payload bytes filled
*/
static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
- uint8_t payload_len,
- struct gsm_lchan *lchan, uint8_t ft)
+ uint8_t payload_len, uint8_t ft)
{
#ifdef USE_L1_RTP_MODE
memcpy(l1_payload, rtp_payload, payload_len);
@@ -306,10 +306,11 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
/*! \brief function for incoming RTP via TCH.req
* \param[in] rtp_pl buffer containing RTP payload
* \param[in] rtp_pl_len length of \a rtp_pl
+ * \param[in] use_cache Use cached payload instead of parsing RTP
* \param[in] marker RTP header Marker bit (indicates speech onset)
* \returns 0 if encoding result can be sent further to L1 without extra actions
* positive value if data is ready AND extra actions are required
- * negative value otherwise
+ * negative value otherwise (no data for L1 encoded)
*
* This function prepares a msgb with a L1 PH-DATA.req primitive and
* queues it into lchan->dl_tch_queue.
@@ -319,7 +320,8 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
* pre-fill the primtive.
*/
int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
- const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn, bool marker)
+ const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn,
+ bool use_cache, bool marker)
{
uint8_t *payload_type;
uint8_t *l1_payload, ft;
@@ -338,17 +340,17 @@ int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
*payload_type = GsmL1_TchPlType_Fr;
rc = rtppayload_to_l1_fr(l1_payload,
rtp_pl, rtp_pl_len);
- if (rc)
+ if (rc && lchan->ts->trx->bts->dtxd)
is_sid = osmo_fr_check_sid(rtp_pl, rtp_pl_len);
} else{
*payload_type = GsmL1_TchPlType_Hr;
rc = rtppayload_to_l1_hr(l1_payload,
rtp_pl, rtp_pl_len);
- if (rc)
+ if (rc && lchan->ts->trx->bts->dtxd)
is_sid = osmo_hr_check_sid(rtp_pl, rtp_pl_len);
}
if (is_sid)
- save_last_sid(lchan, rtp_pl, rtp_pl_len, fn, -1, 0, 0);
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, -1);
break;
#if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
case GSM48_CMODE_SPEECH_EFR:
@@ -359,25 +361,75 @@ int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
break;
#endif
case GSM48_CMODE_SPEECH_AMR:
- rc = dtx_amr_check_onset(lchan, rtp_pl, rtp_pl_len, fn,
- l1_payload, &ft);
+ if (use_cache) {
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload, lchan->tch.dtx.cache,
+ lchan->tch.dtx.len, ft);
+ *len = lchan->tch.dtx.len + 1;
+ return 0;
+ }
+
+ rc = dtx_dl_amr_fsm_step(lchan, rtp_pl, rtp_pl_len, fn,
+ l1_payload, marker, len, &ft);
+ if (rc < 0)
+ return rc;
+ if (!lchan->ts->trx->bts->dtxd) {
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ }
- if (marker || rc > 0) {
+ /* DTX DL-specific logic below: */
+ switch (lchan->tch.dtx.dl_amr_fsm->state) {
+ case ST_ONSET_V:
+ case ST_ONSET_F:
*payload_type = GsmL1_TchPlType_Amr_Onset;
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
*len = 1;
- if (rc != 0) {
- LOGP(DRTP, LOGL_NOTICE, "%s SPEECH frame without"
- " Marker: ONSET forced\n",
- get_value_string(osmo_amr_type_names, ft));
- return rc;
+ return 1;
+ case ST_VOICE:
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ case ST_SID_F1:
+ if (lchan->type == GSM_LCHAN_TCH_H) { /* AMR HR */
+ *payload_type = GsmL1_TchPlType_Amr_SidFirstP1;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl,
+ rtp_pl_len, ft);
+ return 0;
}
- LOGP(DRTP, LOGL_DEBUG, "%s SPEECH frame with Marker\n",
- get_value_string(osmo_amr_type_names, ft));
- }
- else {
+ /* AMR FR */
*payload_type = GsmL1_TchPlType_Amr;
- rc = 2 + rtppayload_to_l1_amr(l1_payload + 2, rtp_pl,
- rtp_pl_len, lchan, ft);
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ case ST_SID_F2:
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ case ST_F1_INH:
+ *payload_type = GsmL1_TchPlType_Amr_SidFirstInH;
+ *len = 3;
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
+ return 1;
+ case ST_U_INH:
+ *payload_type = GsmL1_TchPlType_Amr_SidUpdateInH;
+ *len = 3;
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
+ return 1;
+ case ST_SID_U:
+ return -EAGAIN;
+ case ST_FACCH_V:
+ case ST_FACCH:
+ /* FIXME: if this possible at all? */
+ return 0;
+ default:
+ LOGP(DRTP, LOGL_ERROR, "Unhandled DTX DL AMR FSM state "
+ "%d\n", lchan->tch.dtx.dl_amr_fsm->state);
+ return -EINVAL;
}
break;
default:
@@ -544,7 +596,20 @@ struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn)
switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_AMR:
- *payload_type = GsmL1_TchPlType_Amr;
+ if (lchan->type == GSM_LCHAN_TCH_H &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1 &&
+ lchan->ts->trx->bts->dtxd) {
+ *payload_type = GsmL1_TchPlType_Amr_SidFirstP2;
+ rc = dtx_dl_amr_fsm_step(lchan, NULL, 0, fn, l1_payload,
+ false, &(msu_param->u8Size),
+ NULL);
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+ return msg;
+ } else
+ *payload_type = GsmL1_TchPlType_Amr;
break;
case GSM48_CMODE_SPEECH_V1:
if (lchan->type == GSM_LCHAN_TCH_F)
@@ -560,12 +625,13 @@ struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn)
return NULL;
}
- rc = repeat_last_sid(lchan, l1_payload, fn);
- if (!rc) {
- msgb_free(msg);
- return NULL;
+ if (lchan->ts->trx->bts->dtxd) {
+ rc = repeat_last_sid(lchan, l1_payload, fn);
+ if (!rc) {
+ msgb_free(msg);
+ return NULL;
+ }
+ msu_param->u8Size = rc;
}
- msu_param->u8Size = rc;
-
return msg;
}