示例#1
0
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;
}
示例#2
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);
}
示例#3
0
/*! \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;
}
示例#4
0
/*! \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;
}
示例#5
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;
}
示例#6
0
/*! \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);
}
示例#7
0
/*! \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;
}
示例#8
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);
}
示例#9
0
/*! \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;
}