struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn) { struct msgb *msg; GsmL1_Prim_t *l1p; GsmL1_PhDataReq_t *data_req; GsmL1_MsgUnitParam_t *msu_param; uint8_t *payload_type; uint8_t *l1_payload; int rc; msg = l1p_msgb_alloc(); if (!msg) return NULL; l1p = msgb_l1prim(msg); data_req = &l1p->u.phDataReq; msu_param = &data_req->msgUnitParam; payload_type = &msu_param->u8Buffer[0]; l1_payload = &msu_param->u8Buffer[1]; switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_AMR: if (lchan->type == GSM_LCHAN_TCH_H && 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) return msg; } *payload_type = GsmL1_TchPlType_Amr; break; case GSM48_CMODE_SPEECH_V1: if (lchan->type == GSM_LCHAN_TCH_F) *payload_type = GsmL1_TchPlType_Fr; else *payload_type = GsmL1_TchPlType_Hr; break; case GSM48_CMODE_SPEECH_EFR: *payload_type = GsmL1_TchPlType_Efr; break; default: msgb_free(msg); return NULL; } rc = repeat_last_sid(lchan, l1_payload, fn); if (!rc) { msgb_free(msg); return NULL; } msu_param->u8Size = rc; return msg; }
/*! \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 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 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 * \returns Number of bytes copied + 1 (to accommodate for extra byte with * payload type), 0 if there's nothing to copy */ uint8_t repeat_last_sid(struct gsm_lchan *lchan, uint8_t *dst, uint32_t fn) { /* FIXME: add EFR support */ if (lchan->tch_mode == GSM48_CMODE_SPEECH_EFR) return 0; if (lchan->tch_mode != GSM48_CMODE_SPEECH_AMR) { if (dtx_sched_optional(lchan, fn)) return 0; } else if (dtx_amr_sid_optional(lchan, 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; return lchan->tch.dtx.len + 1; } LOGP(DL1C, LOGL_DEBUG, "Have to send %s frame on TCH but SID buffer " "is empty - sent nothing\n", get_value_string(gsm48_chan_mode_names, lchan->tch_mode)); return 0; }
/*! \brief Check if enough time has passed since last SID (if any) to repeat it * \param[in] lchan Logical channel on which we check scheduling * \param[in] fn Frame Number for which we check scheduling * \returns true if transmission can be omitted, false otherwise */ static inline bool dtx_amr_sid_optional(struct gsm_lchan *lchan, uint32_t fn) { if (!dtx_dl_amr_enabled(lchan)) return true; /* Compute approx. time delta x26 based on Fn duration */ 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) { /* force STI bit to 0 so cache is treated as SID FIRST */ dtx_sti_unset(lchan); lchan->tch.dtx.is_update = false; /* 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 */ if (dx26 < GSM_RTP_FRAME_DURATION_MS * 8 * 26 - 120) return true; return false; } /* 3rd frame after SID FIRST should be SID UPDATE */ if (dx26 < GSM_RTP_FRAME_DURATION_MS * 3 * 26 - 120) return true; return false; }
/*! \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 */ 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.dtx.cache) - amr); lchan->tch.dtx.len = copy_len + amr; /* SID FIRST is special because it's both sent and cached: */ if (update == 0) { 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); }
/*! \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 (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. * * Note that the actual L1 primitive header is not fully initialized * yet, as things like the frame number, etc. are unknown at the time we * 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 use_cache, bool marker) { uint8_t *payload_type; uint8_t *l1_payload, ft; int rc = 0; bool is_sid = false; DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan), osmo_hexdump(rtp_pl, rtp_pl_len)); payload_type = &data[0]; l1_payload = &data[1]; switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_V1: if (lchan->type == GSM_LCHAN_TCH_F) { *payload_type = GsmL1_TchPlType_Fr; rc = rtppayload_to_l1_fr(l1_payload, rtp_pl, rtp_pl_len); 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 && lchan->ts->trx->bts->dtxd) is_sid = osmo_hr_check_sid(rtp_pl, rtp_pl_len); } if (is_sid) 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: *payload_type = GsmL1_TchPlType_Efr; rc = rtppayload_to_l1_efr(l1_payload, rtp_pl, rtp_pl_len); /* FIXME: detect and save EFR SID */ break; #endif case GSM48_CMODE_SPEECH_AMR: 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 (!dtx_dl_amr_enabled(lchan)) { *payload_type = GsmL1_TchPlType_Amr; rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len, ft); return 0; } /* DTX DL-specific logic below: */ switch (lchan->tch.dtx.dl_amr_fsm->state) { case ST_ONSET_V: *payload_type = GsmL1_TchPlType_Amr_Onset; dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0); *len = 3; 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; } /* AMR FR */ *payload_type = GsmL1_TchPlType_Amr; 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: return -EBADMSG; default: LOGP(DRTP, LOGL_ERROR, "Unhandled DTX DL AMR FSM state " "%d\n", lchan->tch.dtx.dl_amr_fsm->state); return -EINVAL; } break; default: /* we don't support CSD modes */ rc = -1; break; } if (rc < 0) { LOGP(DRTP, LOGL_ERROR, "%s unable to parse RTP payload\n", gsm_lchan_name(lchan)); return -EBADMSG; } *len = rc + 1; DEBUGP(DRTP, "%s RTP->L1: %s\n", gsm_lchan_name(lchan), osmo_hexdump(data, *len)); return 0; }
/*! \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)) osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, e, (void *)lchan); }
/*! \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[in] marker RTP Marker bit * \param[out] len Length of expected L1 payload * \param[out] ft_out Frame Type to be populated after decoding * \returns 0 in case of success; negative on error */ 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, 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; int rc; if (dtx_dl_amr_enabled(lchan)) { if (lchan->type == GSM_LCHAN_TCH_H && lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F2 && !rtp_pl) { *len = 3; /* SID-FIRST P1 -> P2 completion */ memcpy(l1_payload, lchan->tch.dtx.cache, 2); 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", 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)) { /* AMR HR - Inhibition */ if (lchan->type == GSM_LCHAN_TCH_H && marker && lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1) return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, E_INHIB, (void *)lchan); /* AMR FR & HR - generic */ if (marker && (lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1 || lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F2 || 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) { 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); } 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; }