msg_utils.c 20.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* (C) 2014 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 <http://www.gnu.org/licenses/>.
 *
 */

Max's avatar
Max committed
20
#include <osmo-bts/dtx_dl_amr_fsm.h>
21 22 23
#include <osmo-bts/msg_utils.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/oml.h>
24
#include <osmo-bts/amr.h>
Max's avatar
Max committed
25
#include <osmo-bts/rsl.h>
26 27 28

#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmocom/gsm/protocol/gsm_12_21.h>
29
#include <osmocom/gsm/abis_nm.h>
30
#include <osmocom/core/msgb.h>
Max's avatar
Max committed
31
#include <osmocom/core/fsm.h>
Max's avatar
Max committed
32
#include <osmocom/trau/osmo_ortp.h>
33 34

#include <arpa/inet.h>
35
#include <errno.h>
36

Max's avatar
Max committed
37 38
#define STI_BIT_MASK 16

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
static int check_fom(struct abis_om_hdr *omh, size_t len)
{
	if (omh->length != len) {
		LOGP(DL1C, LOGL_ERROR, "Incorrect OM hdr length value %d %zu\n",
		     omh->length, len);
		return -1;
	}

	if (len < sizeof(struct abis_om_fom_hdr)) {
		LOGP(DL1C, LOGL_ERROR, "FOM header insufficient space %zu %zu\n",
		     len, sizeof(struct abis_om_fom_hdr));
		return -1;
	}

	return 0;
}

static int check_manuf(struct msgb *msg, struct abis_om_hdr *omh, size_t msg_size)
{
	int type;
	size_t size;

	if (msg_size < 1) {
		LOGP(DL1C, LOGL_ERROR, "No ManId Length Indicator %zu\n",
		     msg_size);
		return -1;
	}

	if (omh->data[0] >= msg_size - 1) {
		LOGP(DL1C, LOGL_ERROR,
		     "Insufficient message space for this ManId Length %d %zu\n",
		     omh->data[0], msg_size - 1);
		return -1;
	}

74 75 76
	if (omh->data[0] == sizeof(abis_nm_ipa_magic) &&
		strncmp(abis_nm_ipa_magic, (const char *)omh->data + 1,
			sizeof(abis_nm_ipa_magic)) == 0) {
77
		type = OML_MSG_TYPE_IPA;
78 79 80 81
		size = sizeof(abis_nm_ipa_magic) + 1;
	} else if (omh->data[0] == sizeof(abis_nm_osmo_magic) &&
		strncmp(abis_nm_osmo_magic, (const char *) omh->data + 1,
			sizeof(abis_nm_osmo_magic)) == 0) {
82
		type = OML_MSG_TYPE_OSMO;
83
		size = sizeof(abis_nm_osmo_magic) + 1;
84 85 86 87 88 89 90 91 92 93 94 95
	} else {
		LOGP(DL1C, LOGL_ERROR, "Manuf Label Unknown\n");
		return -1;
	}

	/* we have verified that the vendor string fits */
	msg->l3h = omh->data + size;
	if (check_fom(omh, msgb_l3len(msg)) != 0)
		return -1;
	return type;
}

Minh-Quang Nguyen's avatar
Minh-Quang Nguyen committed
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
/* check that DTX is in the middle of silence */
static inline bool dtx_is_update(const struct gsm_lchan *lchan)
{
	if (!dtx_dl_amr_enabled(lchan))
		return false;
	if (lchan->tch.dtx.dl_amr_fsm->state == ST_SID_U ||
	    lchan->tch.dtx.dl_amr_fsm->state == ST_U_NOINH)
		return true;
	return false;
}

/* check that DTX is in the beginning of silence for AMR HR */
bool dtx_is_first_p1(const struct gsm_lchan *lchan)
{
	if (!dtx_dl_amr_enabled(lchan))
		return false;
	if ((lchan->type == GSM_LCHAN_TCH_H &&
	     lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1))
		return true;
	return false;
}

118 119 120 121
/* update lchan SID status */
void lchan_set_marker(bool t, struct gsm_lchan *lchan)
{
	if (t)
Max's avatar
Max committed
122 123 124
		lchan->tch.dtx.ul_sid = true;
	else if (lchan->tch.dtx.ul_sid) {
		lchan->tch.dtx.ul_sid = false;
125 126 127 128
		lchan->rtp_tx_marker = true;
	}
}

129 130 131 132 133 134 135
/*! \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
 */
Max's avatar
Max committed
136 137
void dtx_cache_payload(struct gsm_lchan *lchan, const uint8_t *l1_payload,
		       size_t length, uint32_t fn, int update)
Max's avatar
Max committed
138
{
139
	size_t amr = (update < 0) ? 0 : 2,
140
		copy_len = OSMO_MIN(length,
Max's avatar
Max committed
141
				ARRAY_SIZE(lchan->tch.dtx.cache) - amr);
Max's avatar
Max committed
142

Max's avatar
Max committed
143
	lchan->tch.dtx.len = copy_len + amr;
Max's avatar
Max committed
144 145 146
	/* SID FIRST is special because it's both sent and cached: */
	if (update == 0) {
		lchan->tch.dtx.is_update = false; /* Mark SID FIRST explicitly */
Max's avatar
Max committed
147
		/* for non-AMR case - always update FN for incoming SID FIRST */
Minh-Quang Nguyen's avatar
Minh-Quang Nguyen committed
148
		if (!amr || !dtx_is_update(lchan))
Max's avatar
Max committed
149 150 151 152
			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 */
Max's avatar
Max committed
153
	}
Max's avatar
Max committed
154

Max's avatar
Max committed
155
	memcpy(lchan->tch.dtx.cache + amr, l1_payload, copy_len);
Max's avatar
Max committed
156 157
}

Max's avatar
Max committed
158
/*! \brief Check current state of DTX DL AMR FSM and dispatch necessary events
159 160 161 162 163
 *  \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
Max's avatar
Max committed
164
 *  \param[in] marker RTP Marker bit
Max's avatar
Max committed
165
 *  \param[out] len Length of expected L1 payload
166
 *  \param[out] ft_out Frame Type to be populated after decoding
Max's avatar
Max committed
167
 *  \returns 0 in case of success; negative on error
168
 */
Max's avatar
Max committed
169
int dtx_dl_amr_fsm_step(struct gsm_lchan *lchan, const uint8_t *rtp_pl,
170
			size_t rtp_pl_len, uint32_t fn, uint8_t *l1_payload,
Max's avatar
Max committed
171
			bool marker, uint8_t *len, uint8_t *ft_out)
172 173 174 175 176
{
	uint8_t cmr;
	enum osmo_amr_type ft;
	enum osmo_amr_quality bfi;
	int8_t sti, cmi;
Max's avatar
Max committed
177
	int rc;
178

179
	if (dtx_dl_amr_enabled(lchan)) {
Minh-Quang Nguyen's avatar
Minh-Quang Nguyen committed
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
		if (lchan->type == GSM_LCHAN_TCH_H && !rtp_pl) {
			/* we're called by gen_empty_tch_msg() to handle states
			   specific to AMR HR DTX */
			switch (lchan->tch.dtx.dl_amr_fsm->state) {
			case ST_SID_F2:
				*len = 3; /* SID-FIRST P1 -> P2 completion */
				memcpy(l1_payload, lchan->tch.dtx.cache, 2);
				rc = 0;
				dtx_dispatch(lchan, E_COMPL);
				break;
			case ST_SID_U:
				rc = -EBADMSG;
				dtx_dispatch(lchan, E_SID_U);
				break;
			default:
				rc = -EBADMSG;
			}
			return rc;
198
		}
199 200
	}

Max's avatar
Max committed
201 202 203
	if (!rtp_pl_len)
		return -EBADMSG;

Max's avatar
Max committed
204 205
	rc = osmo_amr_rtp_dec(rtp_pl, rtp_pl_len, &cmr, &cmi, &ft, &bfi, &sti);
	if (rc < 0) {
Minh-Quang Nguyen's avatar
Minh-Quang Nguyen committed
206 207
		LOGP(DRTP, LOGL_ERROR, "failed to decode AMR RTP (length %zu, "
		     "%p)\n", rtp_pl_len, rtp_pl);
Max's avatar
Max committed
208
		return rc;
209 210
	}

Max's avatar
Max committed
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
	/* 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;

231
	if (osmo_amr_is_speech(ft)) {
Minh-Quang Nguyen's avatar
Minh-Quang Nguyen committed
232
		/* AMR HR - SID-FIRST_P1 Inhibition */
233
		if (marker && lchan->tch.dtx.dl_amr_fsm->state == ST_VOICE)
Max's avatar
Max committed
234 235
			return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
						      E_INHIB, (void *)lchan);
Minh-Quang Nguyen's avatar
Minh-Quang Nguyen committed
236 237 238 239 240 241 242

		/* AMR HR - SID-UPDATE Inhibition */
		if (marker && lchan->type == GSM_LCHAN_TCH_H &&
		    lchan->tch.dtx.dl_amr_fsm->state == ST_SID_U)
			return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
						      E_INHIB, (void *)lchan);

Max's avatar
Max committed
243 244 245
		/* 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 ||
Minh-Quang Nguyen's avatar
Minh-Quang Nguyen committed
246
			       lchan->tch.dtx.dl_amr_fsm->state == ST_U_NOINH))
Max's avatar
Max committed
247 248
			return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
						      E_ONSET, (void *)lchan);
Minh-Quang Nguyen's avatar
Minh-Quang Nguyen committed
249 250 251 252 253 254

		if (lchan->tch.dtx.dl_amr_fsm->state != ST_VOICE)
			return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
						      E_VOICE, (void *)lchan);

		return 0;
Max's avatar
Max committed
255 256 257
	}

	if (ft == AMR_SID) {
Max's avatar
Max committed
258 259 260 261 262
		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);
Max's avatar
Max committed
263
			return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
264 265
						      sti ? E_SID_U : E_SID_F,
						      (void *)lchan);
Minh-Quang Nguyen's avatar
Minh-Quang Nguyen committed
266
		} else if (lchan->tch.dtx.dl_amr_fsm->state != ST_FACCH)
Max's avatar
Max committed
267
			dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, sti);
Minh-Quang Nguyen's avatar
Minh-Quang Nguyen committed
268 269 270
		if (lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F2)
			return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
						      E_COMPL, (void *)lchan);
Max's avatar
Max committed
271 272 273 274 275 276 277 278
		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;
279 280
	}

Max's avatar
Max committed
281 282 283 284
	if (marker)
		osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, E_VOICE,
				       (void *)lchan);
	*len = 0;
285 286 287
	return 0;
}

Max's avatar
Max committed
288 289 290
/* STI is located in payload byte 6, cache contains 2 byte prefix (CMR/CMI)
 * STI set = SID UPDATE, STI unset = SID FIRST
 */
Max's avatar
Max committed
291 292 293 294
static inline void dtx_sti_set(struct gsm_lchan *lchan)
{
	lchan->tch.dtx.cache[6 + 2] |= STI_BIT_MASK;
}
Max's avatar
Max committed
295

Max's avatar
Max committed
296 297 298 299 300
static inline void dtx_sti_unset(struct gsm_lchan *lchan)
{
	lchan->tch.dtx.cache[6 + 2] &= ~STI_BIT_MASK;
}

Max's avatar
Max committed
301 302 303 304 305
/*! \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
 */
Max's avatar
Max committed
306
static inline bool dtx_amr_sid_optional(struct gsm_lchan *lchan, uint32_t fn)
Max's avatar
Max committed
307
{
Max's avatar
Max committed
308 309 310
	if (!dtx_dl_amr_enabled(lchan))
		return true;

Max's avatar
Max committed
311 312
	/* Compute approx. time delta x26 based on Fn duration */
	uint32_t dx26 = 120 * (fn - lchan->tch.dtx.fn);
Max's avatar
Max committed
313

Max's avatar
Max committed
314
	/* We're resuming after FACCH interruption */
Max's avatar
Max committed
315
	if (lchan->tch.dtx.dl_amr_fsm->state == ST_FACCH) {
Max's avatar
Max committed
316
		/* force STI bit to 0 so cache is treated as SID FIRST */
Max's avatar
Max committed
317
		dtx_sti_unset(lchan);
Max's avatar
Max committed
318
		lchan->tch.dtx.is_update = false;
Max's avatar
Max committed
319 320 321 322
		/* 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 */
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
		if(lchan->type == GSM_LCHAN_TCH_H) {
			if (lchan->tch.dtx.fn != LCHAN_FN_DUMMY &&
			    lchan->tch.dtx.fn != LCHAN_FN_WAIT) {
				/* FACCH interruption is over */
				dtx_dispatch(lchan, E_COMPL);
				return false;
			} else if(lchan->tch.dtx.fn == LCHAN_FN_DUMMY) {
				lchan->tch.dtx.fn = LCHAN_FN_WAIT;
			} else
				lchan->tch.dtx.fn = fn;
		} else if(lchan->type == GSM_LCHAN_TCH_F) {
			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;
		}
Max's avatar
Max committed
341 342
		/* this FN was already used for FACCH or ONSET message so we just
		   prepare things for next one */
Max's avatar
Max committed
343 344 345
		return true;
	}

Max's avatar
Max committed
346 347 348
	if (lchan->tch.dtx.dl_amr_fsm->state == ST_VOICE)
		return true;

Max's avatar
Max committed
349 350 351 352
	/* 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)
Max's avatar
Max committed
353 354 355 356
			return true;
		return false;
	}
	/* 3rd frame after SID FIRST should be SID UPDATE */
Max's avatar
Max committed
357
	if (dx26 < GSM_RTP_FRAME_DURATION_MS * 3 * 26 - 120)
Max's avatar
Max committed
358 359 360 361
		return true;
	return false;
}

Max's avatar
Max committed
362
static inline bool fn_chk(const uint8_t *t, uint32_t fn, uint8_t len)
Max's avatar
Max committed
363 364
{
	uint8_t i;
Max's avatar
Max committed
365
	for (i = 0; i < len; i++)
Max's avatar
Max committed
366 367 368 369 370 371 372 373 374 375
		if (fn % 104 == t[i])
			return false;
	return true;
}

/*! \brief Check if TX scheduling is optional for a given FN in case of DTX
 *  \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
 */
376
static inline bool dtx_sched_optional(struct gsm_lchan *lchan, uint32_t fn)
Max's avatar
Max committed
377 378
{
	/* According to 3GPP TS 45.008 § 8.3: */
Max's avatar
Max committed
379 380 381
	static const uint8_t f[] = { 52, 53, 54, 55, 56, 57, 58, 59 },
				h0[] = { 0, 2, 4, 6, 52, 54, 56, 58 },
				h1[] = { 14, 16, 18, 20, 66, 68, 70, 72 };
382
	if ((lchan->tch_mode == GSM48_CMODE_SPEECH_V1) || (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)){
Max's avatar
Max committed
383
		if (lchan->type == GSM_LCHAN_TCH_F)
Max's avatar
Max committed
384
			return fn_chk(f, fn, ARRAY_SIZE(f));
Max's avatar
Max committed
385
		else
Max's avatar
Max committed
386
			return fn_chk(lchan->nr ? h1 : h0, fn,
Max's avatar
Max committed
387 388
				      lchan->nr ? ARRAY_SIZE(h1) :
				      ARRAY_SIZE(h0));
Max's avatar
Max committed
389 390 391 392
	}
	return false;
}

Max's avatar
Max committed
393 394 395 396 397
/*! \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
 */
398 399 400 401 402 403 404 405 406
bool dtx_dl_amr_enabled(const struct gsm_lchan *lchan)
{
	if (lchan->ts->trx->bts->dtxd &&
	    lchan->tch.dtx.dl_amr_fsm &&
	    lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
		return true;
	return false;
}

Max's avatar
Max committed
407 408 409 410 411 412 413 414 415 416
/*! \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;

417 418 419 420 421 422 423 424
	if (lchan->tch.dtx.dl_amr_fsm->state == ST_U_INH_V ||
	    lchan->tch.dtx.dl_amr_fsm->state == ST_U_INH_F ||
	    lchan->tch.dtx.dl_amr_fsm->state == ST_U_INH_V_REC ||
	    lchan->tch.dtx.dl_amr_fsm->state == ST_U_INH_F_REC ||
	    lchan->tch.dtx.dl_amr_fsm->state == ST_F1_INH_V ||
	    lchan->tch.dtx.dl_amr_fsm->state == ST_F1_INH_F ||
	    lchan->tch.dtx.dl_amr_fsm->state == ST_F1_INH_V_REC ||
	    lchan->tch.dtx.dl_amr_fsm->state == ST_F1_INH_F_REC ||
Max's avatar
Max committed
425 426 427 428 429 430 431 432 433 434 435 436 437
	    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
 */
Max's avatar
Max committed
438 439 440 441 442 443 444
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);
}

Max's avatar
Max committed
445 446 447 448 449 450 451 452 453 454 455
/*! \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;

Minh-Quang Nguyen's avatar
Minh-Quang Nguyen committed
456
	if (dtx_is_first_p1(lchan) || dtx_recursion(lchan))
Max's avatar
Max committed
457 458 459
		dtx_dispatch(lchan, E_COMPL);
}

460 461 462 463 464 465 466 467
/*! \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)
{
468
	uint8_t pl_len;
469 470 471 472 473 474 475
	/* 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;
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
		else {
			if (lchan->ts->trx->bts->dtxd) {
				/* Need to send zeroed TCH frame on mandatory Fn defined in GSM 05.08, section 8.3 */
				switch (lchan->type) {
				case GSM_LCHAN_TCH_F:
					pl_len = GSM_FR_BYTES;
					break;
				case GSM_LCHAN_TCH_H:
					pl_len = GSM_HR_BYTES;
					break;
				default:
					return 0;
				}
				memcpy(dst, gsm_speech_zero, pl_len);

				LOGP(DL1C, LOGL_DEBUG, "%s Have to send %s %s zero speech frame, Fn=%d, Fn mod 104=%d, dump=%s\n",
									gsm_lchan_name(lchan),
									get_value_string(gsm_chan_t_names, lchan->type),
									get_value_string(gsm48_chan_mode_names, lchan->tch_mode),
									fn,
									fn%104,
									osmo_hexdump(dst, pl_len));
				return pl_len + 1;
			}
		}
	} else {
		if (!dtx_sched_optional(lchan, fn) && (lchan->rsl_cmode == RSL_CMOD_SPD_SPEECH)) {
			if (!(lchan->tch.dtx.len)) {
				pl_len = 9;
				/* Send zero SID FIRST frame */
				memcpy(dst, amr_sid_first_zero, pl_len);
				LOGP(DL1C, LOGL_DEBUG, "%s Have to send %s %s zero speech frame, Fn=%d, Fn mod 104=%d, dump=%s\n",
						gsm_lchan_name(lchan),
						get_value_string(gsm_chan_t_names, lchan->type),
						get_value_string(gsm48_chan_mode_names, lchan->tch_mode),
						fn,
						fn%104,
						osmo_hexdump(dst, pl_len));
				return pl_len + 1; /* plus one to take into account of payload type */
			}
		}

518 519
		if (dtx_amr_sid_optional(lchan, fn))
			return 0;
520
	}
521

Max's avatar
Max committed
522
	if (lchan->tch.dtx.len) {
523

Max's avatar
Max committed
524 525 526 527 528 529 530 531 532 533
		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);
Minh-Quang Nguyen's avatar
Minh-Quang Nguyen committed
534
			} else if (dtx_is_update(lchan)) {
Max's avatar
Max committed
535 536 537
				/* enforce SID UPDATE for next repetition: it
				   might have been altered by FACCH handling */
				dtx_sti_set(lchan);
Minh-Quang Nguyen's avatar
Minh-Quang Nguyen committed
538 539 540 541 542 543 544
				if (lchan->type == GSM_LCHAN_TCH_H &&
				    lchan->tch.dtx.dl_amr_fsm->state ==
				    ST_U_NOINH)
					osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
							       E_COMPL,
							       (void *)lchan);
				lchan->tch.dtx.is_update = true;
Max's avatar
Max committed
545 546
			}
		}
Max's avatar
Max committed
547 548
		memcpy(dst, lchan->tch.dtx.cache, lchan->tch.dtx.len);
		lchan->tch.dtx.fn = fn;
549 550 551 552 553
		LOGP(DL1C, LOGL_DEBUG, "%s Have to send %s SID buffer "
			     "%s\n",
				 gsm_lchan_name(lchan),
			     get_value_string(gsm48_chan_mode_names, lchan->tch_mode),
				 osmo_hexdump(lchan->tch.dtx.cache, lchan->tch.dtx.len));
Max's avatar
Max committed
554
		return lchan->tch.dtx.len + 1;
555 556
	}

557
	LOGP(DL1C, LOGL_DEBUG, "%s Have to send %s frame on TCH but SID buffer "
558
	     "is empty - sent nothing\n",
559
		 gsm_lchan_name(lchan),
560 561 562 563 564
	     get_value_string(gsm48_chan_mode_names, lchan->tch_mode));

	return 0;
}

565 566 567 568 569 570 571 572 573 574
/**
 * Return 0 in case the IPA structure is okay and in this
 * case the l2h will be set to the beginning of the data.
 */
int msg_verify_ipa_structure(struct msgb *msg)
{
	struct ipaccess_head *hh;

	if (msgb_l1len(msg) < sizeof(struct ipaccess_head)) {
		LOGP(DL1C, LOGL_ERROR,
575
			"Ipa header insufficient space %d %zu\n",
576 577 578 579 580 581 582 583
			msgb_l1len(msg), sizeof(struct ipaccess_head));
		return -1;
	}

	hh = (struct ipaccess_head *) msg->l1h;

	if (ntohs(hh->len) != msgb_l1len(msg) - sizeof(struct ipaccess_head)) {
		LOGP(DL1C, LOGL_ERROR,
584
			"Incorrect ipa header msg size %d %zu\n",
585 586 587 588
			ntohs(hh->len), msgb_l1len(msg) - sizeof(struct ipaccess_head));
		return -1;
	}

589 590 591 592 593 594 595 596 597 598
	if (hh->proto == IPAC_PROTO_OSMO) {
		struct ipaccess_head_ext *hh_ext = (struct ipaccess_head_ext *) hh->data;
		if (ntohs(hh->len) < sizeof(*hh_ext)) {
			LOGP(DL1C, LOGL_ERROR, "IPA length shorter than OSMO header\n");
			return -1;
		}
		msg->l2h = hh_ext->data;
	} else
		msg->l2h = hh->data;

599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
	return 0;
}

/**
 * \brief Verify the structure of the OML message and set l3h
 *
 * This function verifies that the data in \param in msg is a proper
 * OML message. This code assumes that msg->l2h points to the
 * beginning of the OML message. In the successful case the msg->l3h
 * will be set and will point to the FOM header. The value is undefined
 * in all other cases.
 *
 * \param msg The message to analyze starting from msg->l2h.
 * \return In case the structure is correct a positive number will be
 * returned and msg->l3h will point to the FOM. The number is a
 * classification of the vendor type of the message.
 */
int msg_verify_oml_structure(struct msgb *msg)
{
	struct abis_om_hdr *omh;

	if (msgb_l2len(msg) < sizeof(*omh)) {
621
		LOGP(DL1C, LOGL_ERROR, "Om header insufficient space %d %zu\n",
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
		     msgb_l2len(msg), sizeof(*omh));
		return -1;
	}

	omh = (struct abis_om_hdr *) msg->l2h;
	if (omh->mdisc != ABIS_OM_MDISC_FOM &&
	    omh->mdisc != ABIS_OM_MDISC_MANUF) {
		LOGP(DL1C, LOGL_ERROR, "Incorrect om mdisc value %x\n",
		     omh->mdisc);
		return -1;
	}

	if (omh->placement != ABIS_OM_PLACEMENT_ONLY) {
		LOGP(DL1C, LOGL_ERROR, "Incorrect om placement value %x %x\n",
		     omh->placement, ABIS_OM_PLACEMENT_ONLY);
		return -1;
	}

	if (omh->sequence != 0) {
		LOGP(DL1C, LOGL_ERROR, "Incorrect om sequence value %d\n",
		     omh->sequence);
		return -1;
	}

	if (omh->mdisc == ABIS_OM_MDISC_MANUF)
		return check_manuf(msg, omh, msgb_l2len(msg) - sizeof(*omh));

	msg->l3h = omh->data;
	if (check_fom(omh, msgb_l3len(msg)) != 0)
		return -1;
	return OML_MSG_TYPE_ETSI;
}