aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax <msuraev@sysmocom.de>2016-10-26 17:28:42 +0200
committerHarald Welte <laforge@gnumonks.org>2016-10-28 13:42:29 +0000
commitbce25a60f4bbd798b98727e66b25bf343031f7bc (patch)
tree5e71659c7f73d6db41f4897098fd6860c0957422
parentefb4a4baeb8ecd13ff41f25d61fd91a1162e6fa9 (diff)
DTX DL: split ONSET state handling
Handle ONSET cause by Voice and FACCH separately. In case of Voice we have RTP payload which we have to cache and send later on in next response to L1 RTS. FACCH have higher priority so it preempts both voice and silence alike - hence we can send ONSET immediately but still have to track previous state in order to get back to it gracefully. This affects lc15 and sysmo hw as there's no FSM-based DTX implementation for other models yet. Note: this requires patch for OpenBSC which adds FACCH buffer to tch.dtx struct. Change-Id: Idba14dcd0cb12cd7aee86391fcc152c49fcd7052 Related: OS#1802
-rw-r--r--include/osmo-bts/dtx_dl_amr_fsm.h1
-rw-r--r--src/common/dtx_dl_amr_fsm.c23
-rw-r--r--src/common/msg_utils.c21
-rw-r--r--src/osmo-bts-litecell15/l1_if.c49
-rw-r--r--src/osmo-bts-litecell15/tch.c6
-rw-r--r--src/osmo-bts-sysmo/l1_if.c49
-rw-r--r--src/osmo-bts-sysmo/tch.c6
7 files changed, 120 insertions, 35 deletions
diff --git a/include/osmo-bts/dtx_dl_amr_fsm.h b/include/osmo-bts/dtx_dl_amr_fsm.h
index 8b195953..5c13c198 100644
--- a/include/osmo-bts/dtx_dl_amr_fsm.h
+++ b/include/osmo-bts/dtx_dl_amr_fsm.h
@@ -16,7 +16,6 @@ enum dtx_dl_amr_fsm_states {
ST_U_INH,
ST_SID_U,
ST_ONSET_V,
- ST_ONSET_F,
ST_FACCH_V,
ST_FACCH,
};
diff --git a/src/common/dtx_dl_amr_fsm.c b/src/common/dtx_dl_amr_fsm.c
index b110cf21..a75fd00e 100644
--- a/src/common/dtx_dl_amr_fsm.c
+++ b/src/common/dtx_dl_amr_fsm.c
@@ -53,7 +53,7 @@ void dtx_fsm_sid_f1(struct osmo_fsm_inst *fi, uint32_t event, void *data)
osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
break;
case E_FACCH:
- osmo_fsm_inst_state_chg(fi, ST_ONSET_F, 0, 0);
+ osmo_fsm_inst_state_chg(fi, ST_FACCH, 0, 0);
break;
case E_COMPL:
osmo_fsm_inst_state_chg(fi, ST_SID_F2, 0, 0);
@@ -81,7 +81,7 @@ void dtx_fsm_sid_f2(struct osmo_fsm_inst *fi, uint32_t event, void *data)
osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
break;
case E_FACCH:
- osmo_fsm_inst_state_chg(fi, ST_ONSET_F, 0, 0);
+ osmo_fsm_inst_state_chg(fi, ST_FACCH, 0, 0);
break;
default:
LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
@@ -97,7 +97,7 @@ void dtx_fsm_f1_inh(struct osmo_fsm_inst *fi, uint32_t event, void *data)
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);
+ osmo_fsm_inst_state_chg(fi, ST_FACCH, 0, 0);
break;
default:
LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
@@ -113,7 +113,7 @@ void dtx_fsm_u_inh(struct osmo_fsm_inst *fi, uint32_t event, void *data)
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);
+ osmo_fsm_inst_state_chg(fi, ST_FACCH, 0, 0);
break;
default:
LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
@@ -126,7 +126,7 @@ void dtx_fsm_sid_upd(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case E_FACCH:
- osmo_fsm_inst_state_chg(fi, ST_ONSET_F, 0, 0);
+ osmo_fsm_inst_state_chg(fi, ST_FACCH, 0, 0);
break;
case E_VOICE:
osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
@@ -234,7 +234,7 @@ static struct osmo_fsm_state dtx_dl_amr_fsm_states[] = {
start of silence period (might be interrupted in case of AMR HR) */
[ST_SID_F1]= {
.in_event_mask = X(E_SID_F) | X(E_SID_U) | X(E_VOICE) | X(E_FACCH) | X(E_COMPL) | X(E_INHIB) | X(E_ONSET),
- .out_state_mask = X(ST_SID_U) | X(ST_VOICE) | X(ST_ONSET_F) | X(ST_SID_F2) | X(ST_F1_INH) | X(ST_ONSET_V),
+ .out_state_mask = X(ST_SID_U) | X(ST_VOICE) | X(ST_FACCH) | X(ST_SID_F2) | X(ST_F1_INH) | X(ST_ONSET_V),
.name = "SID-FIRST (P1)",
.action = dtx_fsm_sid_f1,
},
@@ -242,7 +242,7 @@ static struct osmo_fsm_state dtx_dl_amr_fsm_states[] = {
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),
- .out_state_mask = X(ST_SID_U) | X(ST_VOICE) | X(ST_ONSET_F),
+ .out_state_mask = X(ST_SID_U) | X(ST_VOICE) | X(ST_FACCH),
.name = "SID-FIRST (P2)",
.action = dtx_fsm_sid_f2,
},
@@ -265,7 +265,7 @@ static struct osmo_fsm_state dtx_dl_amr_fsm_states[] = {
/* 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),
- .out_state_mask = X(ST_ONSET_F) | X(ST_VOICE) | X(ST_U_INH) | X(ST_SID_U) | X(ST_ONSET_V),
+ .out_state_mask = X(ST_FACCH) | X(ST_VOICE) | X(ST_U_INH) | X(ST_SID_U) | X(ST_ONSET_V),
.name = "SID-UPDATE",
.action = dtx_fsm_sid_upd,
},
@@ -276,13 +276,6 @@ static struct osmo_fsm_state dtx_dl_amr_fsm_states[] = {
.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),
- .out_state_mask = X(ST_VOICE) | X(ST_FACCH),
- .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]= {
diff --git a/src/common/msg_utils.c b/src/common/msg_utils.c
index 9c6f20f1..4b213665 100644
--- a/src/common/msg_utils.c
+++ b/src/common/msg_utils.c
@@ -220,12 +220,23 @@ int dtx_dl_amr_fsm_step(struct gsm_lchan *lchan, const uint8_t *rtp_pl,
* \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(const struct gsm_lchan *lchan,
- uint32_t fn)
+static inline bool dtx_amr_sid_optional(struct gsm_lchan *lchan, uint32_t fn)
{
/* 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 */
+ lchan->tch.dtx.cache[6 + 2] &= ~16;
+ 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 */
+ 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 */
@@ -293,7 +304,11 @@ uint8_t repeat_last_sid(struct gsm_lchan *lchan, uint8_t *dst, uint32_t fn)
if (lchan->tch.dtx.len) {
memcpy(dst, lchan->tch.dtx.cache, lchan->tch.dtx.len);
lchan->tch.dtx.fn = fn;
- lchan->tch.dtx.is_update = true; /* SID UPDATE sent */
+ /* enforce SID UPDATE for next repetition - it might have
+ been altered by FACCH handling */
+ lchan->tch.dtx.cache[6 + 2] |= 16;
+ 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 edc5f5b7..795172bb 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/dtx_dl_amr_fsm.h>
#include <nrw/litecell15/litecell15.h>
#include <nrw/litecell15/gsml1prim.h>
@@ -330,13 +331,15 @@ empty_req_from_l1sap(GsmL1_Prim_t *l1p, struct lc15l1_hdl *fl1,
}
static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
- struct osmo_phsap_prim *l1sap)
+ struct osmo_phsap_prim *l1sap, bool use_cache)
{
struct lc15l1_hdl *fl1 = trx_lc15l1_hdl(trx);
struct msgb *l1msg = l1p_msgb_alloc();
+ struct gsm_lchan *lchan;
uint32_t u32Fn;
uint8_t u8Tn, subCh, u8BlockNbr = 0, sapi = 0;
uint8_t chan_nr, link_id;
+ bool rec = false;
int len;
if (!msg) {
@@ -401,14 +404,46 @@ 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);
+
+ if (use_cache)
+ memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer,
+ lchan->tch.dtx.facch, msgb_l2len(msg));
+ else if (trx->bts->dtxd && lchan->tch.dtx.dl_amr_fsm &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_FACCH) {
+ if (sapi == GsmL1_Sapi_FacchF) {
+ sapi = GsmL1_Sapi_TchF;
+ }
+ if (sapi == GsmL1_Sapi_FacchH) {
+ sapi = GsmL1_Sapi_TchH;
+ }
+ if (sapi == GsmL1_Sapi_TchH || sapi == GsmL1_Sapi_TchF) {
+ /* FACCH interruption of DTX silence */
+ /* cache FACCH data */
+ 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;
+ }
+ }
data_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr, len);
- OSMO_ASSERT(msgb_l2len(msg) <= sizeof(l1p->u.phDataReq.msgUnitParam.u8Buffer));
- memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer, msg->l2h, msgb_l2len(msg));
+ if (!rec && !use_cache) {
+ OSMO_ASSERT(msgb_l2len(msg) <= sizeof(l1p->u.phDataReq.msgUnitParam.u8Buffer));
+ memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer, msg->l2h,
+ msgb_l2len(msg));
+ }
LOGP(DL1P, LOGL_DEBUG, "PH-DATA.req(%s)\n",
- osmo_hexdump(l1p->u.phDataReq.msgUnitParam.u8Buffer,
- l1p->u.phDataReq.msgUnitParam.u8Size));
+ osmo_hexdump(l1p->u.phDataReq.msgUnitParam.u8Buffer,
+ l1p->u.phDataReq.msgUnitParam.u8Size));
} else {
/* empty frame */
GsmL1_Prim_t *l1p = msgb_l1prim(l1msg);
@@ -422,6 +457,8 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
msgb_free(l1msg);
}
+ if (rec)
+ ph_data_req(trx, msg, l1sap, true);
return 0;
}
@@ -566,7 +603,7 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
* free()d below */
switch (OSMO_PRIM_HDR(&l1sap->oph)) {
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST):
- rc = ph_data_req(trx, msg, l1sap);
+ rc = ph_data_req(trx, msg, l1sap, false);
break;
case OSMO_PRIM(PRIM_TCH, PRIM_OP_REQUEST):
rc = ph_tch_req(trx, msg, l1sap, false, l1sap->u.tch.marker);
diff --git a/src/osmo-bts-litecell15/tch.c b/src/osmo-bts-litecell15/tch.c
index b2513887..70764f5f 100644
--- a/src/osmo-bts-litecell15/tch.c
+++ b/src/osmo-bts-litecell15/tch.c
@@ -285,10 +285,9 @@ int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
/* DTX DL-specific logic below: */
switch (lchan->tch.dtx.dl_amr_fsm->state) {
case ST_ONSET_V:
- case ST_ONSET_F:
*payload_type = GsmL1_TchPlType_Amr_Onset;
dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
- *len = 1;
+ *len = 3;
return 1;
case ST_VOICE:
*payload_type = GsmL1_TchPlType_Amr;
@@ -306,6 +305,9 @@ 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;
diff --git a/src/osmo-bts-sysmo/l1_if.c b/src/osmo-bts-sysmo/l1_if.c
index bef2d303..f7585ce8 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/dtx_dl_amr_fsm.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h>
@@ -325,13 +326,15 @@ empty_req_from_l1sap(GsmL1_Prim_t *l1p, struct femtol1_hdl *fl1,
}
static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
- struct osmo_phsap_prim *l1sap)
+ struct osmo_phsap_prim *l1sap, bool use_cache)
{
struct femtol1_hdl *fl1 = trx_femtol1_hdl(trx);
struct msgb *l1msg = l1p_msgb_alloc();
+ struct gsm_lchan *lchan;
uint32_t u32Fn;
uint8_t u8Tn, subCh, u8BlockNbr = 0, sapi = 0;
uint8_t chan_nr, link_id;
+ bool rec = false;
int len;
if (!msg) {
@@ -396,14 +399,46 @@ 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);
+
+ if (use_cache)
+ memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer,
+ lchan->tch.dtx.facch, msgb_l2len(msg));
+ else if (trx->bts->dtxd && lchan->tch.dtx.dl_amr_fsm &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_FACCH) {
+ if (sapi == GsmL1_Sapi_FacchF) {
+ sapi = GsmL1_Sapi_TchF;
+ }
+ if (sapi == GsmL1_Sapi_FacchH) {
+ sapi = GsmL1_Sapi_TchH;
+ }
+ if (sapi == GsmL1_Sapi_TchH || sapi == GsmL1_Sapi_TchF) {
+ /* FACCH interruption of DTX silence */
+ /* cache FACCH data */
+ 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;
+ }
+ }
data_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr, len);
- OSMO_ASSERT(msgb_l2len(msg) <= sizeof(l1p->u.phDataReq.msgUnitParam.u8Buffer));
- memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer, msg->l2h, msgb_l2len(msg));
+ if (!rec && !use_cache) {
+ OSMO_ASSERT(msgb_l2len(msg) <= sizeof(l1p->u.phDataReq.msgUnitParam.u8Buffer));
+ memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer, msg->l2h,
+ msgb_l2len(msg));
+ }
LOGP(DL1P, LOGL_DEBUG, "PH-DATA.req(%s)\n",
- osmo_hexdump(l1p->u.phDataReq.msgUnitParam.u8Buffer,
- l1p->u.phDataReq.msgUnitParam.u8Size));
+ osmo_hexdump(l1p->u.phDataReq.msgUnitParam.u8Buffer,
+ l1p->u.phDataReq.msgUnitParam.u8Size));
} else {
/* empty frame */
GsmL1_Prim_t *l1p = msgb_l1prim(l1msg);
@@ -417,6 +452,8 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
msgb_free(l1msg);
}
+ if (rec)
+ ph_data_req(trx, msg, l1sap, true);
return 0;
}
@@ -561,7 +598,7 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
* free()d below */
switch (OSMO_PRIM_HDR(&l1sap->oph)) {
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST):
- rc = ph_data_req(trx, msg, l1sap);
+ rc = ph_data_req(trx, msg, l1sap, false);
break;
case OSMO_PRIM(PRIM_TCH, PRIM_OP_REQUEST):
rc = ph_tch_req(trx, msg, l1sap, false, l1sap->u.tch.marker);
diff --git a/src/osmo-bts-sysmo/tch.c b/src/osmo-bts-sysmo/tch.c
index b08ba7e1..fbb42b27 100644
--- a/src/osmo-bts-sysmo/tch.c
+++ b/src/osmo-bts-sysmo/tch.c
@@ -383,10 +383,9 @@ int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
/* DTX DL-specific logic below: */
switch (lchan->tch.dtx.dl_amr_fsm->state) {
case ST_ONSET_V:
- case ST_ONSET_F:
*payload_type = GsmL1_TchPlType_Amr_Onset;
dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
- *len = 1;
+ *len = 3;
return 1;
case ST_VOICE:
*payload_type = GsmL1_TchPlType_Amr;
@@ -404,6 +403,9 @@ 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;