aboutsummaryrefslogtreecommitdiffstats
path: root/src/common/msg_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/msg_utils.c')
-rw-r--r--src/common/msg_utils.c125
1 files changed, 107 insertions, 18 deletions
diff --git a/src/common/msg_utils.c b/src/common/msg_utils.c
index b8764433..c5081b40 100644
--- a/src/common/msg_utils.c
+++ b/src/common/msg_utils.c
@@ -22,6 +22,7 @@
#include <osmo-bts/logging.h>
#include <osmo-bts/oml.h>
#include <osmo-bts/amr.h>
+#include <osmo-bts/rsl.h>
#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmocom/gsm/protocol/gsm_12_21.h>
@@ -120,8 +121,15 @@ void dtx_cache_payload(struct gsm_lchan *lchan, const uint8_t *l1_payload,
lchan->tch.dtx.len = copy_len + amr;
/* SID FIRST is special because it's both sent and cached: */
if (update == 0) {
- lchan->tch.dtx.fn = fn;
lchan->tch.dtx.is_update = false; /* Mark SID FIRST explicitly */
+ /* for non-AMR case - always update FN for incoming SID FIRST */
+ if (!amr ||
+ (dtx_dl_amr_enabled(lchan) &&
+ lchan->tch.dtx.dl_amr_fsm->state != ST_SID_U))
+ lchan->tch.dtx.fn = fn;
+ /* for AMR case - do not update FN if SID FIRST arrives in a
+ middle of silence: this should not be happening according to
+ the spec */
}
memcpy(lchan->tch.dtx.cache + amr, l1_payload, copy_len);
@@ -148,13 +156,17 @@ int dtx_dl_amr_fsm_step(struct gsm_lchan *lchan, const uint8_t *rtp_pl,
int8_t sti, cmi;
int rc;
- if (rtp_pl == NULL) { /* SID-FIRST P1 -> P2 */
+ if (lchan->type == GSM_LCHAN_TCH_H && /* SID-FIRST P1 -> P2 completion */
+ lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F2 && !rtp_pl) {
*len = 3;
memcpy(l1_payload, lchan->tch.dtx.cache, 2);
- dtx_dispatch(lchan, E_COMPL);
+ dtx_dispatch(lchan, E_SID_U);
return 0;
}
+ if (!rtp_pl_len)
+ return -EBADMSG;
+
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",
@@ -199,10 +211,15 @@ int dtx_dl_amr_fsm_step(struct gsm_lchan *lchan, const uint8_t *rtp_pl,
}
if (ft == AMR_SID) {
- dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, sti);
- if (lchan->tch.dtx.dl_amr_fsm->state == ST_VOICE)
+ if (lchan->tch.dtx.dl_amr_fsm->state == ST_VOICE) {
+ /* SID FIRST/UPDATE scheduling logic relies on SID FIRST
+ being sent first hence we have to force caching of SID
+ as FIRST regardless of actually decoded type */
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, false);
return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
E_SID_F, (void *)lchan);
+ } else
+ 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);
@@ -220,11 +237,14 @@ int dtx_dl_amr_fsm_step(struct gsm_lchan *lchan, const uint8_t *rtp_pl,
return 0;
}
+/* STI is located in payload byte 6, cache contains 2 byte prefix (CMR/CMI)
+ * STI set = SID UPDATE, STI unset = SID FIRST
+ */
static inline void dtx_sti_set(struct gsm_lchan *lchan)
{
lchan->tch.dtx.cache[6 + 2] |= STI_BIT_MASK;
}
-/* STI is located in payload byte 6, cache contains 2 byte prefix (CMR/CMI) */
+
static inline void dtx_sti_unset(struct gsm_lchan *lchan)
{
lchan->tch.dtx.cache[6 + 2] &= ~STI_BIT_MASK;
@@ -241,18 +261,28 @@ static inline bool dtx_amr_sid_optional(struct gsm_lchan *lchan, uint32_t fn)
uint32_t dx26 = 120 * (fn - lchan->tch.dtx.fn);
/* We're resuming after FACCH interruption */
- if (lchan->tch.dtx.dl_amr_fsm->state == ST_FACCH ||
- lchan->tch.dtx.dl_amr_fsm->state == ST_ONSET_F) {
+ if (lchan->tch.dtx.dl_amr_fsm->state == ST_FACCH) {
/* force STI bit to 0 so cache is treated as SID FIRST */
dtx_sti_unset(lchan);
lchan->tch.dtx.is_update = false;
- osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, E_SID_F,
- (void *)lchan);
- /* this FN was already used for ONSET message so we just prepare
- things for next one */
+ /* check that this FN has not been used for FACCH message
+ already: we rely here on the order of RTS arrival from L1 - we
+ expect that PH-DATA.req ALWAYS comes before PH-TCH.req for the
+ same FN */
+ if (lchan->tch.dtx.fn != LCHAN_FN_DUMMY) {
+ /* FACCH interruption is over */
+ dtx_dispatch(lchan, E_COMPL);
+ return false;
+ } else
+ lchan->tch.dtx.fn = fn;
+ /* this FN was already used for FACCH or ONSET message so we just
+ prepare things for next one */
return true;
}
+ if (lchan->tch.dtx.dl_amr_fsm->state == ST_VOICE)
+ return true;
+
/* according to 3GPP TS 26.093 A.5.1.1:
(*26) to avoid float math, add 1 FN tolerance (-120) */
if (lchan->tch.dtx.is_update) { /* SID UPDATE: every 8th RTP frame */
@@ -297,6 +327,11 @@ static inline bool dtx_sched_optional(struct gsm_lchan *lchan, uint32_t fn)
return false;
}
+/*! \brief Check if DTX DL AMR is enabled for a given lchan (it have proper type,
+ * FSM is allocated etc.)
+ * \param[in] lchan Logical channel on which we check scheduling
+ * \returns true if DTX DL AMR is enabled, false otherwise
+ */
bool dtx_dl_amr_enabled(const struct gsm_lchan *lchan)
{
if (lchan->ts->trx->bts->dtxd &&
@@ -306,6 +341,31 @@ bool dtx_dl_amr_enabled(const struct gsm_lchan *lchan)
return false;
}
+/*! \brief Check if DTX DL AMR FSM state is recursive: requires secondary
+ * response to a single RTS request from L1.
+ * \param[in] lchan Logical channel on which we check scheduling
+ * \returns true if DTX DL AMR FSM state is recursive, false otherwise
+ */
+bool dtx_recursion(const struct gsm_lchan *lchan)
+{
+ if (!dtx_dl_amr_enabled(lchan))
+ return false;
+
+ if (lchan->tch.dtx.dl_amr_fsm->state == ST_U_INH ||
+ lchan->tch.dtx.dl_amr_fsm->state == ST_F1_INH ||
+ lchan->tch.dtx.dl_amr_fsm->state == ST_ONSET_F ||
+ lchan->tch.dtx.dl_amr_fsm->state == ST_ONSET_V ||
+ lchan->tch.dtx.dl_amr_fsm->state == ST_ONSET_F_REC ||
+ lchan->tch.dtx.dl_amr_fsm->state == ST_ONSET_V_REC)
+ return true;
+
+ return false;
+}
+
+/*! \brief Send signal to FSM: with proper check if DIX is enabled for this lchan
+ * \param[in] lchan Logical channel on which we check scheduling
+ * \param[in] e DTX DL AMR FSM Event
+ */
void dtx_dispatch(struct gsm_lchan *lchan, enum dtx_dl_amr_fsm_events e)
{
if (dtx_dl_amr_enabled(lchan))
@@ -313,7 +373,23 @@ void dtx_dispatch(struct gsm_lchan *lchan, enum dtx_dl_amr_fsm_events e)
(void *)lchan);
}
-/* repeat last SID if possible, returns SID length + 1 or 0 */
+/*! \brief Send internal signal to FSM: check that DTX is enabled for this chan,
+ * check that current FSM and lchan states are permitting such signal.
+ * Note: this should be the only way to dispatch E_COMPL to FSM from
+ * BTS code.
+ * \param[in] lchan Logical channel on which we check scheduling
+ */
+void dtx_int_signal(struct gsm_lchan *lchan)
+{
+ if (!dtx_dl_amr_enabled(lchan))
+ return;
+
+ if ((lchan->type == GSM_LCHAN_TCH_H &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1) ||
+ dtx_recursion(lchan))
+ dtx_dispatch(lchan, E_COMPL);
+}
+
/*! \brief Repeat last SID if possible in case of DTX
* \param[in] lchan Logical channel on which we check scheduling
* \param[in] dst Buffer to copy last SID into
@@ -334,13 +410,26 @@ uint8_t repeat_last_sid(struct gsm_lchan *lchan, uint8_t *dst, uint32_t fn)
return 0;
if (lchan->tch.dtx.len) {
+ if (dtx_dl_amr_enabled(lchan)) {
+ if ((lchan->type == GSM_LCHAN_TCH_H &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F2) ||
+ (lchan->type == GSM_LCHAN_TCH_F &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1)) {
+ /* advance FSM in case we've just sent SID FIRST
+ to restore silence after FACCH interruption */
+ osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
+ E_SID_U, (void *)lchan);
+ dtx_sti_unset(lchan);
+ } else if (lchan->tch.dtx.dl_amr_fsm->state ==
+ ST_SID_U) {
+ /* enforce SID UPDATE for next repetition: it
+ might have been altered by FACCH handling */
+ dtx_sti_set(lchan);
+ lchan->tch.dtx.is_update = true;
+ }
+ }
memcpy(dst, lchan->tch.dtx.cache, lchan->tch.dtx.len);
lchan->tch.dtx.fn = fn;
- /* enforce SID UPDATE for next repetition - it might have
- been altered by FACCH handling */
- dtx_sti_set(lchan);
- if (lchan->tch.dtx.dl_amr_fsm->state == ST_SID_U)
- lchan->tch.dtx.is_update = true;
return lchan->tch.dtx.len + 1;
}