From babd05661d13b12234e848acf9c4bff909ef05f4 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 3 Oct 2016 17:37:45 +0200 Subject: 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 --- include/osmo-bts/Makefile.am | 3 +- include/osmo-bts/dtx_dl_amr_fsm.h | 35 +++++ include/osmo-bts/msg_utils.h | 9 +- src/common/Makefile.am | 3 +- src/common/dtx_dl_amr_fsm.c | 320 ++++++++++++++++++++++++++++++++++++++ src/common/l1sap.c | 24 ++- src/common/msg_utils.c | 135 +++++++++------- src/common/rsl.c | 4 +- src/osmo-bts-litecell15/l1_if.c | 14 +- src/osmo-bts-litecell15/l1_if.h | 4 +- src/osmo-bts-litecell15/tch.c | 122 +++++++++++---- src/osmo-bts-sysmo/l1_if.c | 14 +- src/osmo-bts-sysmo/l1_if.h | 4 +- src/osmo-bts-sysmo/tch.c | 122 +++++++++++---- 14 files changed, 675 insertions(+), 138 deletions(-) create mode 100644 include/osmo-bts/dtx_dl_amr_fsm.h create mode 100644 src/common/dtx_dl_amr_fsm.c 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 +#include +#include + +/* 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 . + * + */ + +#include +#include + +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 #include #include +#include #include #include #include @@ -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 #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include #include @@ -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 #include #include +#include #include #include @@ -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 #include #include +#include #include #include @@ -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; } -- cgit v1.2.3