aboutsummaryrefslogtreecommitdiffstats
path: root/src/common/l1sap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/l1sap.c')
-rw-r--r--src/common/l1sap.c1706
1 files changed, 1357 insertions, 349 deletions
diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index f07e79ca..5f275cd8 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -13,7 +13,7 @@
* 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.
+ * GNU Affero 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/>.
@@ -33,10 +33,13 @@
#include <osmocom/gsm/l1sap.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/rsl.h>
+#include <osmocom/gsm/rlp.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/utils.h>
+#include <osmocom/codec/codec.h>
+
#include <osmocom/trau/osmo_ortp.h>
#include <osmo-bts/logging.h>
@@ -51,41 +54,17 @@
#include <osmo-bts/abis.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/handover.h>
-#include <osmo-bts/power_control.h>
#include <osmo-bts/msg_utils.h>
+#include <osmo-bts/rtp_input_preen.h>
#include <osmo-bts/pcuif_proto.h>
#include <osmo-bts/cbch.h>
-
-
-#define CB_FCCH -1
-#define CB_SCH -2
-#define CB_BCCH -3
-#define CB_IDLE -4
-
-/* according to TS 05.02 Clause 7 Table 3 of 9 an Figure 8a */
-static const int ccch_block_table[51] = {
- CB_FCCH, CB_SCH,/* 0..1 */
- CB_BCCH, CB_BCCH, CB_BCCH, CB_BCCH, /* 2..5: BCCH */
- 0, 0, 0, 0, /* 6..9: B0 */
- CB_FCCH, CB_SCH,/* 10..11 */
- 1, 1, 1, 1, /* 12..15: B1 */
- 2, 2, 2, 2, /* 16..19: B2 */
- CB_FCCH, CB_SCH,/* 20..21 */
- 3, 3, 3, 3, /* 22..25: B3 */
- 4, 4, 4, 4, /* 26..29: B4 */
- CB_FCCH, CB_SCH,/* 30..31 */
- 5, 5, 5, 5, /* 32..35: B5 */
- 6, 6, 6, 6, /* 36..39: B6 */
- CB_FCCH, CB_SCH,/* 40..41 */
- 7, 7, 7, 7, /* 42..45: B7 */
- 8, 8, 8, 8, /* 46..49: B8 */
- -4 /* 50: Idle */
-};
+#include <osmo-bts/asci.h>
+#include <osmo-bts/csd_v110.h>
/* determine the CCCH block number based on the frame number */
unsigned int l1sap_fn2ccch_block(uint32_t fn)
{
- int rc = ccch_block_table[fn%51];
+ int rc = gsm0502_fn2ccch_block(fn);
/* if FN is negative, we were called for something that's not CCCH! */
OSMO_ASSERT(rc >= 0);
return rc;
@@ -94,18 +73,25 @@ unsigned int l1sap_fn2ccch_block(uint32_t fn)
struct gsm_lchan *get_lchan_by_chan_nr(struct gsm_bts_trx *trx,
unsigned int chan_nr)
{
+ struct gsm_bts_trx_ts *ts;
unsigned int tn, ss;
tn = L1SAP_CHAN2TS(chan_nr);
- OSMO_ASSERT(tn < ARRAY_SIZE(trx->ts));
+ ts = &trx->ts[tn];
+
+ if (L1SAP_IS_CHAN_VAMOS(chan_nr)) {
+ if (ts->vamos.peer == NULL)
+ return NULL;
+ ts = ts->vamos.peer;
+ }
if (L1SAP_IS_CHAN_CBCH(chan_nr))
ss = 2; /* CBCH is always on sub-slot 2 */
else
ss = l1sap_chan2ss(chan_nr);
- OSMO_ASSERT(ss < ARRAY_SIZE(trx->ts[tn].lchan));
+ OSMO_ASSERT(ss < ARRAY_SIZE(ts->lchan));
- return &trx->ts[tn].lchan[ss];
+ return &ts->lchan[ss];
}
static struct gsm_lchan *
@@ -131,7 +117,8 @@ static uint32_t fn_ms_adj(uint32_t fn, const struct gsm_lchan *lchan)
/* 12/13 frames usable for audio in TCH,
160 samples per RTP packet,
1 RTP packet per 4 frames */
- samples_passed = (fn - lchan->tch.last_fn) * 12 * 160 / (13 * 4);
+ const uint32_t num_fn = GSM_TDMA_FN_SUB(fn, lchan->tch.last_fn);
+ samples_passed = num_fn * 12 * 160 / (13 * 4);
/* round number of samples to the nearest multiple of
GSM_RTP_DURATION */
r = samples_passed + GSM_RTP_DURATION / 2;
@@ -145,27 +132,13 @@ static uint32_t fn_ms_adj(uint32_t fn, const struct gsm_lchan *lchan)
return GSM_RTP_DURATION;
}
-/*! limit number of queue entries to %u; drops any surplus messages */
-static void queue_limit_to(const char *prefix, struct llist_head *queue, unsigned int limit)
-{
- unsigned int count = llist_count(queue);
-
- if (count > limit)
- LOGP(DL1P, LOGL_NOTICE, "%s: freeing %d queued frames\n", prefix, count-limit);
- while (count > limit) {
- struct msgb *tmp = msgb_dequeue(queue);
- msgb_free(tmp);
- count--;
- }
-}
-
/* allocate a msgb containing a osmo_phsap_prim + optional l2 data
- * in order to wrap femtobts header arround l2 data, there must be enough space
+ * in order to wrap femtobts header around l2 data, there must be enough space
* in front and behind data pointer */
struct msgb *l1sap_msgb_alloc(unsigned int l2_len)
{
- int headroom = 128;
- int size = headroom + sizeof(struct osmo_phsap_prim) + l2_len;
+ const int headroom = L1SAP_MSGB_HEADROOM;
+ const int size = headroom + sizeof(struct osmo_phsap_prim) + l2_len;
struct msgb *msg = msgb_alloc_headroom(size, headroom, "l1sap_prim");
if (!msg)
@@ -176,9 +149,15 @@ struct msgb *l1sap_msgb_alloc(unsigned int l2_len)
return msg;
}
+/* Enclose rmsg into an osmo_phsap primitive and hand it over to the higher
+ * layers. The phsap primitive also contains measurement information. The
+ * parameters rssi, ta_offs and is_sub are only needed when the measurement
+ * information is passed along with the TCH data. When separate measurement
+ * indications are used, those last three parameters may be set to zero. */
int add_l1sap_header(struct gsm_bts_trx *trx, struct msgb *rmsg,
struct gsm_lchan *lchan, uint8_t chan_nr, uint32_t fn,
- uint16_t ber10k, int16_t lqual_cb)
+ uint16_t ber10k, int16_t lqual_cb, int8_t rssi,
+ int16_t ta_offs, uint8_t is_sub)
{
struct osmo_phsap_prim *l1sap;
@@ -194,6 +173,10 @@ int add_l1sap_header(struct gsm_bts_trx *trx, struct msgb *rmsg,
l1sap->u.tch.ber10k = ber10k;
l1sap->u.tch.lqual_cb = lqual_cb;
+ l1sap->u.tch.rssi = rssi;
+ l1sap->u.tch.ta_offs_256bits = ta_offs;
+ l1sap->u.tch.is_sub = is_sub;
+
return l1sap_up(trx, l1sap);
}
@@ -257,37 +240,96 @@ int bts_check_for_ciph_cmd(struct msgb *msg, struct gsm_lchan *lchan,
return check_for_ciph_cmd(msg, lchan, chan_nr);
}
-struct gsmtap_inst *gsmtap = NULL;
-uint32_t gsmtap_sapi_mask = 0;
-uint8_t gsmtap_sapi_acch = 0;
-
-const struct value_string gsmtap_sapi_names[] = {
- { GSMTAP_CHANNEL_BCCH, "BCCH" },
- { GSMTAP_CHANNEL_CCCH, "CCCH" },
- { GSMTAP_CHANNEL_RACH, "RACH" },
- { GSMTAP_CHANNEL_AGCH, "AGCH" },
- { GSMTAP_CHANNEL_PCH, "PCH" },
- { GSMTAP_CHANNEL_SDCCH, "SDCCH" },
- { GSMTAP_CHANNEL_TCH_F, "TCH/F" },
- { GSMTAP_CHANNEL_TCH_H, "TCH/H" },
- { GSMTAP_CHANNEL_PACCH, "PACCH" },
- { GSMTAP_CHANNEL_PDCH, "PDTCH" },
- { GSMTAP_CHANNEL_PTCCH, "PTCCH" },
- { GSMTAP_CHANNEL_CBCH51,"CBCH" },
- { GSMTAP_CHANNEL_ACCH, "SACCH" },
+uint16_t l1sap_log_ctx_sapi;
+
+const struct value_string l1sap_common_sapi_names[] = {
+ { L1SAP_COMMON_SAPI_UNKNOWN, "UNKNOWN" },
+ /* alphabetic order */
+ { L1SAP_COMMON_SAPI_AGCH, "AGCH" },
+ { L1SAP_COMMON_SAPI_BCCH, "BCCH" },
+ { L1SAP_COMMON_SAPI_CBCH, "CBCH" },
+ { L1SAP_COMMON_SAPI_FACCH_F, "FACCH/F" },
+ { L1SAP_COMMON_SAPI_FACCH_H, "FACCH/H" },
+ { L1SAP_COMMON_SAPI_FCCH, "FCCH" },
+ { L1SAP_COMMON_SAPI_IDLE, "IDLE" },
+ { L1SAP_COMMON_SAPI_NCH, "NCH" },
+ { L1SAP_COMMON_SAPI_PACCH, "PACCH" },
+ { L1SAP_COMMON_SAPI_PAGCH, "PAGCH" },
+ { L1SAP_COMMON_SAPI_PBCCH, "PBCCH" },
+ { L1SAP_COMMON_SAPI_PCH, "PCH" },
+ { L1SAP_COMMON_SAPI_PDTCH, "PDTCH" },
+ { L1SAP_COMMON_SAPI_PNCH, "PNCH" },
+ { L1SAP_COMMON_SAPI_PPCH, "PPCH" },
+ { L1SAP_COMMON_SAPI_PRACH, "PRACH" },
+ { L1SAP_COMMON_SAPI_PTCCH, "PTCCH" },
+ { L1SAP_COMMON_SAPI_RACH, "RACH" },
+ { L1SAP_COMMON_SAPI_SACCH, "SACCH" },
+ { L1SAP_COMMON_SAPI_SCH, "SCH" },
+ { L1SAP_COMMON_SAPI_SDCCH, "SDCCH" },
+ { L1SAP_COMMON_SAPI_TCH_F, "TCH/F" },
+ { L1SAP_COMMON_SAPI_TCH_H, "TCH/H" },
{ 0, NULL }
};
+static enum l1sap_common_sapi get_common_sapi_ph_data(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
+{
+ uint8_t link_id = l1sap->u.data.link_id;
+ uint8_t chan_nr = l1sap->u.data.chan_nr;
+ uint32_t u32Fn = l1sap->u.data.fn;
+
+ if (L1SAP_IS_CHAN_TCHF(chan_nr))
+ return L1SAP_COMMON_SAPI_TCH_F;
+
+ if (L1SAP_IS_CHAN_TCHH(chan_nr))
+ return L1SAP_COMMON_SAPI_TCH_H;
+
+ if (L1SAP_IS_CHAN_SDCCH4(chan_nr) || L1SAP_IS_CHAN_SDCCH8(chan_nr))
+ return L1SAP_COMMON_SAPI_SDCCH;
+
+ if (L1SAP_IS_CHAN_BCCH(chan_nr))
+ return L1SAP_COMMON_SAPI_BCCH;
+
+ if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr))
+ /* The sapi depends on DSP configuration, not on the actual SYSTEM INFORMATION 3. */
+ return ((l1sap_fn2ccch_block(u32Fn) >= num_agch(trx, "PH-DATA-REQ"))
+ ? L1SAP_COMMON_SAPI_PCH
+ : L1SAP_COMMON_SAPI_AGCH);
+
+ if (L1SAP_IS_CHAN_CBCH(chan_nr))
+ return L1SAP_COMMON_SAPI_CBCH;
+
+ if (L1SAP_IS_LINK_SACCH(link_id))
+ return L1SAP_COMMON_SAPI_SACCH;
+
+ return L1SAP_COMMON_SAPI_UNKNOWN;
+}
+
+static enum l1sap_common_sapi get_common_sapi_by_trx_prim(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
+{
+ /* Only downlink prims are relevant */
+ switch (OSMO_PRIM_HDR(&l1sap->oph)) {
+ case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST):
+ if (ts_is_pdch(&trx->ts[L1SAP_CHAN2TS(l1sap->u.data.chan_nr)]))
+ return ((L1SAP_IS_PTCCH(l1sap->u.data.fn))
+ ? L1SAP_COMMON_SAPI_PTCCH
+ : L1SAP_COMMON_SAPI_PDTCH);
+ return get_common_sapi_ph_data(trx, l1sap);
+ default:
+ return L1SAP_COMMON_SAPI_UNKNOWN;
+ }
+}
+
/* send primitive as gsmtap */
-static int gsmtap_ph_data(struct osmo_phsap_prim *l1sap, uint8_t *chan_type,
- uint8_t *ss, uint32_t fn, uint8_t **data, unsigned int *len,
+static int gsmtap_ph_data(const struct osmo_phsap_prim *l1sap,
+ uint8_t *chan_type, uint8_t *ss, uint32_t fn,
+ uint8_t **data, unsigned int *len,
uint8_t num_agch)
{
struct msgb *msg = l1sap->oph.msg;
uint8_t chan_nr, link_id;
*data = msgb_l2(msg);
- *len = msgb_l2len(msg);
+ *len = msgb_l2(msg) ? msgb_l2len(msg) : 0;
chan_nr = l1sap->u.data.chan_nr;
link_id = l1sap->u.data.link_id;
@@ -322,47 +364,63 @@ static int gsmtap_ph_data(struct osmo_phsap_prim *l1sap, uint8_t *chan_type,
return 0;
}
-static int gsmtap_pdch(struct osmo_phsap_prim *l1sap, uint8_t *chan_type,
- uint8_t *ss, uint32_t fn, uint8_t **data, unsigned int *len)
+static int gsmtap_pdch(const struct osmo_phsap_prim *l1sap,
+ uint8_t *chan_type, uint8_t *ss, uint32_t fn,
+ uint8_t **data, unsigned int *len)
{
struct msgb *msg = l1sap->oph.msg;
*data = msgb_l2(msg);
- *len = msgb_l2len(msg);
+ *len = msgb_l2(msg) ? msgb_l2len(msg) : 0;
if (L1SAP_IS_PTCCH(fn)) {
*chan_type = GSMTAP_CHANNEL_PTCCH;
*ss = L1SAP_FN2PTCCHBLOCK(fn);
- if (l1sap->oph.primitive == PRIM_OP_INDICATION) {
- OSMO_ASSERT(len > 0);
- if ((*data[0]) == 7)
- return -EINVAL;
- (*data)++;
- (*len)--;
- }
- } else
- *chan_type = GSMTAP_CHANNEL_PACCH;
+ } else {
+ /* TODO: distinguish PACCH */
+ *chan_type = GSMTAP_CHANNEL_PDTCH;
+ }
return 0;
}
-static int gsmtap_ph_rach(struct osmo_phsap_prim *l1sap, uint8_t *chan_type,
- uint8_t *tn, uint8_t *ss, uint32_t *fn, uint8_t **data, unsigned int *len)
+static int gsmtap_ph_rach(const struct osmo_phsap_prim *l1sap, uint8_t *chan_type,
+ uint8_t *tn, uint8_t *ss, uint32_t *fn,
+ uint8_t **data, unsigned int *len)
{
- uint8_t chan_nr;
+ uint8_t chan_nr = l1sap->u.rach_ind.chan_nr;
+ static uint8_t ra_buf[2];
*chan_type = GSMTAP_CHANNEL_RACH;
*fn = l1sap->u.rach_ind.fn;
- *tn = L1SAP_CHAN2TS(l1sap->u.rach_ind.chan_nr);
- chan_nr = l1sap->u.rach_ind.chan_nr;
+ *tn = L1SAP_CHAN2TS(chan_nr);
+
if (L1SAP_IS_CHAN_TCHH(chan_nr))
*ss = L1SAP_CHAN2SS_TCHH(chan_nr);
else if (L1SAP_IS_CHAN_SDCCH4(chan_nr))
*ss = L1SAP_CHAN2SS_SDCCH4(chan_nr);
else if (L1SAP_IS_CHAN_SDCCH8(chan_nr))
*ss = L1SAP_CHAN2SS_SDCCH8(chan_nr);
- *data = (uint8_t *)&l1sap->u.rach_ind.ra;
- *len = 1;
+ else if (L1SAP_IS_CHAN_PDCH(chan_nr)) {
+ if (L1SAP_IS_PTCCH(*fn)) {
+ /* TODO: calculate sub-slot from frame-number */
+ *chan_type = GSMTAP_CHANNEL_PTCCH;
+ } else {
+ *chan_type = GSMTAP_CHANNEL_PDTCH;
+ }
+ }
+
+ if (l1sap->u.rach_ind.is_11bit) {
+ /* Pack as described in 3GPP TS 44.004, figure 7.4a.b */
+ ra_buf[0] = (uint8_t) (l1sap->u.rach_ind.ra >> 3);
+ ra_buf[1] = (uint8_t) (l1sap->u.rach_ind.ra & 0x07);
+ *len = sizeof(ra_buf);
+ *data = ra_buf;
+ } else {
+ ra_buf[0] = (uint8_t) (l1sap->u.rach_ind.ra & 0xff);
+ *len = sizeof(ra_buf[0]);
+ *data = ra_buf;
+ }
return 0;
}
@@ -375,8 +433,14 @@ static const uint8_t paging_fill[GSM_MACBLOCK_LEN] = {
static bool is_fill_frame(uint8_t chan_type, const uint8_t *data, unsigned int len)
{
+ if (len != GSM_MACBLOCK_LEN)
+ return false;
+
switch (chan_type) {
case GSMTAP_CHANNEL_AGCH:
+ case GSMTAP_CHANNEL_SDCCH:
+ case GSMTAP_CHANNEL_TCH_F:
+ case GSMTAP_CHANNEL_TCH_H:
if (!memcmp(data, fill_frame, GSM_MACBLOCK_LEN))
return true;
break;
@@ -384,6 +448,7 @@ static bool is_fill_frame(uint8_t chan_type, const uint8_t *data, unsigned int l
if (!memcmp(data, paging_fill, GSM_MACBLOCK_LEN))
return true;
break;
+ /* FIXME: implement the same for GSMTAP_CHANNEL_PDTCH from/to PCU */
/* don't use 'default' case here as the above only conditionally return true */
}
return false;
@@ -396,9 +461,11 @@ static int to_gsmtap(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
uint8_t chan_type = 0, tn = 0, ss = 0;
uint32_t fn;
uint16_t uplink = GSMTAP_ARFCN_F_UPLINK;
+ int8_t signal_dbm;
int rc;
- if (!gsmtap)
+ struct gsmtap_inst *inst = trx->bts->gsmtap.inst;
+ if (!inst)
return 0;
switch (OSMO_PRIM_HDR(&l1sap->oph)) {
@@ -414,10 +481,12 @@ static int to_gsmtap(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
else
rc = gsmtap_ph_data(l1sap, &chan_type, &ss, fn, &data,
&len, num_agch(trx, "GSMTAP"));
+ signal_dbm = l1sap->u.data.rssi;
break;
case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_INDICATION):
rc = gsmtap_ph_rach(l1sap, &chan_type, &tn, &ss, &fn, &data,
&len);
+ signal_dbm = l1sap->u.rach_ind.rssi;
break;
default:
rc = -ENOTSUP;
@@ -429,10 +498,10 @@ static int to_gsmtap(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
if (len == 0)
return 0;
if ((chan_type & GSMTAP_CHANNEL_ACCH)) {
- if (!gsmtap_sapi_acch)
+ if (!trx->bts->gsmtap.sapi_acch)
return 0;
} else {
- if (!((1 << (chan_type & 31)) & gsmtap_sapi_mask))
+ if (!((1 << (chan_type & 31)) & trx->bts->gsmtap.sapi_mask))
return 0;
}
@@ -441,8 +510,8 @@ static int to_gsmtap(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
if (is_fill_frame(chan_type, data, len))
return 0;
- gsmtap_send(gsmtap, trx->arfcn | uplink, tn, chan_type, ss, fn, 0, 0,
- data, len);
+ gsmtap_send(inst, trx->arfcn | uplink, tn, chan_type, ss, fn,
+ signal_dbm, 0 /* TODO: SNR */, data, len);
return 0;
}
@@ -487,22 +556,73 @@ static unsigned int calc_exprd_rach_frames(struct gsm_bts *bts, uint32_t fn)
return rach_frames_expired;
}
+static void l1sap_interf_meas_calc_avg(struct gsm_bts_trx *trx)
+{
+ unsigned int tn, ln;
+
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+
+ if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ continue;
+ if (ts->mo.nm_state.availability != NM_AVSTATE_OK)
+ continue;
+
+ for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
+ struct gsm_lchan *lchan = &ts->lchan[ln];
+
+ lchan->meas.interf_meas_avg_dbm = 0;
+ lchan->meas.interf_band = 0;
+
+ /* There must be at least one sample */
+ if (lchan->meas.interf_meas_num == 0)
+ continue;
+
+ /* Average all collected samples */
+ gsm_lchan_interf_meas_calc_avg(lchan);
+ }
+ }
+}
+
+static void l1sap_interf_meas_report(struct gsm_bts *bts)
+{
+ const uint32_t period = bts->interference.intave * 104;
+ struct gsm_bts_trx *trx;
+
+ if (bts->interference.intave == 0)
+ return;
+ if (bts->gsm_time.fn % period != 0)
+ return;
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ if (trx->mo.nm_state.operational != NM_OPSTATE_ENABLED ||
+ trx->bb_transc.mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ continue;
+ /* Calculate the average of all received samples */
+ l1sap_interf_meas_calc_avg(trx);
+ /* Report to the BSC over the A-bis/RSL */
+ rsl_tx_rf_res(trx);
+ /* Report to the PCU over the PCUIF */
+ pcu_tx_interf_ind(trx, bts->gsm_time.fn);
+ }
+}
+
/* time information received from bts model */
static int l1sap_info_time_ind(struct gsm_bts *bts,
struct osmo_phsap_prim *l1sap,
struct info_time_ind_param *info_time_ind)
{
- int frames_expired;
- int i;
+ unsigned int frames_expired;
+ unsigned int i;
DEBUGPFN(DL1P, info_time_ind->fn, "Rx MPH_INFO time ind\n");
/* Calculate and check frame difference */
- frames_expired = info_time_ind->fn - bts->gsm_time.fn;
+ frames_expired = GSM_TDMA_FN_SUB(info_time_ind->fn, bts->gsm_time.fn);
if (frames_expired > 1) {
if (bts->gsm_time.fn)
LOGPFN(DL1P, LOGL_ERROR, info_time_ind->fn,
- "Invalid condition detected: Frame difference is %"PRIu32"-%"PRIu32"=%d > 1!\n",
+ "Invalid condition detected: Frame difference is %"PRIu32"-%"PRIu32"=%u > 1!\n",
info_time_ind->fn, bts->gsm_time.fn, frames_expired);
}
@@ -515,10 +635,14 @@ static int l1sap_info_time_ind(struct gsm_bts *bts,
/* increment number of RACH slots that have passed by since the
* last time indication */
for (i = 0; i < frames_expired; i++) {
- uint32_t fn = (info_time_ind->fn + GSM_HYPERFRAME - i) % GSM_HYPERFRAME;
+ uint32_t fn = GSM_TDMA_FN_SUB(info_time_ind->fn, i);
bts->load.rach.total += calc_exprd_rach_frames(bts, fn);
}
+ /* Report interference levels to the BSC */
+ if (bts_internal_flag_get(bts, BTS_INTERNAL_FLAG_INTERF_MEAS))
+ l1sap_interf_meas_report(bts);
+
return 0;
}
@@ -542,49 +666,93 @@ static inline void set_ms_to_data(struct gsm_lchan *lchan, int16_t data, bool se
}
}
+bool trx_sched_is_sacch_fn(const struct gsm_bts_trx_ts *ts, uint32_t fn, bool uplink);
+
/* measurement information received from bts model */
-static int l1sap_info_meas_ind(struct gsm_bts_trx *trx,
- struct osmo_phsap_prim *l1sap,
- struct info_meas_ind_param *info_meas_ind)
+static void process_l1sap_meas_data(struct gsm_lchan *lchan,
+ const struct osmo_phsap_prim *l1sap,
+ enum osmo_ph_prim ind_type)
{
struct bts_ul_meas ulm;
- struct gsm_lchan *lchan;
-
- lchan = get_active_lchan_by_chan_nr(trx, info_meas_ind->chan_nr);
- if (!lchan) {
- LOGPFN(DL1P, LOGL_ERROR, info_meas_ind->fn,
- "No lchan for MPH INFO MEAS IND (chan_nr=%s)\n", rsl_chan_nr_str(info_meas_ind->chan_nr));
- return 0;
- }
+ const struct info_meas_ind_param *info_meas_ind;
+ const struct ph_data_param *ph_data_ind;
+ const struct ph_tch_param *ph_tch_ind;
+ uint32_t fn;
+ const char *ind_name;
- DEBUGPFN(DL1P, info_meas_ind->fn,
- "%s MPH_INFO meas ind, ta_offs_256bits=%d, ber10k=%d, inv_rssi=%u\n",
- gsm_lchan_name(lchan), info_meas_ind->ta_offs_256bits,
- info_meas_ind->ber10k, info_meas_ind->inv_rssi);
+ /* Do not process measurement reports from non-active VGCS calls. */
+ if (rsl_chan_rt_is_asci(lchan->rsl_chan_rt) && lchan->asci.talker_active != VGCS_TALKER_ACTIVE)
+ return;
- /* in the GPRS case we are not interested in measurement
- * processing. The PCU will take care of it */
- if (lchan->type == GSM_LCHAN_PDTCH)
- return 0;
+ switch (ind_type) {
+ case PRIM_MPH_INFO:
+ /* (legacy way, see also OS#2977) */
+ info_meas_ind = &l1sap->u.info.u.meas_ind;
+ fn = info_meas_ind->fn;
+ ind_name = "MPH INFO";
+ ulm = (struct bts_ul_meas) {
+ .ta_offs_256bits = info_meas_ind->ta_offs_256bits,
+ .inv_rssi = info_meas_ind->inv_rssi,
+ .ber10k = info_meas_ind->ber10k,
+ .ci_cb = info_meas_ind->c_i_cb,
+ };
+ /* additionally treat SACCH frames (match by TDMA FN) as SUB frames */
+ if (info_meas_ind->is_sub || trx_sched_is_sacch_fn(lchan->ts, fn, true))
+ ulm.is_sub = 1;
+ break;
+ case PRIM_TCH:
+ ph_tch_ind = &l1sap->u.tch;
+ if (ph_tch_ind->rssi == 0)
+ return;
+ fn = ph_tch_ind->fn;
+ ind_name = "TCH";
+ ulm = (struct bts_ul_meas) {
+ .ta_offs_256bits = ph_tch_ind->ta_offs_256bits,
+ .inv_rssi = abs(ph_tch_ind->rssi),
+ .ber10k = ph_tch_ind->ber10k,
+ .ci_cb = ph_tch_ind->lqual_cb,
+ .is_sub = ph_tch_ind->is_sub,
+ };
+ /* PRIM_TCH always carries DCCH, not SACCH */
+ break;
+ case PRIM_PH_DATA:
+ ph_data_ind = &l1sap->u.data;
+ if (ph_data_ind->rssi == 0)
+ return;
+ fn = ph_data_ind->fn;
+ ind_name = "DATA";
+ ulm = (struct bts_ul_meas) {
+ .ta_offs_256bits = ph_data_ind->ta_offs_256bits,
+ .inv_rssi = abs(ph_data_ind->rssi),
+ .ber10k = ph_data_ind->ber10k,
+ .ci_cb = ph_data_ind->lqual_cb,
+ };
+ /* additionally treat SACCH frames (match by RSL link ID) as SUB frames */
+ if (ph_data_ind->is_sub || L1SAP_IS_LINK_SACCH(ph_data_ind->link_id))
+ ulm.is_sub = 1;
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
- memset(&ulm, 0, sizeof(ulm));
- ulm.ta_offs_256bits = info_meas_ind->ta_offs_256bits;
- ulm.ber10k = info_meas_ind->ber10k;
- ulm.inv_rssi = info_meas_ind->inv_rssi;
- ulm.is_sub = info_meas_ind->is_sub;
+ LOGPLCFN(lchan, fn, DL1P, LOGL_DEBUG,
+ "%s meas ind, ta_offs_256bits=%d, ber10k=%d, inv_rssi=%u, C/I=%d cB\n", ind_name,
+ ulm.ta_offs_256bits, ulm.ber10k, ulm.inv_rssi, ulm.ci_cb);
/* we assume that symbol period is 1 bit: */
- set_ms_to_data(lchan, info_meas_ind->ta_offs_256bits / 256, true);
+ set_ms_to_data(lchan, ulm.ta_offs_256bits / 256, true);
- lchan_meas_process_measurement(lchan, &ulm, info_meas_ind->fn);
+ lchan_meas_process_measurement(lchan, &ulm, fn);
- return 0;
+ return;
}
-/* any L1 MPH_INFO indication prim recevied from bts model */
+/* any L1 MPH_INFO indication prim received from bts model */
static int l1sap_mph_info_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct mph_info_param *info)
{
+ const struct info_meas_ind_param *meas_ind;
+ struct gsm_lchan *lchan;
int rc = 0;
switch (info->type) {
@@ -599,7 +767,22 @@ static int l1sap_mph_info_ind(struct gsm_bts_trx *trx,
&info->u.time_ind);
break;
case PRIM_INFO_MEAS:
- rc = l1sap_info_meas_ind(trx, l1sap, &info->u.meas_ind);
+ /* We should never get an INFO_IND with PRIM_INFO_MEAS
+ * when BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB is set */
+ if (bts_internal_flag_get(trx->bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB))
+ OSMO_ASSERT(false);
+
+ meas_ind = &l1sap->u.info.u.meas_ind;
+
+ lchan = get_active_lchan_by_chan_nr(trx, meas_ind->chan_nr);
+ if (!lchan) {
+ LOGPFN(DL1P, LOGL_ERROR, meas_ind->fn,
+ "No lchan for chan_nr=%s\n",
+ rsl_chan_nr_str(meas_ind->chan_nr));
+ return 0;
+ }
+
+ process_l1sap_meas_data(lchan, l1sap, PRIM_MPH_INFO);
break;
default:
LOGP(DL1P, LOGL_NOTICE, "unknown MPH_INFO ind type %d\n",
@@ -616,6 +799,12 @@ static int l1sap_info_act_cnf(struct gsm_bts_trx *trx,
struct info_act_cnf_param *info_act_cnf)
{
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, info_act_cnf->chan_nr);
+ if (lchan == NULL) {
+ LOGPTRX(trx, DL1C, LOGL_ERROR, "get_lchan_by_chan_nr(chan_nr=%s) "
+ "yields NULL for PRIM_INFO_ACTIVATE.conf\n",
+ rsl_chan_nr_str(info_act_cnf->chan_nr));
+ return -ENODEV;
+ }
LOGPLCHAN(lchan, DL1C, LOGL_INFO, "activate confirm chan_nr=%s trx=%d\n",
rsl_chan_nr_str(info_act_cnf->chan_nr), trx->nr);
@@ -639,6 +828,12 @@ static int l1sap_info_rel_cnf(struct gsm_bts_trx *trx,
struct info_act_cnf_param *info_act_cnf)
{
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, info_act_cnf->chan_nr);
+ if (lchan == NULL) {
+ LOGPTRX(trx, DL1C, LOGL_ERROR, "get_lchan_by_chan_nr(chan_nr=%s) "
+ "yields NULL for PRIM_INFO_ACTIVATE.conf\n",
+ rsl_chan_nr_str(info_act_cnf->chan_nr));
+ return -ENODEV;
+ }
LOGPLCHAN(lchan, DL1C, LOGL_INFO, "deactivate confirm chan_nr=%s trx=%d\n",
rsl_chan_nr_str(info_act_cnf->chan_nr), trx->nr);
@@ -655,7 +850,7 @@ static int l1sap_info_rel_cnf(struct gsm_bts_trx *trx,
return 0;
}
-/* any L1 MPH_INFO confirm prim recevied from bts model */
+/* any L1 MPH_INFO confirm prim received from bts model */
static int l1sap_mph_info_cnf(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct mph_info_param *info)
{
@@ -692,16 +887,15 @@ static int lchan_pdtch_ph_rts_ind_loop(struct gsm_lchan *lchan,
uint8_t *p;
/* de-queue response message (loopback) */
- loop_msg = msgb_dequeue(&lchan->dl_tch_queue);
+ loop_msg = msgb_dequeue_count(&lchan->dl_tch_queue, &lchan->dl_tch_queue_len);
if (!loop_msg) {
- LOGPGT(DL1P, LOGL_NOTICE, tm, "%s: no looped PDTCH message, sending empty\n",
- gsm_lchan_name(lchan));
+ LOGPLCGT(lchan, tm, DL1P, LOGL_NOTICE, "no looped PDTCH message, sending empty\n");
/* empty downlink message */
p = msgb_put(msg, GSM_MACBLOCK_LEN);
memset(p, 0, GSM_MACBLOCK_LEN);
} else {
- LOGPGT(DL1P, LOGL_NOTICE, tm, "%s: looped PDTCH message of %u bytes\n",
- gsm_lchan_name(lchan), msgb_l2len(loop_msg));
+ LOGPLCGT(lchan, tm, DL1P, LOGL_NOTICE, "looped PDTCH message of %u bytes\n",
+ msgb_l2len(loop_msg));
/* copy over data from queued response message */
p = msgb_put(msg, msgb_l2len(loop_msg));
memcpy(p, msgb_l2(loop_msg), msgb_l2len(loop_msg));
@@ -710,17 +904,34 @@ static int lchan_pdtch_ph_rts_ind_loop(struct gsm_lchan *lchan,
return 0;
}
-/* Check if given CCCH frame number is for a PCH or for an AGCH (this function is
+/* Check if given CCCH frame number is for a NCH, PCH or for an AGCH (this function is
* only used internally, it is public to call it from unit-tests) */
-int is_ccch_for_agch(struct gsm_bts_trx *trx, uint32_t fn) {
+enum ccch_msgt get_ccch_msgt(struct gsm_bts_trx *trx, uint32_t fn)
+{
+ uint8_t block, first_block, num_blocks;
+ int rc;
+
+ block = l1sap_fn2ccch_block(fn);
+
+ /* If there is an NCH, check if the block number matches. It has priority over PCH/AGCH. */
+ if (trx->bts->asci.pos_nch >= 0) {
+ rc = osmo_gsm48_si1ro_nch_pos_decode(trx->bts->asci.pos_nch, &num_blocks, &first_block);
+ if (rc >= 0 && block >= first_block && block < first_block + num_blocks)
+ return CCCH_MSGT_NCH;
+ }
+
/* Note: The number of available access grant channels is set by the
* parameter BS_AG_BLKS_RES via system information type 3. This SI is
- * transfered to osmo-bts via RSL */
- return l1sap_fn2ccch_block(fn) < num_agch(trx, "PH-RTS-IND");
+ * transferred to osmo-bts via RSL */
+ if (l1sap_fn2ccch_block(fn) < num_agch(trx, "PH-RTS-IND"))
+ return CCCH_MSGT_AGCH;
+
+ return CCCH_MSGT_PCH;
}
+
/* return the measured average of frame numbers that the RTS clock is running in advance */
-int32_t bts_get_avg_fn_advance(struct gsm_bts *bts)
+int32_t bts_get_avg_fn_advance(const struct gsm_bts *bts)
{
if (bts->fn_stats.avg_count == 0)
return 0;
@@ -729,7 +940,7 @@ int32_t bts_get_avg_fn_advance(struct gsm_bts *bts)
static void l1sap_update_fnstats(struct gsm_bts *bts, uint32_t rts_fn)
{
- int32_t delta = (rts_fn + GSM_HYPERFRAME - bts->gsm_time.fn) % GSM_HYPERFRAME;
+ int32_t delta = GSM_TDMA_FN_SUB(rts_fn, bts->gsm_time.fn);
if (delta < bts->fn_stats.min)
bts->fn_stats.min = delta;
@@ -746,6 +957,100 @@ static void l1sap_update_fnstats(struct gsm_bts *bts, uint32_t rts_fn)
}
}
+/* Common dequeueing function */
+static inline struct msgb *lapdm_phsap_dequeue_msg(struct lapdm_entity *le, uint32_t fn)
+{
+ struct osmo_phsap_prim pp;
+ if (lapdm_phsap_dequeue_prim_fn(le, &pp, fn) < 0)
+ return NULL;
+ return pp.oph.msg;
+}
+
+/* Special dequeueing function with FACCH repetition (3GPP TS 44.006, section 10) */
+static inline struct msgb *lapdm_phsap_dequeue_msg_facch(struct gsm_lchan *lchan, struct lapdm_entity *le, uint32_t fn)
+{
+ struct osmo_phsap_prim pp;
+ struct msgb *msg;
+
+ /* Note: The repeated version of the FACCH block must be scheduled 8 or 9 bursts after the original
+ * transmission. see 3GPP TS 44.006, section 10.2 for a more detailed explaination. */
+ if (lchan->rep_acch.dl_facch[0].msg && GSM_TDMA_FN_SUB(fn, lchan->rep_acch.dl_facch[0].fn) >= 8) {
+ /* Re-use stored FACCH message buffer from SLOT #0 for repetition. */
+ msg = lchan->rep_acch.dl_facch[0].msg;
+ lchan->rep_acch.dl_facch[0].msg = NULL;
+ } else if (lchan->rep_acch.dl_facch[1].msg && GSM_TDMA_FN_SUB(fn, lchan->rep_acch.dl_facch[1].fn) >= 8) {
+ /* Re-use stored FACCH message buffer from SLOT #1 for repetition. */
+ msg = lchan->rep_acch.dl_facch[1].msg;
+ lchan->rep_acch.dl_facch[1].msg = NULL;
+ } else {
+ /* Fetch new FACCH from queue ... */
+ if (lapdm_phsap_dequeue_prim_fn(le, &pp, fn) < 0)
+ return NULL;
+ msg = pp.oph.msg;
+
+ /* Check if the LAPDm frame is a command frame,
+ * see also: 3GPP TS 04.06 section 3.2 and 3.3.2.
+ * If the MS explicitly indicated that repeated ACCH is
+ * supported, than all FACCH frames may be repeated
+ * see also: 3GPP TS 44.006, section 10.3). */
+ if (!(lchan->rep_acch_cap.dl_facch_all || msg->data[0] & 0x02))
+ return msg;
+
+ /* ... and store the message buffer for repetition. */
+ if (lchan->rep_acch.dl_facch[0].msg == NULL) {
+ lchan->rep_acch.dl_facch[0].msg = msgb_copy(msg, "rep_facch_0");
+ lchan->rep_acch.dl_facch[0].fn = fn;
+ } else if (lchan->rep_acch.dl_facch[1].msg == NULL) {
+ lchan->rep_acch.dl_facch[1].msg = msgb_copy(msg, "rep_facch_1");
+ lchan->rep_acch.dl_facch[1].fn = fn;
+ } else {
+ /* By definition 3GPP TS 05.02 does not allow more than two (for TCH/H only one) FACCH blocks
+ * to be transmitted simultaniously. */
+ OSMO_ASSERT(false);
+ }
+ }
+
+ return msg;
+}
+
+/* Special dequeueing function with SACCH repetition (3GPP TS 44.006, section 11) */
+static inline struct msgb *lapdm_phsap_dequeue_msg_sacch(struct gsm_lchan *lchan, struct lapdm_entity *le, uint32_t fn)
+{
+ struct osmo_phsap_prim pp;
+ struct msgb *msg;
+ uint8_t sapi;
+
+ /* Note: When the MS disables SACCH repetition, we still must collect
+ * possible candidates in order to have one ready in case the MS enables
+ * SACCH repetition. */
+
+ if (lchan->rep_acch.dl_sacch_msg) {
+ if (lchan->meas.l1_info.srr_sro == 0) {
+ /* Toss previous repetition candidate */
+ msgb_free(lchan->rep_acch.dl_sacch_msg);
+ lchan->rep_acch.dl_sacch_msg = NULL;
+ } else {
+ /* Use previous repetition candidate */
+ msg = lchan->rep_acch.dl_sacch_msg;
+ lchan->rep_acch.dl_sacch_msg = NULL;
+ return msg;
+ }
+ }
+
+ /* Fetch new repetition candidate from queue */
+ if (lapdm_phsap_dequeue_prim_fn(le, &pp, fn) < 0)
+ return NULL;
+ msg = pp.oph.msg;
+ sapi = (msg->data[0] >> 2) & 0x07;
+
+ /* Only LAPDm frames for SAPI 0 may become a repetition
+ * candidate. */
+ if (sapi == 0)
+ lchan->rep_acch.dl_sacch_msg = msgb_copy(msg, "rep_sacch");
+
+ return msg;
+}
+
/* PH-RTS-IND prim received from bts model */
static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct ph_data_param *rts_ind)
@@ -756,12 +1061,12 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
uint8_t chan_nr, link_id;
uint8_t tn;
uint32_t fn;
- uint8_t *p, *si;
+ uint8_t *p = NULL;
+ uint8_t *si;
struct lapdm_entity *le;
- struct osmo_phsap_prim pp;
+ struct msgb *pp_msg;
bool dtxd_facch = false;
int rc;
- int is_ag_res;
chan_nr = rts_ind->chan_nr;
link_id = rts_ind->link_id;
@@ -793,10 +1098,10 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
} else {
/* forward RTS.ind to PCU */
if (L1SAP_IS_PTCCH(rts_ind->fn)) {
- pcu_tx_rts_req(&trx->ts[tn], 1, fn, 1 /* ARFCN */,
+ pcu_tx_rts_req(&trx->ts[tn], 1, fn, trx->arfcn,
L1SAP_FN2PTCCHBLOCK(fn));
} else {
- pcu_tx_rts_req(&trx->ts[tn], 0, fn, 0 /* ARFCN */,
+ pcu_tx_rts_req(&trx->ts[tn], 0, fn, trx->arfcn,
L1SAP_FN2MACBLOCK(fn));
}
/* return early, PCU takes care of rest */
@@ -820,19 +1125,45 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
rsl_chan_nr_str(chan_nr));
return 0;
}
+ if (lchan->pending_rel_ind_msg) {
+ LOGPLCGT(lchan, &g_time, DRSL, LOGL_INFO, "Forward RLL RELease INDication to the BSC\n");
+ abis_bts_rsl_sendmsg(lchan->pending_rel_ind_msg);
+ lchan->pending_rel_ind_msg = NULL;
+ }
if (L1SAP_IS_LINK_SACCH(link_id)) {
p = msgb_put(msg, GSM_MACBLOCK_LEN);
/* L1-header, if not set/modified by layer 1 */
p[0] = lchan->ms_power_ctrl.current;
- p[1] = lchan->rqd_ta;
+ if (lchan->rep_acch.ul_sacch_active)
+ p[0] |= 0x40; /* See also: 3GPP TS 44.004, section 7.1 */
+ p[1] = lchan->ta_ctrl.current;
le = &lchan->lapdm_ch.lapdm_acch;
+ if (lchan->rep_acch_cap.dl_sacch) {
+ /* Check if MS requests SACCH repetition and update state accordingly */
+ if (lchan->meas.l1_info.srr_sro) {
+ if (lchan->rep_acch.dl_sacch_active == false)
+ LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-SACCH repetition: inactive => active\n");
+ lchan->rep_acch.dl_sacch_active = true;
+ } else {
+ if (lchan->rep_acch.dl_sacch_active == true)
+ LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-SACCH repetition: active => inactive\n");
+ lchan->rep_acch.dl_sacch_active = false;
+ }
+ pp_msg = lapdm_phsap_dequeue_msg_sacch(lchan, le, fn);
+ } else {
+ pp_msg = lapdm_phsap_dequeue_msg(le, fn);
+ }
} else {
if (lchan->ts->trx->bts->dtxd)
dtxd_facch = true;
le = &lchan->lapdm_ch.lapdm_dcch;
+ if (lchan->rep_acch.dl_facch_active && lchan->rsl_cmode != RSL_CMOD_SPD_SIGN)
+ pp_msg = lapdm_phsap_dequeue_msg_facch(lchan, le, fn);
+ else
+ pp_msg = lapdm_phsap_dequeue_msg(le, fn);
+ lchan->tch.dtx_fr_hr_efr.dl_facch_stealing = (pp_msg != NULL);
}
- rc = lapdm_phsap_dequeue_prim(le, &pp);
- if (rc < 0) {
+ if (!pp_msg) {
if (L1SAP_IS_LINK_SACCH(link_id)) {
/* No SACCH data from LAPDM pending, send SACCH filling */
uint8_t *si = lchan_sacch_get(lchan);
@@ -841,6 +1172,10 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
memcpy(p + 2, si, GSM_MACBLOCK_LEN - 2);
} else
memcpy(p + 2, fill_frame, GSM_MACBLOCK_LEN - 2);
+ } else if (vgcs_is_uplink_free(lchan)) {
+ /* If UPLINK FREE message is stored, send it with every DCCH frame. */
+ p = msgb_put(msg, GSM_MACBLOCK_LEN);
+ vgcs_uplink_free_get(lchan, p);
} else if (L1SAP_IS_CHAN_SDCCH4(chan_nr) || L1SAP_IS_CHAN_SDCCH8(chan_nr) ||
(lchan->rsl_cmode == RSL_CMOD_SPD_SIGN && !lchan->ts->trx->bts->dtxd)) {
/*
@@ -857,21 +1192,28 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
} else {
/* The +2 is empty space where the DSP inserts the L1 hdr */
if (L1SAP_IS_LINK_SACCH(link_id))
- memcpy(p + 2, pp.oph.msg->data + 2, GSM_MACBLOCK_LEN - 2);
+ memcpy(p + 2, pp_msg->data + 2, GSM_MACBLOCK_LEN - 2);
else {
p = msgb_put(msg, GSM_MACBLOCK_LEN);
- memcpy(p, pp.oph.msg->data, GSM_MACBLOCK_LEN);
+ memcpy(p, pp_msg->data, GSM_MACBLOCK_LEN);
/* check if it is a RR CIPH MODE CMD. if yes, enable RX ciphering */
- check_for_ciph_cmd(pp.oph.msg, lchan, chan_nr);
+ check_for_ciph_cmd(pp_msg, lchan, chan_nr);
if (dtxd_facch)
dtx_dispatch(lchan, E_FACCH);
+ if (rsl_chan_rt_is_vgcs(lchan->rsl_chan_rt)) {
+ /* Check for UPLINK FREE message and store. */
+ if (pp_msg->data[0] == GSM48_MT_RR_SH_UL_FREE << 2)
+ vgcs_uplink_free_set(lchan, pp_msg->data);
+ /* Keep UPLINK FREE message when sending short header messages. */
+ else if ((pp_msg->data[0] & 0x03) != 0x00)
+ vgcs_uplink_free_reset(lchan);
+ }
}
- msgb_free(pp.oph.msg);
+ msgb_free(pp_msg);
}
} else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) {
p = msgb_put(msg, GSM_MACBLOCK_LEN);
- is_ag_res = is_ccch_for_agch(trx, fn);
- rc = bts_ccch_copy_msg(trx->bts, p, &g_time, is_ag_res);
+ rc = bts_ccch_copy_msg(trx->bts, p, &g_time, get_ccch_msgt(trx, fn));
if (rc <= 0)
memcpy(p, fill_frame, GSM_MACBLOCK_LEN);
}
@@ -884,48 +1226,270 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
return 1;
}
-static bool rtppayload_is_octet_aligned(const uint8_t *rtp_pl, uint8_t payload_len)
-{
- /*
- * Logic: If 1st bit padding is not zero, packet is either:
- * - bandwidth-efficient AMR payload.
- * - malformed packet.
- * However, Bandwidth-efficient AMR 4,75 frame last in payload(F=0, FT=0)
- * with 4th,5ht,6th AMR payload to 0 matches padding==0.
- * Furthermore, both AMR 4,75 bw-efficient and octet alignment are 14 bytes long (AMR 4,75 encodes 95b):
- * bw-efficient: 95b, + 4b hdr + 6b ToC = 105b, + padding = 112b = 14B.
- * octet-aligned: 1B hdr + 1B ToC + 95b = 111b, + padding = 112b = 14B.
- * We cannot use other fields to match since they are inside the AMR
- * payload bits which are unknown.
- * As a result, this function may return false positive (true) for some AMR
- * 4,75 AMR frames, but given the length, CMR and FT read is the same as a
- * consequence, the damage in here is harmless other than being unable to
- * decode the audio at the other side.
- */
- #define AMR_PADDING1(rtp_pl) (rtp_pl[0] & 0x0f)
- #define AMR_PADDING2(rtp_pl) (rtp_pl[1] & 0x03)
+/* The following static functions are helpers for l1sap_tch_rts_ind(),
+ * used only for FR/HR/EFR speech modes. For these speech TCH modes,
+ * if our incoming RTP stream includes SID frames, we need to apply
+ * the following transformations to the downlink frame stream we actually
+ * transmit:
+ *
+ * - We need to cache the last received SID and retransmit it in
+ * SACCH-aligned frame positions, or if the SACCH-aligned frame
+ * position was stolen by FACCH, then right after that FACCH.
+ *
+ * - That cached SID needs to be aged and expired, in accord with
+ * TS 28.062 section C.3.2.1.1 paragraph 5 - the paragraph concerning
+ * expiration of cached downlink SID.
+ *
+ * - In all other frame positions, extraneous SID frames need to be
+ * dropped - we send an empty payload to the BTS model, causing it
+ * to transmit an induced BFI condition on the air (fake DTXd),
+ * or perhaps real DTXd (actually turning off Tx) in the future.
+ */
- if(payload_len < 2 || AMR_PADDING1(rtp_pl) || AMR_PADDING2(rtp_pl))
+/*! \brief Check if the given FN of TCH-RTS-IND corresponds to a mandatory
+ * SID position for non-AMR codecs that follow SACCH alignment.
+ * \param[in] lchan Logical channel on which we check scheduling
+ * \param[in] fn Frame Number for which we check scheduling
+ * \returns true if this FN is a mandatory SID position, false otherwise
+ */
+static inline bool fr_hr_efr_sid_position(struct gsm_lchan *lchan, uint32_t fn)
+{
+ /* See GSM 05.08 section 8.3 for frame numbers - but we are
+ * specifically looking for FNs corresponding to the beginning
+ * of the complete SID frame to be transmitted, rather than all FNs
+ * where we have to put out a non-suppressed burst. */
+ switch (lchan->type) {
+ case GSM_LCHAN_TCH_F:
+ return fn % 104 == 52;
+ case GSM_LCHAN_TCH_H:
+ switch (lchan->nr) {
+ case 0:
+ return fn % 104 == 0 || fn % 104 == 52;
+ case 1:
+ return fn % 104 == 14 || fn % 104 == 66;
+ default:
+ return false;
+ }
+ default:
return false;
+ }
+}
+/*! \brief This helper function implements DTXd input processing for FR/HR/EFR:
+ * we got an RTP input frame, now we need to check if it is SID or not,
+ * and update our DL SID reshaper state accordingly.
+ * \param[in] lchan Logical channel structure of the TCH we work with
+ * \param[in] resp_msg The input frame from RTP
+ * \param[out] sid_result Output flag indicating if the received frame is SID
+ * \returns true if the frame in resp_msg is good, false otherwise
+ */
+static bool fr_hr_efr_dtxd_input(struct gsm_lchan *lchan, struct msgb *resp_msg,
+ bool *sid_result)
+{
+ enum osmo_gsm631_sid_class sidc;
+ bool is_sid;
+
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ if (lchan->type == GSM_LCHAN_TCH_F) {
+ sidc = osmo_fr_sid_classify(msgb_l2(resp_msg));
+ switch (sidc) {
+ case OSMO_GSM631_SID_CLASS_SPEECH:
+ is_sid = false;
+ break;
+ case OSMO_GSM631_SID_CLASS_INVALID:
+ /* TS 28.062 section C.3.2.1.1: invalid SIDs
+ * from call leg A UL are treated like BFIs.
+ * Drop this frame and act as if we got nothing
+ * at all from RTP for this FN. */
+ return false;
+ case OSMO_GSM631_SID_CLASS_VALID:
+ is_sid = true;
+ /* The SID code word may have a one bit error -
+ * rejuvenate it. */
+ osmo_fr_sid_reset(msgb_l2(resp_msg));
+ break;
+ default:
+ /* SID classification per GSM 06.31 section
+ * 6.1.1 has only 3 possible outcomes. */
+ OSMO_ASSERT(0);
+ }
+ } else {
+ /* The same kind of classification as we do in
+ * osmo_{fr,efr}_sid_classify() is impossible for HR:
+ * the equivalent ternary SID classification per
+ * GSM 06.41 can only be done in the UL-handling BTS,
+ * directly coupled to the GSM 05.03 channel decoder,
+ * and cannot be reconstructed downstream from frame
+ * payload bits. The only kind of SID we can detect
+ * here is the perfect, error-free kind. However,
+ * if we received RFC 5993 payload and the sender
+ * told us it is valid SID, honor that indication
+ * and rejuvenate the SID codeword. */
+ if (rtpmsg_is_rfc5993_sid(resp_msg)) {
+ is_sid = true;
+ osmo_hr_sid_reset(msgb_l2(resp_msg));
+ } else {
+ is_sid = osmo_hr_check_sid(msgb_l2(resp_msg),
+ msgb_l2len(resp_msg));
+ }
+ }
+ break;
+ case GSM48_CMODE_SPEECH_EFR:
+ /* same logic as for FRv1 */
+ sidc = osmo_efr_sid_classify(msgb_l2(resp_msg));
+ switch (sidc) {
+ case OSMO_GSM631_SID_CLASS_SPEECH:
+ is_sid = false;
+ break;
+ case OSMO_GSM631_SID_CLASS_INVALID:
+ return false;
+ case OSMO_GSM631_SID_CLASS_VALID:
+ is_sid = true;
+ osmo_efr_sid_reset(msgb_l2(resp_msg));
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ break;
+ default:
+ /* This static function should never be called except for
+ * V1 and EFR speech modes. */
+ OSMO_ASSERT(0);
+ }
+ *sid_result = is_sid;
+ lchan->tch.dtx_fr_hr_efr.last_rtp_input_was_sid = is_sid;
+ if (is_sid) {
+ uint8_t copy_len;
+
+ copy_len = OSMO_MIN(msgb_l2len(resp_msg),
+ ARRAY_SIZE(lchan->tch.dtx_fr_hr_efr.last_sid));
+ memcpy(lchan->tch.dtx_fr_hr_efr.last_sid,
+ msgb_l2(resp_msg), copy_len);
+ lchan->tch.dtx_fr_hr_efr.last_sid_len = copy_len;
+ lchan->tch.dtx_fr_hr_efr.last_sid_age = 0;
+ } else {
+ /* We got a speech frame, not SID - therefore, the state flag
+ * of "we already transmitted a SID" needs to be cleared,
+ * so that the very first SID that follows this talkspurt
+ * will get transmitted right away, without waiting for
+ * the next mandatory SID position. */
+ lchan->tch.dtx_fr_hr_efr.dl_sid_transmitted = false;
+ }
return true;
}
-static bool rtppayload_is_valid(struct gsm_lchan *lchan, struct msgb *resp_msg)
+/*! \brief This helper function implements DTXd output processing for FR/HR/EFR:
+ * here we update our state to deal with cached SID aging, mandatory-Tx
+ * frame positions and FACCH stealing, and we make the desired output
+ * transformations of either regurgitating a cached SID or vice-versa,
+ * dropping a SID we received from RTP.
+ * \param[in] lchan Logical channel structure of the TCH we work with
+ * \param[in] fn Frame Number for which we are preparing DL
+ * \param[in] current_input_is_sid Self-explanatory
+ * \param[in] resp_msg_p Pointer to l1sap_tch_rts_ind() internal variable
+ * \param[in] resp_l1sap_p ditto
+ * \param[in] empty_l1sap_p ditto
+ *
+ * This function gets called with pointers to l1sap_tch_rts_ind() internal
+ * variables and cannot be properly understood on its own, without
+ * understanding the parent function first. This situation is unfortunate,
+ * but it was the only way to factor the present logic out of
+ * l1sap_tch_rts_ind() main body.
+ */
+static void fr_hr_efr_dtxd_output(struct gsm_lchan *lchan, uint32_t fn,
+ bool current_input_is_sid,
+ struct msgb **resp_msg_p,
+ struct osmo_phsap_prim **resp_l1sap_p,
+ struct osmo_phsap_prim *empty_l1sap_p)
{
- /* Avoid sending bw-efficient AMR to lower layers, most bts models
- * don't support it. */
- if(lchan->tch_mode == GSM48_CMODE_SPEECH_AMR &&
- !rtppayload_is_octet_aligned(resp_msg->data, resp_msg->len)) {
- LOGPLCHAN(lchan, DL1P, LOGL_NOTICE,
- "RTP->L1: Dropping unexpected AMR encoding (bw-efficient?) %s\n",
- osmo_hexdump(resp_msg->data, resp_msg->len));
- return false;
+ struct msgb *resp_msg = *resp_msg_p;
+ bool sid_in_hand = current_input_is_sid;
+
+ /* Are we at a mandatory-Tx frame position? If so, clear the state flag
+ * of "we already transmitted a SID in this window" - as of right now,
+ * a SID has _not_ been transmitted in the present window yet, and if
+ * we are in a DTX pause, we do need to transmit a SID update as soon
+ * as we are able to, FACCH permitting. */
+ if (fr_hr_efr_sid_position(lchan, fn))
+ lchan->tch.dtx_fr_hr_efr.dl_sid_transmitted = false;
+
+ /* The next stanza implements logic that was originally meant to reside
+ * in the TFO block in TRAUs: if the source feeding us RTP is in a DTXu
+ * pause (resp_msg == NULL, meaning we got nothing from RTP) *and* the
+ * most recent RTP input we did get was a SID, and that SID hasn't
+ * expired, then we need to replicate that SID. */
+ if (!resp_msg && lchan->tch.dtx_fr_hr_efr.last_rtp_input_was_sid &&
+ lchan->tch.dtx_fr_hr_efr.last_sid_age < 47) {
+ /* Whatever we do further below, any time another 20 ms window
+ * has passed since the last SID was received in RTP, we have
+ * to age that cached SID. */
+ lchan->tch.dtx_fr_hr_efr.last_sid_age++;
+
+ /* The following conditional checking dl_sid_transmitted flag
+ * is peculiar to our sans-E1 architecture. In the original
+ * T1/E1 Abis architecture the TFO-enabled TRAU would repeat
+ * the cached SID from call leg A into *every* 20 ms frame in
+ * its Abis output, and then the BTS would select which ones
+ * it should transmit per the rules of GSM 06.31/06.81 section
+ * 5.1.2. But in our architecture it would be silly and
+ * wasteful to allocate and fill an msgb for this cached SID
+ * only to toss it later in the same function - hence the
+ * logic of section 5.1.2 is absorbed into the decision right
+ * here to proceed with cached SID regurgitation or not,
+ * in the form of the following conditional. */
+ if (!lchan->tch.dtx_fr_hr_efr.dl_sid_transmitted)
+ resp_msg = l1sap_msgb_alloc(lchan->tch.dtx_fr_hr_efr.last_sid_len);
+ if (resp_msg) {
+ resp_msg->l2h = msgb_put(resp_msg,
+ lchan->tch.dtx_fr_hr_efr.last_sid_len);
+ memcpy(resp_msg->l2h, lchan->tch.dtx_fr_hr_efr.last_sid,
+ lchan->tch.dtx_fr_hr_efr.last_sid_len);
+ *resp_msg_p = resp_msg;
+ *resp_l1sap_p = msgb_l1sap_prim(resp_msg);
+ sid_in_hand = true;
+ }
+ } else if (resp_msg && sid_in_hand) {
+ /* This "else if" leg implements the logic of section 5.1.2
+ * for cases when a SID is already present in the RTP input,
+ * as indicated by (resp_msg != NULL) and sid_in_hand being
+ * true. Because we are in the "else" clause of the big "if"
+ * above, this path executes only when the SID has come from
+ * RTP in _this_ frame, rather than regurgitated from cache.
+ * But be it fresh or cached, the rules of section 5.1.2 still
+ * apply, and if we've already transmitted a SID in the current
+ * window, then we need to suppress further SIDs and send
+ * an empty payload to the BTS model, causing the latter
+ * to transmit an induced BFI condition on the air. This
+ * strange-seeming behavior is needed so that the spec-compliant
+ * Rx DTX handler in the MS will produce the expected output,
+ * same as if the GSM network were the old-fashioned kind with
+ * Abis TRAUs and TFO. */
+ if (lchan->tch.dtx_fr_hr_efr.dl_sid_transmitted) {
+ msgb_free(resp_msg);
+ resp_msg = NULL;
+ *resp_msg_p = NULL;
+ *resp_l1sap_p = empty_l1sap_p;
+ }
}
- return true;
+
+ /* The following conditional answers this question: are we actually
+ * transmitting a SID frame on the air right now, at this frame number?
+ * If we are certain the BTS model is going to transmit this SID,
+ * we set the state flag so we won't be transmitting any more SIDs
+ * until we either hit the next mandatory-Tx position or get another
+ * little talkspurt followed by new SID. The check for FACCH stealing
+ * is included because if the BTS model is going to transmit FACCH in
+ * the current FN, then we are not actually transmitting SID right now,
+ * and we still need to transmit a SID ASAP, as soon as the TCH becomes
+ * becomes free from FACCH activity. GSM 06.31/06.81 section 5.1.2
+ * does mention that if the mandatory-Tx frame position is taken up
+ * by FACCH, then we need to send SID in the following frame. */
+ if (resp_msg && sid_in_hand && !lchan->tch.dtx_fr_hr_efr.dl_facch_stealing)
+ lchan->tch.dtx_fr_hr_efr.dl_sid_transmitted = true;
}
-/* TCH-RTS-IND prim recevied from bts model */
+/* TCH-RTS-IND prim received from bts model */
static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct ph_tch_param *rts_ind)
{
@@ -935,19 +1499,19 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
struct gsm_lchan *lchan;
uint8_t chan_nr, marker = 0;
uint32_t fn;
- int rc;
+ bool is_fr_hr_efr_sid = false;
chan_nr = rts_ind->chan_nr;
fn = rts_ind->fn;
gsm_fn2gsmtime(&g_time, fn);
- DEBUGPGT(DL1P, &g_time, "Rx TCH-RTS.ind chan_nr=%s\n", rsl_chan_nr_str(chan_nr));
-
lchan = get_active_lchan_by_chan_nr(trx, chan_nr);
if (!lchan) {
LOGPGT(DL1P, LOGL_ERROR, &g_time, "No lchan for PH-RTS.ind (chan_nr=%s)\n", rsl_chan_nr_str(chan_nr));
return 0;
+ } else {
+ LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "Rx TCH-RTS.ind\n");
}
if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
@@ -961,13 +1525,9 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
lchan->abis_ip.rtp_socket->rx_user_ts += GSM_RTP_DURATION;
}
/* get a msgb from the dl_tx_queue */
- resp_msg = msgb_dequeue(&lchan->dl_tch_queue);
+ resp_msg = msgb_dequeue_count(&lchan->dl_tch_queue, &lchan->dl_tch_queue_len);
if (!resp_msg) {
- DEBUGPGT(DL1P, &g_time, "%s DL TCH Tx queue underrun\n", gsm_lchan_name(lchan));
- resp_l1sap = &empty_l1sap;
- } else if(!rtppayload_is_valid(lchan, resp_msg)) {
- msgb_free(resp_msg);
- resp_msg = NULL;
+ LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "DL TCH Tx queue underrun\n");
resp_l1sap = &empty_l1sap;
} else {
/* Obtain RTP header Marker bit from control buffer */
@@ -977,16 +1537,27 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
msgb_push(resp_msg, sizeof(*resp_l1sap));
resp_msg->l1h = resp_msg->data;
resp_l1sap = msgb_l1sap_prim(resp_msg);
+
+ /* FR/HR/EFR SID or non-SID input handling */
+ if (lchan->tch_mode == GSM48_CMODE_SPEECH_V1 ||
+ lchan->tch_mode == GSM48_CMODE_SPEECH_EFR) {
+ bool drop;
+
+ drop = !fr_hr_efr_dtxd_input(lchan, resp_msg,
+ &is_fr_hr_efr_sid);
+ if (OSMO_UNLIKELY(drop)) {
+ msgb_free(resp_msg);
+ resp_msg = NULL;
+ resp_l1sap = &empty_l1sap;
+ }
+ }
}
- /* check for pending REL_IND */
- if (lchan->pending_rel_ind_msg) {
- LOGPGT(DRSL, LOGL_INFO, &g_time, "%s Forward REL_IND to L3\n", gsm_lchan_name(lchan));
- /* Forward it to L3 */
- rc = abis_bts_rsl_sendmsg(lchan->pending_rel_ind_msg);
- lchan->pending_rel_ind_msg = NULL;
- if (rc < 0)
- return rc;
+ /* FR/HR/EFR DTXd output stage */
+ if (lchan->tch_mode == GSM48_CMODE_SPEECH_V1 ||
+ lchan->tch_mode == GSM48_CMODE_SPEECH_EFR) {
+ fr_hr_efr_dtxd_output(lchan, fn, is_fr_hr_efr_sid, &resp_msg,
+ &resp_l1sap, &empty_l1sap);
}
memset(resp_l1sap, 0, sizeof(*resp_l1sap));
@@ -996,48 +1567,67 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
resp_l1sap->u.tch.fn = fn;
resp_l1sap->u.tch.marker = marker;
- DEBUGPGT(DL1P, &g_time, "Tx TCH.req chan_nr=%s\n", rsl_chan_nr_str(chan_nr));
+ LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "Tx TCH.req\n");
l1sap_down(trx, resp_l1sap);
return 0;
}
+/* Reset link timeout to current value. */
+void radio_link_timeout_reset(struct gsm_lchan *lchan)
+{
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+
+ lchan->s = bts->radio_link_timeout.current;
+}
+
/* process radio link timeout counter S. Follows TS 05.08 Section 5.2
* "MS Procedure" as the "BSS Procedure [...] shall be determined by the
* network operator." */
-static void radio_link_timeout(struct gsm_lchan *lchan, int bad_frame)
+static void radio_link_timeout(struct gsm_lchan *lchan, bool bad_frame)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
+ /* Bypass radio link timeout on VGCS/VBS channels: There is no
+ * uplink SACCH on these when talker is not active. */
+ if (rsl_chan_rt_is_asci(lchan->rsl_chan_rt) && lchan->asci.talker_active != VGCS_TALKER_ACTIVE)
+ return;
+
/* Bypass radio link timeout if set to -1 */
- if (bts->radio_link_timeout < 0)
+ if (bts->radio_link_timeout.current < 0)
return;
/* if link loss criterion already reached */
if (lchan->s == 0) {
- DEBUGP(DMEAS, "%s radio link counter S already 0.\n",
- gsm_lchan_name(lchan));
+ LOGPLCHAN(lchan, DMEAS, LOGL_DEBUG,
+ "radio link timeout counter S is already 0\n");
return;
}
if (bad_frame) {
- /* count down radio link counter S */
- lchan->s--;
- DEBUGP(DMEAS, "%s counting down radio link counter S=%d\n",
- gsm_lchan_name(lchan), lchan->s);
- if (lchan->s == 0)
+ LOGPLCHAN(lchan, DMEAS, LOGL_DEBUG,
+ "decreasing radio link timeout counter S=%d -> %d\n",
+ lchan->s, lchan->s - 1);
+ lchan->s--; /* count down radio link counter S */
+ if (lchan->s == 0) {
+ LOGPLCHAN(lchan, DMEAS, LOGL_NOTICE,
+ "radio link timeout counter S reached zero, "
+ "dropping connection\n");
rsl_tx_conn_fail(lchan, RSL_ERR_RADIO_LINK_FAIL);
+ }
return;
}
- if (lchan->s < bts->radio_link_timeout) {
+ if (lchan->s < bts->radio_link_timeout.current) {
/* count up radio link counter S */
- lchan->s += 2;
- if (lchan->s > bts->radio_link_timeout)
- lchan->s = bts->radio_link_timeout;
- DEBUGP(DMEAS, "%s counting up radio link counter S=%d\n",
- gsm_lchan_name(lchan), lchan->s);
+ int s = lchan->s + 2;
+ if (s > bts->radio_link_timeout.current)
+ s = bts->radio_link_timeout.current;
+ LOGPLCHAN(lchan, DMEAS, LOGL_DEBUG,
+ "increasing radio link timeout counter S=%d -> %d\n",
+ lchan->s, s);
+ lchan->s = s;
}
}
@@ -1074,6 +1664,59 @@ int bts_check_for_first_ciphrd(struct gsm_lchan *lchan,
return check_for_first_ciphrd(lchan, data, len);
}
+/* Decide if repeated UL-SACCH should be applied or not. If the BER level, of
+ * the received SACCH blocks rises above a certain threshold UL-SACCH
+ * repetition is enabled */
+static void repeated_ul_sacch_active_decision(struct gsm_lchan *lchan,
+ uint16_t ber10k)
+{
+ uint16_t upper = 0;
+ uint16_t lower = 0;
+ bool prev_repeated_ul_sacch_active = lchan->rep_acch.ul_sacch_active;
+
+ /* This is an optimization so that we exit as quickly as possible if
+ * there are no uplink SACCH repetition capabilities present.
+ * However If the repeated UL-SACCH capabilities vanish for whatever
+ * reason, we must be sure that UL-SACCH repetition is disabled. */
+ if (!lchan->rep_acch_cap.ul_sacch) {
+ lchan->rep_acch.ul_sacch_active = false;
+ goto out;
+ }
+
+ /* Threshold disabled (repetition is always on) */
+ if (lchan->rep_acch_cap.rxqual == 0) {
+ lchan->rep_acch.ul_sacch_active = true;
+ goto out;
+ }
+
+ /* convert from RXQUAL value to ber10k value.
+ * see also GSM 05.08, section 8.2.4 (first table, without frame */
+ static const uint16_t ber10k_by_rxqual_upper[] =
+ { 0, 20, 40, 80, 160, 320, 640, 1280 };
+ static const uint16_t ber10k_by_rxqual_lower[] =
+ { 0, 0, 0, 20, 40, 80, 160, 320 };
+ /* Note: The values in the upper vector are taken from the left side
+ * of the table in GSM 05.08, section 8.2.4. The lower vector is just
+ * the upper vector shifted by 2. */
+
+ upper = ber10k_by_rxqual_upper[lchan->rep_acch_cap.rxqual];
+ lower = ber10k_by_rxqual_lower[lchan->rep_acch_cap.rxqual];
+
+ /* If upper/rxqual == 0, then repeated UL-SACCH is always on */
+ if (ber10k >= upper)
+ lchan->rep_acch.ul_sacch_active = true;
+ else if (ber10k <= lower)
+ lchan->rep_acch.ul_sacch_active = false;
+
+out:
+ if (lchan->rep_acch.ul_sacch_active == prev_repeated_ul_sacch_active)
+ return;
+ if (lchan->rep_acch.ul_sacch_active)
+ LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "UL-SACCH repetition: inactive => active\n");
+ else
+ LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "UL-SACCH repetition: active => inactive\n");
+}
+
/* DATA received from bts model */
static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct ph_data_param *data_ind)
@@ -1087,10 +1730,8 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
uint8_t chan_nr, link_id;
uint8_t tn;
uint32_t fn;
- int8_t rssi;
enum osmo_ph_pres_info_type pr_info = data_ind->pdch_presence_info;
- rssi = data_ind->rssi;
chan_nr = data_ind->chan_nr;
link_id = data_ind->link_id;
fn = data_ind->fn;
@@ -1105,36 +1746,33 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
lchan = get_lchan_by_chan_nr(trx, chan_nr);
if (!lchan)
LOGPGT(DL1P, LOGL_ERROR, &g_time, "No lchan for chan_nr=%s\n", rsl_chan_nr_str(chan_nr));
- if (lchan && lchan->loopback && !L1SAP_IS_PTCCH(fn)) {
+ if (lchan && lchan->loopback) {
/* we are in loopback mode (for BER testing)
* mode and need to enqeue the frame to be
* returned in downlink */
- queue_limit_to(gsm_lchan_name(lchan), &lchan->dl_tch_queue, 1);
- msgb_enqueue(&lchan->dl_tch_queue, msg);
+ lchan_dl_tch_queue_enqueue(lchan, msg, 1);
/* Return 1 to signal that we're still using msg
* and it should not be freed */
return 1;
}
- /* don't send bad frames to PCU */
- if (len == 0)
- return -EINVAL;
+ /* There can be no DATA.ind on PTCCH/U (rather RACH.ind instead), but some
+ * BTS models with buggy implementation may still be sending them to us. */
if (L1SAP_IS_PTCCH(fn)) {
- pcu_tx_data_ind(&trx->ts[tn], PCU_IF_SAPI_PTCCH, fn,
- 0 /* ARFCN */, L1SAP_FN2PTCCHBLOCK(fn),
- data, len, rssi, data_ind->ber10k,
- data_ind->ta_offs_256bits/64,
- data_ind->lqual_cb);
- } else {
- /* drop incomplete UL block */
- if (pr_info != PRES_INFO_BOTH)
- return 0;
- /* PDTCH / PACCH frame handling */
- pcu_tx_data_ind(&trx->ts[tn], PCU_IF_SAPI_PDTCH, fn, 0 /* ARFCN */,
- L1SAP_FN2MACBLOCK(fn), data, len, rssi, data_ind->ber10k,
- data_ind->ta_offs_256bits/64, data_ind->lqual_cb);
+ LOGPGT(DL1P, LOGL_NOTICE, &g_time, "There can be no DATA.ind on PTCCH/U. "
+ "This is probably a bug of the BTS model you're using, please fix!\n");
+ return -EINVAL;
}
+
+ /* Drop all data from incomplete UL block */
+ if (pr_info != PRES_INFO_BOTH)
+ len = 0;
+
+ /* PDTCH / PACCH frame handling */
+ pcu_tx_data_ind(&trx->ts[tn], PCU_IF_SAPI_PDTCH, fn, trx->arfcn,
+ L1SAP_FN2MACBLOCK(fn), data, len, data_ind->rssi, data_ind->ber10k,
+ data_ind->ta_offs_256bits/64, data_ind->lqual_cb);
return 0;
}
@@ -1144,10 +1782,37 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
return 0;
}
+ /* The ph_data_param contained in the l1sap primitive may contain
+ * measurement data. If this data is present, forward it for
+ * processing */
+ if (bts_internal_flag_get(trx->bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB))
+ process_l1sap_meas_data(lchan, l1sap, PRIM_PH_DATA);
+
+ if (L1SAP_IS_LINK_SACCH(link_id)) {
+ repeated_ul_sacch_active_decision(lchan, data_ind->ber10k);
+
+ /* Radio Link Timeout counter */
+ if (len == 0) {
+ LOGPLCGT(lchan, &g_time, DL1P, LOGL_INFO, "Lost SACCH block\n");
+ radio_link_timeout(lchan, true);
+ } else {
+ radio_link_timeout(lchan, false);
+ }
+
+ /* Trigger the measurement reporting/processing logic */
+ lchan_meas_handle_sacch(lchan, msg);
+ }
+
+ if (L1SAP_IS_LINK_SACCH(link_id))
+ le = &lchan->lapdm_ch.lapdm_acch;
+ else
+ le = &lchan->lapdm_ch.lapdm_dcch;
+
/* bad frame */
if (len == 0) {
- if (L1SAP_IS_LINK_SACCH(link_id))
- radio_link_timeout(lchan, 1);
+ /* Notify current receive FN to lapdm. */
+ lapdm_t200_fn(le, fn);
+
return -EINVAL;
}
@@ -1155,39 +1820,221 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
if (lchan->ho.active == HANDOVER_WAIT_FRAME)
handover_frame(lchan);
- if (L1SAP_IS_LINK_SACCH(link_id)) {
- radio_link_timeout(lchan, 0);
- le = &lchan->lapdm_ch.lapdm_acch;
- /* save the SACCH L1 header in the lchan struct for RSL MEAS RES */
- if (len < 2) {
- LOGPGT(DL1P, LOGL_NOTICE, &g_time, "SACCH with size %u<2 !?!\n", len);
- return -EINVAL;
- }
- /* Some brilliant engineer decided that the ordering of
- * fields on the Um interface is different from the
- * order of fields in RLS. See TS 04.04 (Chapter 7.2)
- * vs. TS 08.58 (Chapter 9.3.10). */
- lchan->meas.l1_info[0] = data[0] << 3;
- lchan->meas.l1_info[0] |= ((data[0] >> 5) & 1) << 2;
- lchan->meas.l1_info[1] = data[1];
- lchan->meas.flags |= LC_UL_M_F_L1_VALID;
-
- lchan_ms_pwr_ctrl(lchan, data[0] & 0x1f, data_ind->rssi);
- } else
- le = &lchan->lapdm_ch.lapdm_dcch;
+ if (rsl_chan_rt_is_asci(lchan->rsl_chan_rt)) {
+ /* report first valid received frame to VGCS talker process */
+ if (lchan->asci.talker_active == VGCS_TALKER_WAIT_FRAME)
+ vgcs_talker_frame(lchan);
+ /* Do not forward any message that is received on the uplink to LAPD while
+ * the uplink is not active. If the MS did not recognize (fast enough) that
+ * the uplink is free, it may continue to transmit LAPD messages. A
+ * response by LAPD to these messages is not desired and not required. If
+ * LAPD would respond, it would cause stopping transmission of UPLINK FREE
+ * messages. No MS could access the uplink anymore.
+ */
+ if (lchan->asci.talker_active != VGCS_TALKER_ACTIVE)
+ return 0;
+ }
if (check_for_first_ciphrd(lchan, data, len))
l1sap_tx_ciph_req(lchan->ts->trx, chan_nr, 1, 0);
/* SDCCH, SACCH and FACCH all go to LAPDm */
- msgb_pull(msg, (msg->l2h - msg->data));
- msg->l1h = NULL;
+ msgb_pull_to_l2(msg);
lapdm_phsap_up(&l1sap->oph, le);
+ /* Notify current receive FN to lapdm. */
+ lapdm_t200_fn(le, fn);
+
/* don't free, because we forwarded data */
return 1;
}
+/* process one MAC block of unpacked bits of a non-transparent CSD channel */
+static void gsmtap_csd_rlp_process(struct gsm_lchan *lchan, bool is_uplink,
+ const struct ph_tch_param *tch_ind,
+ const uint8_t *data, unsigned int data_len)
+{
+ struct gsm_bts_trx *trx = lchan->ts->trx;
+ struct gsmtap_inst *inst = trx->bts->gsmtap.inst;
+ struct osmo_rlp_frame_decoded rlpf;
+ pbit_t *rlp_buf;
+ uint16_t arfcn;
+ int byte_len;
+
+ if (!inst || !trx->bts->gsmtap.rlp)
+ return;
+
+ if (lchan->csd_mode != LCHAN_CSD_M_NT)
+ return;
+
+ if (is_uplink)
+ rlp_buf = lchan->tch.csd.rlp_buf_ul;
+ else
+ rlp_buf = lchan->tch.csd.rlp_buf_dl;
+
+ /* TCH/F 9.6: 4x60bit block => 240bit RLP frame
+ * TCH/F 4.8: 2x 2x60bit blocks starting at B0/B2/B4 => 240bit RLP frame
+ * TCH/H 4.8: 4x60bit block => 240bit RLP frame
+ * TCH/F 2.4: 2x36bit blocks => transparent only
+ * TCH/H 2.4: 4x36bit blocks => transparent only
+ * TCH/F 14.4: 2x 290 bit block (starting with M1=0) => 576-bit RLP frame
+ */
+
+ if (lchan->type == GSM_LCHAN_TCH_F && lchan->tch_mode == GSM48_CMODE_DATA_6k0) {
+ /* in this mode we have 120bit MAC blocks; two of them need to be concatenated
+ * to render a 240-bit RLP frame. The fist block is present in B0/B2/B4.
+ * The E7 bit is used to indicate the Frame MF0a */
+ OSMO_ASSERT(data_len == 120);
+ ubit_t e7 = data[4*7+3];
+ if (e7 == 0) {
+ osmo_ubit2pbit_ext(rlp_buf, 0, data, 0, data_len, 1);
+ return;
+ } else {
+ osmo_ubit2pbit_ext(rlp_buf, 120, data, 0, data_len, 1);
+ byte_len = 240/8;
+ }
+ } else if (lchan->type == GSM_LCHAN_TCH_F && lchan->tch_mode == GSM48_CMODE_DATA_14k5) {
+ /* in this mode we have 290bit MAC blocks containing M1, M2 and 288 data bits;
+ * two of them need to be concatenated to render a
+ * 576-bit RLP frame. The start of a RLP frame is
+ * denoted by a block with M1-bit set to 0. */
+ OSMO_ASSERT(data_len == 290);
+ ubit_t m1 = data[0];
+ if (m1 == 0) {
+ osmo_ubit2pbit_ext(rlp_buf, 0, data, 2, data_len, 1);
+ return;
+ } else {
+ osmo_ubit2pbit_ext(rlp_buf, 288, data, 2, data_len, 1);
+ byte_len = 576/8;
+ }
+ } else {
+ byte_len = osmo_ubit2pbit_ext(rlp_buf, 0, data, 0, data_len, 1);
+ }
+
+ if (trx->bts->gsmtap.rlp_skip_null) {
+ int rc = osmo_rlp_decode(&rlpf, 0, rlp_buf, byte_len);
+ if (rc == 0 && rlpf.ftype == OSMO_RLP_FT_U && rlpf.u_ftype == OSMO_RLP_U_FT_NULL)
+ return;
+ }
+
+ arfcn = trx->arfcn;
+ if (is_uplink)
+ arfcn |= GSMTAP_ARFCN_F_UPLINK;
+
+ gsmtap_send_ex(inst, GSMTAP_TYPE_GSM_RLP, arfcn, lchan->ts->nr,
+ lchan->type == GSM_LCHAN_TCH_H ? GSMTAP_CHANNEL_VOICE_H : GSMTAP_CHANNEL_VOICE_F,
+ lchan->nr, tch_ind->fn, tch_ind->rssi, 0, rlp_buf, byte_len);
+
+}
+
+static void send_ul_rtp_packet_data(struct gsm_lchan *lchan, const struct ph_tch_param *tch_ind,
+ const uint8_t *data, uint16_t data_len)
+{
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+ uint8_t rtp_pl[RFC4040_RTP_PLEN];
+ int rc;
+
+ gsmtap_csd_rlp_process(lchan, true, tch_ind, data, data_len);
+
+ rc = csd_v110_rtp_encode(lchan, &rtp_pl[0], data, data_len);
+ if (rc < 0)
+ return;
+
+ rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_TOTAL);
+ if (lchan->rtp_tx_marker)
+ rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_MARKER);
+
+ osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket,
+ &rtp_pl[0], sizeof(rtp_pl),
+ fn_ms_adj(tch_ind->fn, lchan),
+ lchan->rtp_tx_marker);
+ /* Only clear the marker bit once we have sent a RTP packet with it */
+ lchan->rtp_tx_marker = false;
+}
+
+/* a helper function for the logic in l1sap_tch_ind() */
+static void send_ul_rtp_packet_speech(struct gsm_lchan *lchan, uint32_t fn,
+ const uint8_t *rtp_pl, uint16_t rtp_pl_len)
+{
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+
+ if (lchan->abis_ip.osmux.use) {
+ lchan_osmux_send_frame(lchan, rtp_pl, rtp_pl_len,
+ fn_ms_adj(fn, lchan), lchan->rtp_tx_marker);
+ } else if (lchan->abis_ip.rtp_socket) {
+ osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket,
+ rtp_pl, rtp_pl_len, fn_ms_adj(fn, lchan), lchan->rtp_tx_marker);
+ rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_TOTAL);
+ if (lchan->rtp_tx_marker)
+ rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_MARKER);
+ }
+ /* Only clear the marker bit once we have sent a RTP packet with it */
+ lchan->rtp_tx_marker = false;
+}
+
+/* a helper function for emitting HR1 UL in RFC 5993 format */
+static void send_rtp_rfc5993(struct gsm_lchan *lchan, uint32_t fn,
+ struct msgb *msg)
+{
+ uint8_t toc;
+
+ OSMO_ASSERT(msg->len == GSM_HR_BYTES);
+ /* FIXME: implement proper SID classification per GSM 06.41 section
+ * 6.1.1; see OS#6036. Until then, detect error-free SID frames
+ * using our existing osmo_hr_check_sid() function. */
+ if (osmo_hr_check_sid(msg->data, msg->len))
+ toc = 0x20;
+ else
+ toc = 0x00;
+ msgb_push_u8(msg, toc);
+ send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len);
+}
+
+/* A helper function for l1sap_tch_ind(): handling BFI
+ *
+ * Please note that we pass the msgb to this function, even though it is
+ * currently not used. This msgb passing is a provision for adding
+ * support for TRAU-UL-like RTP payload formats like TW-TS-001 that allow
+ * indicating BFI along with deemed-bad frame data bits, just like
+ * GSM 08.60 and 08.61 TRAU-UL frames.
+ */
+static void tch_ul_bfi_handler(struct gsm_lchan *lchan,
+ const struct gsm_time *g_time, struct msgb *msg)
+{
+ uint32_t fn = g_time->fn;
+ uint8_t ecu_out[GSM_FR_BYTES];
+ int rc;
+
+ /* Are we applying an ECU to this uplink, and are we in a state
+ * (not DTX pause) where we emit ECU output? */
+ if (lchan->ecu_state && !osmo_ecu_is_dtx_pause(lchan->ecu_state)) {
+ rc = osmo_ecu_frame_out(lchan->ecu_state, ecu_out);
+ /* did it actually give us some output? */
+ if (rc > 0) {
+ /* yes, send it out in RTP */
+ send_ul_rtp_packet_speech(lchan, fn, ecu_out, rc);
+ return;
+ }
+ }
+
+ /* Are we in rtp continuous-streaming special mode? If so, send out
+ * a BFI packet as zero-length RTP payload. */
+ if (lchan->ts->trx->bts->rtp_nogaps_mode) {
+ send_ul_rtp_packet_speech(lchan, fn, NULL, 0);
+ return;
+ }
+
+ /* Most classic form of BFI handling: generate an intentional gap
+ * in the outgoing RTP stream. */
+ LOGPLCGT(lchan, g_time, DRTP, LOGL_DEBUG,
+ "Skipping RTP frame with lost payload\n");
+ if (lchan->abis_ip.osmux.use)
+ lchan_osmux_skipped_frame(lchan, fn_ms_adj(fn, lchan));
+ else if (lchan->abis_ip.rtp_socket)
+ osmo_rtp_skipped_frame(lchan->abis_ip.rtp_socket, fn_ms_adj(fn, lchan));
+ lchan->rtp_tx_marker = true;
+}
+
/* TCH received from bts model */
static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
struct ph_tch_param *tch_ind)
@@ -1204,42 +2051,62 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
gsm_fn2gsmtime(&g_time, fn);
- LOGPGT(DL1P, LOGL_INFO, &g_time, "Rx TCH.ind chan_nr=%s\n", rsl_chan_nr_str(chan_nr));
-
lchan = get_active_lchan_by_chan_nr(trx, chan_nr);
if (!lchan) {
LOGPGT(DL1P, LOGL_ERROR, &g_time, "No lchan for TCH.ind (chan_nr=%s)\n", rsl_chan_nr_str(chan_nr));
return 0;
+ } else {
+ LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "Rx TCH.ind\n");
}
- msgb_pull(msg, sizeof(*l1sap));
+ /* Notify current receive FN to lapdm.
+ * TCH frames may be indicated before FACCH frames are indicated. To prevent T200 timeout before FACCH is
+ * received, subtract one frame number, so that timeout is processed next time after FACCH is received.
+ */
+ lapdm_t200_fn(&lchan->lapdm_ch.lapdm_dcch, GSM_TDMA_FN_SUB(fn, 1));
+
+ /* The ph_tch_param contained in the l1sap primitive may contain
+ * measurement data. If this data is present, forward it for
+ * processing */
+ if (bts_internal_flag_get(trx->bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB))
+ process_l1sap_meas_data(lchan, l1sap, PRIM_TCH);
+
+ msgb_pull_to_l2(msg);
/* Low level layers always call us when TCH content is expected, even if
* the content is not available due to decoding issues. Content not
* available is expected as empty payload. We also check if quality is
* good enough. */
if (msg->len && tch_ind->lqual_cb >= bts->min_qual_norm) {
+ /* feed the good frame to the ECU, if we are applying one */
+ if (lchan->ecu_state)
+ osmo_ecu_frame_in(lchan->ecu_state, false, msg->data, msg->len);
/* hand msg to RTP code for transmission */
- if (lchan->abis_ip.rtp_socket)
- osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket,
- msg->data, msg->len, fn_ms_adj(fn, lchan), lchan->rtp_tx_marker);
+ switch (lchan->rsl_cmode) {
+ case RSL_CMOD_SPD_SPEECH:
+ if (bts->emit_hr_rfc5993 && lchan->type == GSM_LCHAN_TCH_H &&
+ lchan->tch_mode == GSM48_CMODE_SPEECH_V1)
+ send_rtp_rfc5993(lchan, fn, msg);
+ else
+ send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len);
+ break;
+ case RSL_CMOD_SPD_DATA:
+ send_ul_rtp_packet_data(lchan, tch_ind, msg->data, msg->len);
+ break;
+ case RSL_CMOD_SPD_SIGN:
+ return 0; /* drop stale TCH.ind */
+ default: /* shall not happen */
+ OSMO_ASSERT(0);
+ }
/* if loopback is enabled, also queue received RTP data */
if (lchan->loopback) {
- /* make sure the queue doesn't get too long */
- queue_limit_to(gsm_lchan_name(lchan), &lchan->dl_tch_queue, 1);
- /* add new frame to queue */
- msgb_enqueue(&lchan->dl_tch_queue, msg);
+ /* add new frame to queue, make sure the queue doesn't get too long */
+ lchan_dl_tch_queue_enqueue(lchan, msg, 1);
/* Return 1 to signal that we're still using msg and it should not be freed */
return 1;
}
- /* Only clear the marker bit once we have sent a RTP packet with it */
- lchan->rtp_tx_marker = false;
} else {
- DEBUGPGT(DRTP, &g_time, "Skipping RTP frame with lost payload (chan_nr=0x%02x)\n",
- chan_nr);
- if (lchan->abis_ip.rtp_socket)
- osmo_rtp_skipped_frame(lchan->abis_ip.rtp_socket, fn_ms_adj(fn, lchan));
- lchan->rtp_tx_marker = true;
+ tch_ul_bfi_handler(lchan, &g_time, msg);
}
lchan->tch.last_fn = fn;
@@ -1248,14 +2115,15 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
#define RACH_MIN_TOA256 -2 * 256
-static bool rach_pass_filter(struct ph_rach_ind_param *rach_ind, struct gsm_bts *bts)
+static bool rach_pass_filter(struct ph_rach_ind_param *rach_ind, struct gsm_bts *bts,
+ const char *chan_name)
{
int16_t toa256 = rach_ind->acc_delay_256bits;
/* Check for RACH exceeding BER threshold (ghost RACH) */
if (rach_ind->ber10k > bts->max_ber10k_rach) {
- LOGPFN(DL1C, LOGL_INFO, rach_ind->fn, "Ignoring RACH request: "
- "BER10k(%u) > BER10k_MAX(%u)\n",
+ LOGPFN(DL1C, LOGL_DEBUG, rach_ind->fn, "Ignoring an Access Burst on %s: "
+ "BER10k(%u) > BER10k_MAX(%u)\n", chan_name,
rach_ind->ber10k, bts->max_ber10k_rach);
return false;
}
@@ -1266,16 +2134,16 @@ static bool rach_pass_filter(struct ph_rach_ind_param *rach_ind, struct gsm_bts
* according to maximal allowed Timing Advance value.
*/
if (toa256 < RACH_MIN_TOA256 || toa256 > bts->max_ta * 256) {
- LOGPFN(DL1C, LOGL_INFO, rach_ind->fn, "Ignoring RACH request: "
- "ToA(%d) exceeds the allowed range (%d..%d)\n",
+ LOGPFN(DL1C, LOGL_DEBUG, rach_ind->fn, "Ignoring an Access Burst on %s: "
+ "ToA(%d) exceeds the allowed range (%d..%d)\n", chan_name,
toa256, RACH_MIN_TOA256, bts->max_ta * 256);
return false;
}
/* Link quality defined by C/I (Carrier-to-Interference ratio) */
if (rach_ind->lqual_cb < bts->min_qual_rach) {
- LOGPFN(DL1C, LOGL_INFO, rach_ind->fn, "Ignoring RACH request: "
- "link quality (%d) below the minimum (%d)\n",
+ LOGPFN(DL1C, LOGL_DEBUG, rach_ind->fn, "Ignoring an Access Burst on %s: "
+ "link quality (%d) below the minimum (%d)\n", chan_name,
rach_ind->lqual_cb, bts->min_qual_rach);
return false;
}
@@ -1283,23 +2151,58 @@ static bool rach_pass_filter(struct ph_rach_ind_param *rach_ind, struct gsm_bts
return true;
}
-/* Special case where handover RACH is detected */
-static int l1sap_handover_rach(struct gsm_bts_trx *trx,
- struct osmo_phsap_prim *l1sap, struct ph_rach_ind_param *rach_ind)
+/* Special case where RACH on DCCH uplink is detected */
+static int l1sap_dcch_rach(struct gsm_bts_trx *trx, struct ph_rach_ind_param *rach_ind)
{
+ struct gsm_lchan *lchan;
+
/* Filter out noise / interference / ghosts */
- if (!rach_pass_filter(rach_ind, trx->bts)) {
+ if (!rach_pass_filter(rach_ind, trx->bts, "DCCH")) {
rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_DROP);
return 0;
}
- handover_rach(get_lchan_by_chan_nr(trx, rach_ind->chan_nr),
- rach_ind->ra, rach_ind->acc_delay);
+ lchan = get_lchan_by_chan_nr(trx, rach_ind->chan_nr);
+ /* Differentiate + dispatch hand-over and VGCS RACH */
+ if (rsl_chan_rt_is_asci(lchan->rsl_chan_rt)) {
+ rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_VGCS);
+ vgcs_rach(lchan, rach_ind->ra, rach_ind->acc_delay, rach_ind->fn);
+ } else {
+ rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_HO);
+ handover_rach(lchan, rach_ind->ra, rach_ind->acc_delay);
+ }
/* must return 0, so in case of msg at l1sap, it will be freed */
return 0;
}
+/* Special case for Access Bursts on PDTCH/U or PTCCH/U */
+static int l1sap_pdch_rach(struct gsm_bts_trx *trx, struct ph_rach_ind_param *rach_ind)
+{
+ /* Filter out noise / interference / ghosts */
+ if (!rach_pass_filter(rach_ind, trx->bts, "PDCH"))
+ return -EAGAIN;
+
+ /* PTCCH/U (Packet Timing Advance Control Channel) */
+ if (L1SAP_IS_PTCCH(rach_ind->fn)) {
+ LOGPFN(DL1P, LOGL_DEBUG, rach_ind->fn,
+ /* TODO: calculate and print Timing Advance Index */
+ "Access Burst for continuous Timing Advance control (toa256=%d)\n",
+ rach_ind->acc_delay_256bits);
+
+ /* QTA: Timing Advance in units of 1/4 of a symbol */
+ pcu_tx_rach_ind(trx->bts->nr, trx->nr, rach_ind->chan_nr & 0x07,
+ rach_ind->acc_delay_256bits >> 6,
+ rach_ind->ra, rach_ind->fn, rach_ind->is_11bit,
+ rach_ind->burst_type, PCU_IF_SAPI_PTCCH);
+ return 0;
+ } else { /* The MS may acknowledge DL data by 4 consequent Access Bursts */
+ LOGPFN(DL1P, LOGL_NOTICE, rach_ind->fn,
+ "Access Bursts on PDTCH/U are not (yet) supported\n");
+ return -ENOTSUP;
+ }
+}
+
/* RACH received from bts model */
static int l1sap_ph_rach_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct ph_rach_ind_param *rach_ind)
@@ -1309,10 +2212,16 @@ static int l1sap_ph_rach_ind(struct gsm_bts_trx *trx,
DEBUGPFN(DL1P, rach_ind->fn, "Rx PH-RA.ind\n");
- /* check for handover access burst on dedicated channels */
- if (!L1SAP_IS_CHAN_RACH(rach_ind->chan_nr)) {
- rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_HO);
- return l1sap_handover_rach(trx, l1sap, rach_ind);
+ /* Check the origin of an Access Burst */
+ switch (rach_ind->chan_nr & 0xf8) {
+ case RSL_CHAN_RACH:
+ /* CS or PS RACH, to be handled in this function */
+ break;
+ case RSL_CHAN_OSMO_PDCH:
+ /* TODO: do we need to count Access Bursts on PDCH? */
+ return l1sap_pdch_rach(trx, rach_ind);
+ default:
+ return l1sap_dcch_rach(trx, rach_ind);
}
rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_RCVD);
@@ -1322,7 +2231,7 @@ static int l1sap_ph_rach_ind(struct gsm_bts_trx *trx,
bts->load.rach.busy++;
/* Filter out noise / interference / ghosts */
- if (!rach_pass_filter(rach_ind, bts)) {
+ if (!rach_pass_filter(rach_ind, bts, "CCCH")) {
rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_DROP);
return 0;
}
@@ -1344,9 +2253,11 @@ static int l1sap_ph_rach_ind(struct gsm_bts_trx *trx,
LOGPFN(DL1P, LOGL_INFO, rach_ind->fn, "RACH for packet access (toa=%d, ra=%d)\n",
rach_ind->acc_delay, rach_ind->ra);
- pcu_tx_rach_ind(bts, rach_ind->acc_delay << 2,
- rach_ind->ra, rach_ind->fn,
- rach_ind->is_11bit, rach_ind->burst_type);
+ /* QTA: Timing Advance in units of 1/4 of a symbol */
+ pcu_tx_rach_ind(bts->nr, trx->nr, rach_ind->chan_nr & 0x07,
+ rach_ind->acc_delay_256bits >> 6,
+ rach_ind->ra, rach_ind->fn, rach_ind->is_11bit,
+ rach_ind->burst_type, PCU_IF_SAPI_RACH);
return 0;
}
@@ -1412,6 +2323,9 @@ int l1sap_up(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
/* any L1 prim sent to bts model */
static int l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
{
+ l1sap_log_ctx_sapi = get_common_sapi_by_trx_prim(trx, l1sap);
+ log_set_context(LOG_CTX_L1_SAPI, &l1sap_log_ctx_sapi);
+
if (OSMO_PRIM_HDR(&l1sap->oph) ==
OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST))
to_gsmtap(trx, l1sap);
@@ -1440,8 +2354,12 @@ int l1sap_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
l1sap->u.data.chan_nr = RSL_CHAN_OSMO_PDCH | ts->nr;
l1sap->u.data.link_id = 0x00;
l1sap->u.data.fn = fn;
- msg->l2h = msgb_put(msg, len);
- memcpy(msg->l2h, data, len);
+ if (len) {
+ msg->l2h = msgb_put(msg, len);
+ memcpy(msg->l2h, data, len);
+ } else {
+ msg->l2h = NULL; /* Idle block */
+ }
return l1sap_down(ts->trx, l1sap);
}
@@ -1452,19 +2370,59 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
uint32_t timestamp, bool marker)
{
struct gsm_lchan *lchan = rs->priv;
+ struct gsm_bts *bts = lchan->ts->trx->bts;
struct msgb *msg;
- struct osmo_phsap_prim *l1sap;
+ bool rfc5993_sid = false;
+
+ rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_TOTAL);
+ if (marker)
+ rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_MARKER);
/* if we're in loopback mode, we don't accept frames from the
* RTP socket anymore */
- if (lchan->loopback)
+ if (lchan->loopback) {
+ rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_DROP_LOOPBACK);
return;
+ }
- msg = l1sap_msgb_alloc(rtp_pl_len);
+ /* initial preen */
+ switch (rtp_payload_input_preen(lchan, rtp_pl, rtp_pl_len, &rfc5993_sid)) {
+ case PL_DECISION_DROP:
+ rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_DROP_PREEN);
+ return;
+ case PL_DECISION_ACCEPT:
+ break;
+ case PL_DECISION_STRIP_HDR_OCTET:
+ rtp_pl++;
+ rtp_pl_len--;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ msg = l1sap_msgb_alloc(512);
if (!msg)
return;
- memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len);
- msgb_pull(msg, sizeof(*l1sap));
+
+ if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA) {
+ int rc = csd_v110_rtp_decode(lchan, msg->tail,
+ rtp_pl, rtp_pl_len);
+ if (rc > 0) {
+ /* 'fake' tch_ind containing all-zero so gsmtap code can be shared
+ * between UL and DL */
+ static const struct ph_tch_param fake_tch_ind = {};
+ gsmtap_csd_rlp_process(lchan, false, &fake_tch_ind, msg->tail, rc);
+ msgb_put(msg, rc);
+ } else {
+ rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_DROP_V110_DEC);
+ msgb_free(msg);
+ return;
+ }
+ } else {
+ memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len);
+ }
+
+ msgb_pull(msg, sizeof(struct osmo_phsap_prim));
/* Store RTP header Marker bit in control buffer */
rtpmsg_marker_bit(msg) = marker;
@@ -1472,11 +2430,11 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
rtpmsg_seq(msg) = seq_number;
/* Store RTP header Timestamp in control buffer */
rtpmsg_ts(msg) = timestamp;
+ /* Store RFC 5993 SID flag likewise */
+ rtpmsg_is_rfc5993_sid(msg) = rfc5993_sid;
/* make sure the queue doesn't get too long */
- queue_limit_to(gsm_lchan_name(lchan), &lchan->dl_tch_queue, 1);
-
- msgb_enqueue(&lchan->dl_tch_queue, msg);
+ lchan_dl_tch_queue_enqueue(lchan, msg, 1);
}
static int l1sap_chan_act_dact_modify(struct gsm_bts_trx *trx, uint8_t chan_nr,
@@ -1494,53 +2452,50 @@ static int l1sap_chan_act_dact_modify(struct gsm_bts_trx *trx, uint8_t chan_nr,
return l1sap_down(trx, &l1sap);
}
-int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *tp)
+int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr)
{
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
- struct gsm48_chan_desc *cd;
int rc;
- LOGPLCHAN(lchan, DL1C, LOGL_INFO, "activating channel chan_nr=%s trx=%d\n",
- rsl_chan_nr_str(chan_nr), trx->nr);
-
- /* osmo-pcu calls this without a valid 'tp' parameter, so we
- * need to make sure ew don't crash here */
- if (tp && TLVP_PRESENT(tp, GSM48_IE_CHANDESC_2) &&
- TLVP_LEN(tp, GSM48_IE_CHANDESC_2) >= sizeof(*cd)) {
- cd = (struct gsm48_chan_desc *)
- TLVP_VAL(tp, GSM48_IE_CHANDESC_2);
-
- /* our L1 only supports one global TSC for all channels
- * one one TRX, so we need to make sure not to activate
- * channels with a different TSC!! */
- if (cd->h0.tsc != (lchan->ts->trx->bts->bsic & 7)) {
- LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "lchan TSC %u != BSIC-TSC %u\n",
- cd->h0.tsc, lchan->ts->trx->bts->bsic & 7);
- return -RSL_ERR_SERV_OPT_UNIMPL;
- }
+ if (lchan->state == LCHAN_S_ACTIVE) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "Trying to activate already active channel %s\n",
+ rsl_chan_nr_str(chan_nr));
+ return -1;
}
- lchan->sacch_deact = 0;
- lchan->s = lchan->ts->trx->bts->radio_link_timeout;
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Activating channel %s\n", rsl_chan_nr_str(chan_nr));
+
+ radio_link_timeout_reset(lchan);
rc = l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_ACTIVATE, 0);
if (rc)
return -RSL_ERR_EQUIPMENT_FAIL;
+ /* Is it TCH? If it is, attempt to allocate an Error Concealment Unit
+ * instance, if available, unless it is disabled by vty config. */
+ if (lchan_is_tch(lchan) && trx->bts->use_ul_ecu)
+ lchan->ecu_state = osmo_ecu_init(trx, lchan2ecu_codec(lchan));
+ else
+ lchan->ecu_state = NULL;
+
/* Init DTX DL FSM if necessary */
- if (trx->bts->dtxd && lchan->type != GSM_LCHAN_SDCCH) {
- char name[32];
- snprintf(name, sizeof(name), "bts%u-trx%u-ts%u-ss%u", lchan->ts->trx->bts->nr,
- lchan->ts->trx->nr, lchan->ts->nr, lchan->nr);
+ if (trx->bts->dtxd && lchan_is_tch(lchan)) {
lchan->tch.dtx.dl_amr_fsm = osmo_fsm_inst_alloc(&dtx_dl_amr_fsm,
tall_bts_ctx,
lchan,
LOGL_DEBUG,
- name);
+ NULL);
if (!lchan->tch.dtx.dl_amr_fsm) {
l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE, 0);
return -RSL_ERR_EQUIPMENT_FAIL;
}
+
+ rc = osmo_fsm_inst_update_id_f(lchan->tch.dtx.dl_amr_fsm,
+ "bts%u-trx%u-ts%u-ss%u%s",
+ trx->bts->nr, trx->nr,
+ lchan->ts->nr, lchan->nr,
+ lchan->ts->vamos.is_shadow ? "-shadow" : "");
+ OSMO_ASSERT(rc == 0);
}
return 0;
}
@@ -1548,14 +2503,31 @@ int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *
int l1sap_chan_rel(struct gsm_bts_trx *trx, uint8_t chan_nr)
{
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
- LOGPLCHAN(lchan, DL1C, LOGL_INFO, "deactivating channel chan_nr=%s trx=%d\n",
- rsl_chan_nr_str(chan_nr), trx->nr);
+
+ if (lchan->state == LCHAN_S_NONE) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "Trying to deactivate already deactivated channel %s\n",
+ rsl_chan_nr_str(chan_nr));
+ return -1;
+ }
+
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Deactivating channel %s\n",
+ rsl_chan_nr_str(chan_nr));
if (lchan->tch.dtx.dl_amr_fsm) {
osmo_fsm_inst_free(lchan->tch.dtx.dl_amr_fsm);
lchan->tch.dtx.dl_amr_fsm = NULL;
}
+ /* clear ECU state (if any) */
+ if (lchan->ecu_state) {
+ osmo_ecu_destroy(lchan->ecu_state);
+ lchan->ecu_state = NULL;
+ }
+
+ /* reset CSD RLP buffers to avoid any user plane data leaking from
+ * one previous lchan into a later one */
+ memset(&lchan->tch.csd, 0, sizeof(lchan->tch.csd));
+
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE,
0);
}
@@ -1564,10 +2536,8 @@ int l1sap_chan_deact_sacch(struct gsm_bts_trx *trx, uint8_t chan_nr)
{
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
- LOGPLCHAN(lchan, DL1C, LOGL_INFO, "deactivating sacch chan_nr=%s trx=%d\n",
- rsl_chan_nr_str(chan_nr), trx->nr);
-
- lchan->sacch_deact = 1;
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Deactivating SACCH on channel %s\n",
+ rsl_chan_nr_str(chan_nr));
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE,
1);
@@ -1575,8 +2545,46 @@ int l1sap_chan_deact_sacch(struct gsm_bts_trx *trx, uint8_t chan_nr)
int l1sap_chan_modify(struct gsm_bts_trx *trx, uint8_t chan_nr)
{
- LOGP(DL1C, LOGL_INFO, "modifying channel chan_nr=%s trx=%d\n",
- rsl_chan_nr_str(chan_nr), trx->nr);
+ struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
+
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Modifying channel %s\n",
+ rsl_chan_nr_str(chan_nr));
+
+ /* Is it TCH? If it is and we are applying internal uplink ECUs,
+ * the new channel mode calls for a different ECU. Any changes
+ * in vty config (enabling or disabling this ECU application)
+ * will also take effect upon channel modification. */
+ if (lchan_is_tch(lchan)) {
+ if (lchan->ecu_state)
+ osmo_ecu_destroy(lchan->ecu_state);
+ if (trx->bts->use_ul_ecu)
+ lchan->ecu_state = osmo_ecu_init(trx, lchan2ecu_codec(lchan));
+ else
+ lchan->ecu_state = NULL;
+ }
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_MODIFY, 0);
}
+
+int l1sap_uplink_access(struct gsm_lchan *lchan, bool active)
+{
+ uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ struct osmo_phsap_prim l1sap;
+
+ if (lchan->state != LCHAN_S_ACTIVE && !rsl_chan_rt_is_asci(lchan->rsl_chan_rt)) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "Channel %s is not an active ASCI type channel.\n",
+ rsl_chan_nr_str(chan_nr));
+ return -EINVAL;
+ }
+
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "%s uplink access detection on channel %s\n",
+ (active) ? "Activating" : "Deactivating", rsl_chan_nr_str(chan_nr));
+
+
+ memset(&l1sap, 0, sizeof(l1sap));
+ osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_REQUEST, NULL);
+ l1sap.u.info.type = (active) ? PRIM_INFO_ACT_UL_ACC : PRIM_INFO_DEACT_UL_ACC;
+ l1sap.u.info.u.ulacc_req.chan_nr = chan_nr;
+
+ return l1sap_down(lchan->ts->trx, &l1sap);
+}