diff options
-rw-r--r-- | include/osmo-bts/dtx_dl_amr_fsm.h | 5 | ||||
-rw-r--r-- | include/osmo-bts/msg_utils.h | 2 | ||||
-rw-r--r-- | src/common/dtx_dl_amr_fsm.c | 147 | ||||
-rw-r--r-- | src/common/msg_utils.c | 125 | ||||
-rw-r--r-- | src/osmo-bts-litecell15/l1_if.c | 33 | ||||
-rw-r--r-- | src/osmo-bts-litecell15/tch.c | 31 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/l1_if.c | 33 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/tch.c | 31 |
8 files changed, 270 insertions, 137 deletions
diff --git a/include/osmo-bts/dtx_dl_amr_fsm.h b/include/osmo-bts/dtx_dl_amr_fsm.h index 8b195953..4fb2f251 100644 --- a/include/osmo-bts/dtx_dl_amr_fsm.h +++ b/include/osmo-bts/dtx_dl_amr_fsm.h @@ -14,10 +14,13 @@ enum dtx_dl_amr_fsm_states { ST_SID_F2, ST_F1_INH, ST_U_INH, + ST_F1_INH_REC, + ST_U_INH_REC, ST_SID_U, ST_ONSET_V, ST_ONSET_F, - ST_FACCH_V, + ST_ONSET_V_REC, + ST_ONSET_F_REC, ST_FACCH, }; diff --git a/include/osmo-bts/msg_utils.h b/include/osmo-bts/msg_utils.h index 4f9868ca..73210455 100644 --- a/include/osmo-bts/msg_utils.h +++ b/include/osmo-bts/msg_utils.h @@ -29,6 +29,8 @@ enum { void lchan_set_marker(bool t, struct gsm_lchan *lchan); bool dtx_dl_amr_enabled(const struct gsm_lchan *lchan); void dtx_dispatch(struct gsm_lchan *lchan, enum dtx_dl_amr_fsm_events e); +bool dtx_recursion(const struct gsm_lchan *lchan); +void dtx_int_signal(struct gsm_lchan *lchan); 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, diff --git a/src/common/dtx_dl_amr_fsm.c b/src/common/dtx_dl_amr_fsm.c index 50759572..d903b0cf 100644 --- a/src/common/dtx_dl_amr_fsm.c +++ b/src/common/dtx_dl_amr_fsm.c @@ -96,11 +96,8 @@ void dtx_fsm_sid_f2(struct osmo_fsm_inst *fi, uint32_t event, void *data) 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); + case E_COMPL: + osmo_fsm_inst_state_chg(fi, ST_F1_INH_REC, 0, 0); break; default: LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event); @@ -112,11 +109,34 @@ void dtx_fsm_f1_inh(struct osmo_fsm_inst *fi, uint32_t event, void *data) void dtx_fsm_u_inh(struct osmo_fsm_inst *fi, uint32_t event, void *data) { switch (event) { - case E_VOICE: + case E_COMPL: + osmo_fsm_inst_state_chg(fi, ST_U_INH_REC, 0, 0); + break; + default: + LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event); + OSMO_ASSERT(0); + break; + } +} + +void dtx_fsm_f1_inh_rec(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case E_COMPL: osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0); break; - case E_FACCH: - osmo_fsm_inst_state_chg(fi, ST_FACCH, 0, 0); + default: + LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event); + OSMO_ASSERT(0); + break; + } +} + +void dtx_fsm_u_inh_rec(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case E_COMPL: + osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0); break; default: LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event); @@ -155,13 +175,8 @@ void dtx_fsm_sid_upd(struct osmo_fsm_inst *fi, uint32_t event, void *data) void dtx_fsm_onset_v(struct osmo_fsm_inst *fi, uint32_t event, void *data) { switch (event) { - case E_SID_F: - 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); + case E_COMPL: + osmo_fsm_inst_state_chg(fi, ST_ONSET_V_REC, 0, 0); break; default: LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event); @@ -173,14 +188,8 @@ void dtx_fsm_onset_v(struct osmo_fsm_inst *fi, uint32_t event, void *data) void dtx_fsm_onset_f(struct osmo_fsm_inst *fi, uint32_t event, void *data) { switch (event) { - case E_SID_F: - 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); + case E_COMPL: + osmo_fsm_inst_state_chg(fi, ST_ONSET_F_REC, 0, 0); break; default: LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event); @@ -189,17 +198,24 @@ void dtx_fsm_onset_f(struct osmo_fsm_inst *fi, uint32_t event, void *data) } } -void dtx_fsm_facch_v(struct osmo_fsm_inst *fi, uint32_t event, void *data) +void dtx_fsm_onset_v_rec(struct osmo_fsm_inst *fi, uint32_t event, void *data) { switch (event) { - case E_FACCH: - break; - case E_VOICE: + case E_COMPL: osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0); break; - case E_SID_U: + default: + LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event); + OSMO_ASSERT(0); break; - case E_SID_F: + } +} + +void dtx_fsm_onset_f_rec(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case E_COMPL: + osmo_fsm_inst_state_chg(fi, ST_FACCH, 0, 0); break; default: LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event); @@ -211,13 +227,14 @@ void dtx_fsm_facch_v(struct osmo_fsm_inst *fi, uint32_t event, void *data) void dtx_fsm_facch(struct osmo_fsm_inst *fi, uint32_t event, void *data) { switch (event) { + case E_SID_U: + case E_SID_F: 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: + case E_COMPL: osmo_fsm_inst_state_chg(fi, ST_SID_F1, 0, 0); break; default: @@ -244,29 +261,43 @@ static struct osmo_fsm_state dtx_dl_amr_fsm_states[] = { .action = dtx_fsm_sid_f1, }, /* SID-FIRST P2 (only for AMR HR): - actual start of silence period in case of 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) | X(E_ONSET), .out_state_mask = X(ST_SID_U) | X(ST_VOICE) | X(ST_ONSET_F) | X(ST_ONSET_V), .name = "SID-FIRST (P2)", .action = dtx_fsm_sid_f2, }, - /* SID-FIRST Inhibited: - incoming SPEECH or FACCH (only for AMR HR) */ + /* SID-FIRST Inhibited: incoming SPEECH (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), + .in_event_mask = X(E_COMPL), + .out_state_mask = X(ST_F1_INH_REC), .name = "SID-FIRST (Inh)", .action = dtx_fsm_f1_inh, }, - /* SID-UPDATE Inhibited: - incoming SPEECH or FACCH (only for AMR HR) */ + /* SID-UPDATE Inhibited: incoming SPEECH (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), + .in_event_mask = X(E_COMPL), + .out_state_mask = X(ST_U_INH_REC), .name = "SID-UPDATE (Inh)", .action = dtx_fsm_u_inh, }, + /* SID-FIRST Inhibition recursion in progress: + Inhibit itself was already sent, now have to send the voice that caused it */ + [ST_F1_INH_REC]= { + .in_event_mask = X(E_COMPL), + .out_state_mask = X(ST_VOICE), + .name = "SID-FIRST (Inh, Rec)", + .action = dtx_fsm_f1_inh_rec, + }, + /* SID-UPDATE Inhibition recursion in progress: + Inhibit itself was already sent, now have to send the voice that caused it */ + [ST_U_INH_REC]= { + .in_event_mask = X(E_COMPL), + .out_state_mask = X(ST_VOICE), + .name = "SID-UPDATE (Inh, Rec)", + .action = dtx_fsm_u_inh_rec, + }, /* 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), @@ -276,31 +307,39 @@ static struct osmo_fsm_state dtx_dl_amr_fsm_states[] = { }, /* ONSET - end of silent period due to incoming SPEECH frame */ [ST_ONSET_V]= { - .in_event_mask = X(E_SID_F) | X(E_SID_U) | X(E_VOICE) | X(E_FACCH), - .out_state_mask = X(ST_VOICE) | X(ST_FACCH_V), + .in_event_mask = X(E_COMPL), + .out_state_mask = X(ST_ONSET_V_REC), .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) | X(E_SID_F), - .out_state_mask = X(ST_VOICE) | X(ST_FACCH), + .in_event_mask = X(E_COMPL), + .out_state_mask = X(ST_ONSET_F_REC), .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, + /* ONSET recursion in progress: + ONSET itself was already sent, now have to send the voice that caused it */ + [ST_ONSET_V_REC]= { + .in_event_mask = X(E_COMPL), + .out_state_mask = X(ST_VOICE), + .name = "ONSET (SPEECH, Rec)", + .action = dtx_fsm_onset_v_rec, + }, + /* ONSET recursion in progress: + ONSET itself was already sent, now have to send the data that caused it */ + [ST_ONSET_F_REC]= { + .in_event_mask = X(E_COMPL), + .out_state_mask = X(ST_FACCH), + .name = "ONSET (FACCH, Rec)", + .action = dtx_fsm_onset_f_rec, }, /* 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), + .in_event_mask = X(E_FACCH) | X(E_VOICE) | X(E_COMPL) | X(E_SID_U) | X(E_SID_F), + .out_state_mask = X(ST_VOICE) | X(ST_SID_F1), .name = "FACCH", .action = dtx_fsm_facch, }, @@ -310,7 +349,7 @@ const struct value_string dtx_dl_amr_fsm_event_names[] = { { E_VOICE, "Voice" }, { E_ONSET, "ONSET" }, { E_FACCH, "FACCH" }, - { E_COMPL, "Complete P1 -> P2" }, + { E_COMPL, "Complete" }, { E_INHIB, "Inhibit" }, { E_SID_F, "SID-FIRST" }, { E_SID_U, "SID-UPDATE" }, 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; } diff --git a/src/osmo-bts-litecell15/l1_if.c b/src/osmo-bts-litecell15/l1_if.c index 4a6c7399..632e769b 100644 --- a/src/osmo-bts-litecell15/l1_if.c +++ b/src/osmo-bts-litecell15/l1_if.c @@ -53,6 +53,7 @@ #include <osmo-bts/cbch.h> #include <osmo-bts/bts_model.h> #include <osmo-bts/l1sap.h> +#include <osmo-bts/msg_utils.h> #include <osmo-bts/dtx_dl_amr_fsm.h> #include <nrw/litecell15/litecell15.h> @@ -339,7 +340,6 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg, uint32_t u32Fn; uint8_t u8Tn, subCh, u8BlockNbr = 0, sapi = 0; uint8_t chan_nr, link_id; - bool rec = false; int len; if (!msg) { @@ -355,6 +355,7 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg, u32Fn = l1sap->u.data.fn; u8Tn = L1SAP_CHAN2TS(chan_nr); subCh = 0x1f; + lchan = get_lchan_by_chan_nr(trx, chan_nr); if (L1SAP_IS_LINK_SACCH(link_id)) { sapi = GsmL1_Sapi_Sacch; if (!L1SAP_IS_CHAN_TCHF(chan_nr)) @@ -404,8 +405,7 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg, if (len) { /* data request */ GsmL1_Prim_t *l1p = msgb_l1prim(l1msg); - lchan = get_lchan_by_chan_nr(trx, chan_nr); - + data_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr, len); if (use_cache) memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer, lchan->tch.dtx.facch, msgb_l2len(msg)); @@ -423,20 +423,25 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg, memcpy(lchan->tch.dtx.facch, msg->l2h, msgb_l2len(msg)); /* prepare ONSET message */ - len = 3; l1p->u.phDataReq.msgUnitParam.u8Buffer[0] = GsmL1_TchPlType_Amr_Onset; /* ignored CMR/CMI pair */ l1p->u.phDataReq.msgUnitParam.u8Buffer[1] = 0; l1p->u.phDataReq.msgUnitParam.u8Buffer[2] = 0; - /* ONSET is ready, recursive call is necessary */ - rec = true; + /* update length */ + data_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, + subCh, u8BlockNbr, 3); + /* update FN so it can be checked by TCH silence + resume handler */ + lchan->tch.dtx.fn = LCHAN_FN_DUMMY; } + } else if (dtx_dl_amr_enabled(lchan) && + lchan->tch.dtx.dl_amr_fsm->state == ST_FACCH) { + /* update FN so it can be checked by TCH silence + resume handler */ + lchan->tch.dtx.fn = LCHAN_FN_DUMMY; } - - data_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr, len); - - if (!rec && !use_cache) { + else { OSMO_ASSERT(msgb_l2len(msg) <= sizeof(l1p->u.phDataReq.msgUnitParam.u8Buffer)); memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer, msg->l2h, msgb_l2len(msg)); @@ -455,9 +460,10 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg, if (osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], l1msg) != 0) { LOGP(DL1P, LOGL_ERROR, "MQ_L1_WRITE queue full. Dropping msg.\n"); msgb_free(l1msg); - } + } else + dtx_int_signal(lchan); - if (rec) + if (dtx_recursion(lchan)) ph_data_req(trx, msg, l1sap, true); return 0; } @@ -536,8 +542,9 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg, } /* send message to DSP's queue */ osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg); + dtx_int_signal(lchan); - if (rc > 0 && trx->bts->dtxd) /* DTX: send voice after ONSET was sent */ + if (dtx_recursion(lchan)) /* DTX: send voice after ONSET was sent */ return ph_tch_req(trx, l1sap->oph.msg, l1sap, true, false); return 0; diff --git a/src/osmo-bts-litecell15/tch.c b/src/osmo-bts-litecell15/tch.c index 74950738..de3c7e35 100644 --- a/src/osmo-bts-litecell15/tch.c +++ b/src/osmo-bts-litecell15/tch.c @@ -305,9 +305,6 @@ int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len, *payload_type = GsmL1_TchPlType_Amr; rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len, ft); - /* force STI bit to 0 to make sure resume after FACCH - works properly */ - l1_payload[6 + 2] &= ~16; return 0; case ST_SID_F2: *payload_type = GsmL1_TchPlType_Amr; @@ -326,7 +323,6 @@ int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len, return 1; case ST_SID_U: return -EAGAIN; - case ST_FACCH_V: case ST_FACCH: return -EBADMSG; default: @@ -496,19 +492,17 @@ struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn) switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_AMR: if (lchan->type == GSM_LCHAN_TCH_H && - lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1 && dtx_dl_amr_enabled(lchan)) { + /* we have to explicitly handle sending SID FIRST P2 for + AMR HR in here */ *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; + if (rc == 0) + return msg; + } + *payload_type = GsmL1_TchPlType_Amr; break; case GSM48_CMODE_SPEECH_V1: if (lchan->type == GSM_LCHAN_TCH_F) @@ -524,13 +518,12 @@ struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn) return NULL; } - if (dtx_dl_amr_enabled(lchan)) { - rc = repeat_last_sid(lchan, l1_payload, fn); - if (!rc) { - msgb_free(msg); - return NULL; - } - msu_param->u8Size = rc; + rc = repeat_last_sid(lchan, l1_payload, fn); + if (!rc) { + msgb_free(msg); + return NULL; } + 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 61ffe395..ed643fcf 100644 --- a/src/osmo-bts-sysmo/l1_if.c +++ b/src/osmo-bts-sysmo/l1_if.c @@ -49,6 +49,7 @@ #include <osmo-bts/cbch.h> #include <osmo-bts/bts_model.h> #include <osmo-bts/l1sap.h> +#include <osmo-bts/msg_utils.h> #include <osmo-bts/dtx_dl_amr_fsm.h> #include <sysmocom/femtobts/superfemto.h> @@ -334,7 +335,6 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg, uint32_t u32Fn; uint8_t u8Tn, subCh, u8BlockNbr = 0, sapi = 0; uint8_t chan_nr, link_id; - bool rec = false; int len; if (!msg) { @@ -350,6 +350,7 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg, u32Fn = l1sap->u.data.fn; u8Tn = L1SAP_CHAN2TS(chan_nr); subCh = 0x1f; + lchan = get_lchan_by_chan_nr(trx, chan_nr); if (L1SAP_IS_LINK_SACCH(link_id)) { sapi = GsmL1_Sapi_Sacch; if (!L1SAP_IS_CHAN_TCHF(chan_nr)) @@ -399,8 +400,7 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg, if (len) { /* data request */ GsmL1_Prim_t *l1p = msgb_l1prim(l1msg); - lchan = get_lchan_by_chan_nr(trx, chan_nr); - + data_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr, len); if (use_cache) memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer, lchan->tch.dtx.facch, msgb_l2len(msg)); @@ -418,20 +418,25 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg, memcpy(lchan->tch.dtx.facch, msg->l2h, msgb_l2len(msg)); /* prepare ONSET message */ - len = 3; l1p->u.phDataReq.msgUnitParam.u8Buffer[0] = GsmL1_TchPlType_Amr_Onset; /* ignored CMR/CMI pair */ l1p->u.phDataReq.msgUnitParam.u8Buffer[1] = 0; l1p->u.phDataReq.msgUnitParam.u8Buffer[2] = 0; - /* ONSET is ready, recursive call is necessary */ - rec = true; + /* update length */ + data_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, + subCh, u8BlockNbr, 3); + /* update FN so it can be checked by TCH silence + resume handler */ + lchan->tch.dtx.fn = LCHAN_FN_DUMMY; } + } else if (dtx_dl_amr_enabled(lchan) && + lchan->tch.dtx.dl_amr_fsm->state == ST_FACCH) { + /* update FN so it can be checked by TCH silence + resume handler */ + lchan->tch.dtx.fn = LCHAN_FN_DUMMY; } - - data_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr, len); - - if (!rec && !use_cache) { + else { OSMO_ASSERT(msgb_l2len(msg) <= sizeof(l1p->u.phDataReq.msgUnitParam.u8Buffer)); memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer, msg->l2h, msgb_l2len(msg)); @@ -450,9 +455,10 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg, if (osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], l1msg) != 0) { LOGP(DL1P, LOGL_ERROR, "MQ_L1_WRITE queue full. Dropping msg.\n"); msgb_free(l1msg); - } + } else + dtx_int_signal(lchan); - if (rec) + if (dtx_recursion(lchan)) ph_data_req(trx, msg, l1sap, true); return 0; } @@ -531,8 +537,9 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg, } /* send message to DSP's queue */ osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg); + dtx_int_signal(lchan); - if (rc > 0 && trx->bts->dtxd) /* DTX: send voice after ONSET was sent */ + if (dtx_recursion(lchan)) /* DTX: send voice after ONSET was sent */ return ph_tch_req(trx, l1sap->oph.msg, l1sap, true, false); return 0; diff --git a/src/osmo-bts-sysmo/tch.c b/src/osmo-bts-sysmo/tch.c index addb2ffb..16c2cf3a 100644 --- a/src/osmo-bts-sysmo/tch.c +++ b/src/osmo-bts-sysmo/tch.c @@ -403,9 +403,6 @@ int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len, *payload_type = GsmL1_TchPlType_Amr; rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len, ft); - /* force STI bit to 0 to make sure resume after FACCH - works properly */ - l1_payload[6 + 2] &= ~16; return 0; case ST_SID_F2: *payload_type = GsmL1_TchPlType_Amr; @@ -424,7 +421,6 @@ int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len, return 1; case ST_SID_U: return -EAGAIN; - case ST_FACCH_V: case ST_FACCH: return -EBADMSG; default: @@ -598,19 +594,17 @@ struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn) switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_AMR: if (lchan->type == GSM_LCHAN_TCH_H && - lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1 && dtx_dl_amr_enabled(lchan)) { + /* we have to explicitly handle sending SID FIRST P2 for + AMR HR in here */ *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; + if (rc == 0) + return msg; + } + *payload_type = GsmL1_TchPlType_Amr; break; case GSM48_CMODE_SPEECH_V1: if (lchan->type == GSM_LCHAN_TCH_F) @@ -626,13 +620,12 @@ struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn) return NULL; } - if (dtx_dl_amr_enabled(lchan)) { - rc = repeat_last_sid(lchan, l1_payload, fn); - if (!rc) { - msgb_free(msg); - return NULL; - } - msu_param->u8Size = rc; + rc = repeat_last_sid(lchan, l1_payload, fn); + if (!rc) { + msgb_free(msg); + return NULL; } + msu_param->u8Size = rc; + return msg; } |