2013-07-29 07:42:23 +00:00
|
|
|
/* L1 SAP primitives */
|
|
|
|
|
|
|
|
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
|
|
|
* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
|
|
|
|
*
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2016-09-17 12:15:03 +00:00
|
|
|
#include <stdbool.h>
|
2013-07-29 07:42:23 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
2017-06-29 15:53:49 +00:00
|
|
|
#include <inttypes.h>
|
2013-07-29 07:42:23 +00:00
|
|
|
|
|
|
|
#include <osmocom/core/msgb.h>
|
|
|
|
#include <osmocom/gsm/l1sap.h>
|
2017-07-03 08:56:59 +00:00
|
|
|
#include <osmocom/gsm/gsm_utils.h>
|
2013-07-29 07:42:23 +00:00
|
|
|
#include <osmocom/core/gsmtap.h>
|
|
|
|
#include <osmocom/core/gsmtap_util.h>
|
|
|
|
#include <osmocom/core/utils.h>
|
|
|
|
|
|
|
|
#include <osmocom/trau/osmo_ortp.h>
|
|
|
|
|
|
|
|
#include <osmo-bts/logging.h>
|
|
|
|
#include <osmo-bts/gsm_data.h>
|
|
|
|
#include <osmo-bts/l1sap.h>
|
2016-10-03 15:37:45 +00:00
|
|
|
#include <osmo-bts/dtx_dl_amr_fsm.h>
|
2013-07-29 07:42:23 +00:00
|
|
|
#include <osmo-bts/pcu_if.h>
|
|
|
|
#include <osmo-bts/measurement.h>
|
|
|
|
#include <osmo-bts/bts.h>
|
|
|
|
#include <osmo-bts/rsl.h>
|
2017-01-02 20:42:56 +00:00
|
|
|
#include <osmo-bts/oml.h>
|
2017-06-22 11:07:33 +00:00
|
|
|
#include <osmo-bts/abis.h>
|
2013-07-29 07:42:23 +00:00
|
|
|
#include <osmo-bts/bts_model.h>
|
2014-08-27 15:13:20 +00:00
|
|
|
#include <osmo-bts/handover.h>
|
2016-01-04 19:29:24 +00:00
|
|
|
#include <osmo-bts/power_control.h>
|
2016-09-17 12:15:03 +00:00
|
|
|
#include <osmo-bts/msg_utils.h>
|
2017-08-31 11:52:10 +00:00
|
|
|
#include <osmo-bts/pcuif_proto.h>
|
2013-07-29 07:42:23 +00:00
|
|
|
|
2016-06-23 13:39:31 +00:00
|
|
|
struct gsm_lchan *get_lchan_by_chan_nr(struct gsm_bts_trx *trx,
|
|
|
|
unsigned int chan_nr)
|
2015-12-05 10:54:08 +00:00
|
|
|
{
|
|
|
|
return &trx->ts[L1SAP_CHAN2TS(chan_nr)].lchan[l1sap_chan2ss(chan_nr)];
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct gsm_lchan *
|
|
|
|
get_active_lchan_by_chan_nr(struct gsm_bts_trx *trx, unsigned int chan_nr)
|
|
|
|
{
|
|
|
|
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
|
|
|
|
|
2016-01-15 14:21:32 +00:00
|
|
|
if (lchan && lchan->state != LCHAN_S_ACTIVE) {
|
|
|
|
LOGP(DL1P, LOGL_NOTICE, "%s: assuming active lchan, but "
|
|
|
|
"state is %s\n", gsm_lchan_name(lchan),
|
|
|
|
gsm_lchans_name(lchan->state));
|
2015-12-05 10:54:08 +00:00
|
|
|
return NULL;
|
2016-01-15 14:21:32 +00:00
|
|
|
}
|
2015-12-05 10:54:08 +00:00
|
|
|
return lchan;
|
|
|
|
}
|
|
|
|
|
2013-07-29 07:42:23 +00:00
|
|
|
static int l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap);
|
|
|
|
|
2016-12-09 13:13:22 +00:00
|
|
|
static uint32_t fn_ms_adj(uint32_t fn, const struct gsm_lchan *lchan)
|
2016-06-03 11:29:29 +00:00
|
|
|
{
|
2016-12-09 13:13:22 +00:00
|
|
|
uint32_t samples_passed, r;
|
|
|
|
|
|
|
|
if (lchan->tch.last_fn != LCHAN_FN_DUMMY) {
|
2016-10-11 16:13:32 +00:00
|
|
|
/* 12/13 frames usable for audio in TCH,
|
|
|
|
160 samples per RTP packet,
|
|
|
|
1 RTP packet per 4 frames */
|
2016-12-09 13:13:22 +00:00
|
|
|
samples_passed = (fn - lchan->tch.last_fn) * 12 * 160 / (13 * 4);
|
2016-06-03 11:29:29 +00:00
|
|
|
/* round number of samples to the nearest multiple of
|
|
|
|
GSM_RTP_DURATION */
|
2016-12-09 13:13:22 +00:00
|
|
|
r = samples_passed + GSM_RTP_DURATION / 2;
|
2016-06-03 11:29:29 +00:00
|
|
|
r -= r % GSM_RTP_DURATION;
|
2017-06-29 15:53:49 +00:00
|
|
|
|
|
|
|
if (r != GSM_RTP_DURATION)
|
2017-12-02 16:31:45 +00:00
|
|
|
LOGP(DRTP, LOGL_ERROR, "RTP clock out of sync with lower layer:"
|
2017-06-29 15:53:49 +00:00
|
|
|
" %"PRIu32" vs %d (%"PRIu32"->%"PRIu32")\n",
|
|
|
|
r, GSM_RTP_DURATION, lchan->tch.last_fn, fn);
|
2016-06-03 11:29:29 +00:00
|
|
|
}
|
|
|
|
return GSM_RTP_DURATION;
|
|
|
|
}
|
|
|
|
|
2017-06-23 19:08:09 +00:00
|
|
|
/*! 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)
|
|
|
|
{
|
|
|
|
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--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-29 07:42:23 +00:00
|
|
|
/* 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 front and behind data pointer */
|
|
|
|
struct msgb *l1sap_msgb_alloc(unsigned int l2_len)
|
|
|
|
{
|
2017-11-08 15:38:53 +00:00
|
|
|
int headroom = 128;
|
|
|
|
int size = headroom + sizeof(struct osmo_phsap_prim) + l2_len;
|
|
|
|
struct msgb *msg = msgb_alloc_headroom(size, headroom, "l1sap_prim");
|
2013-07-29 07:42:23 +00:00
|
|
|
|
|
|
|
if (!msg)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
msg->l1h = msgb_put(msg, sizeof(struct osmo_phsap_prim));
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2016-06-17 11:10:38 +00:00
|
|
|
int add_l1sap_header(struct gsm_bts_trx *trx, struct msgb *rmsg,
|
2017-06-30 13:19:00 +00:00
|
|
|
struct gsm_lchan *lchan, uint8_t chan_nr, uint32_t fn,
|
|
|
|
uint16_t ber10k, int16_t lqual_cb)
|
2016-06-17 11:10:38 +00:00
|
|
|
{
|
|
|
|
struct osmo_phsap_prim *l1sap;
|
|
|
|
|
2017-06-30 13:19:00 +00:00
|
|
|
LOGP(DL1P, LOGL_DEBUG, "%s Rx -> RTP: %s\n",
|
2016-06-17 11:10:38 +00:00
|
|
|
gsm_lchan_name(lchan), osmo_hexdump(rmsg->data, rmsg->len));
|
|
|
|
|
|
|
|
rmsg->l2h = rmsg->data;
|
|
|
|
msgb_push(rmsg, sizeof(*l1sap));
|
|
|
|
rmsg->l1h = rmsg->data;
|
|
|
|
l1sap = msgb_l1sap_prim(rmsg);
|
|
|
|
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_INDICATION,
|
|
|
|
rmsg);
|
|
|
|
l1sap->u.tch.chan_nr = chan_nr;
|
|
|
|
l1sap->u.tch.fn = fn;
|
2017-06-30 13:19:00 +00:00
|
|
|
l1sap->u.tch.ber10k = ber10k;
|
|
|
|
l1sap->u.tch.lqual_cb = lqual_cb;
|
2016-06-17 11:10:38 +00:00
|
|
|
|
|
|
|
return l1sap_up(trx, l1sap);
|
|
|
|
}
|
|
|
|
|
2013-09-01 09:09:20 +00:00
|
|
|
static int l1sap_tx_ciph_req(struct gsm_bts_trx *trx, uint8_t chan_nr,
|
|
|
|
uint8_t downlink, uint8_t uplink)
|
|
|
|
{
|
|
|
|
struct osmo_phsap_prim l1sap_ciph;
|
|
|
|
|
|
|
|
osmo_prim_init(&l1sap_ciph.oph, SAP_GSM_PH, PRIM_MPH_INFO,
|
|
|
|
PRIM_OP_REQUEST, NULL);
|
|
|
|
l1sap_ciph.u.info.type = PRIM_INFO_ACT_CIPH;
|
|
|
|
l1sap_ciph.u.info.u.ciph_req.chan_nr = chan_nr;
|
|
|
|
l1sap_ciph.u.info.u.ciph_req.downlink = downlink;
|
|
|
|
l1sap_ciph.u.info.u.ciph_req.uplink = uplink;
|
|
|
|
|
|
|
|
return l1sap_down(trx, &l1sap_ciph);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* check if the message is a GSM48_MT_RR_CIPH_M_CMD, and if yes, enable
|
|
|
|
* uni-directional de-cryption on the uplink. We need this ugly layering
|
|
|
|
* violation as we have no way of passing down L3 metadata (RSL CIPHERING CMD)
|
|
|
|
* to this point in L1 */
|
|
|
|
static int check_for_ciph_cmd(struct msgb *msg, struct gsm_lchan *lchan,
|
|
|
|
uint8_t chan_nr)
|
|
|
|
{
|
2016-01-25 14:43:03 +00:00
|
|
|
uint8_t n_s;
|
2013-09-01 09:09:20 +00:00
|
|
|
|
|
|
|
/* only do this if we are in the right state */
|
|
|
|
switch (lchan->ciph_state) {
|
|
|
|
case LCHAN_CIPH_NONE:
|
2013-07-02 09:04:11 +00:00
|
|
|
case LCHAN_CIPH_RX_REQ:
|
2013-09-01 09:09:20 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* First byte (Address Field) of LAPDm header) */
|
|
|
|
if (msg->data[0] != 0x03)
|
|
|
|
return 0;
|
|
|
|
/* First byte (protocol discriminator) of RR */
|
|
|
|
if ((msg->data[3] & 0xF) != GSM48_PDISC_RR)
|
|
|
|
return 0;
|
|
|
|
/* 2nd byte (msg type) of RR */
|
|
|
|
if ((msg->data[4] & 0x3F) != GSM48_MT_RR_CIPH_M_CMD)
|
|
|
|
return 0;
|
|
|
|
|
2016-01-25 14:43:03 +00:00
|
|
|
/* Remember N(S) + 1 to find the first ciphered frame */
|
|
|
|
n_s = (msg->data[1] >> 1) & 0x7;
|
|
|
|
lchan->ciph_ns = (n_s + 1) % 8;
|
|
|
|
|
2013-09-01 09:09:20 +00:00
|
|
|
l1sap_tx_ciph_req(lchan->ts->trx, chan_nr, 0, 1);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-01-25 14:43:03 +00:00
|
|
|
/* public helpers for the test */
|
|
|
|
int bts_check_for_ciph_cmd(struct msgb *msg, struct gsm_lchan *lchan,
|
|
|
|
uint8_t chan_nr)
|
|
|
|
{
|
|
|
|
return check_for_ciph_cmd(msg, lchan, chan_nr);
|
|
|
|
}
|
|
|
|
|
2013-07-29 07:45:22 +00:00
|
|
|
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" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* send primitive as gsmtap */
|
|
|
|
static int gsmtap_ph_data(struct osmo_phsap_prim *l1sap, uint8_t *chan_type,
|
2016-10-12 14:24:32 +00:00
|
|
|
uint8_t *ss, uint32_t fn, uint8_t **data, int *len,
|
|
|
|
uint8_t num_agch)
|
2013-07-29 07:45:22 +00:00
|
|
|
{
|
|
|
|
struct msgb *msg = l1sap->oph.msg;
|
|
|
|
uint8_t chan_nr, link_id;
|
|
|
|
|
|
|
|
*data = msg->data + sizeof(struct osmo_phsap_prim);
|
|
|
|
*len = msg->len - sizeof(struct osmo_phsap_prim);
|
|
|
|
chan_nr = l1sap->u.data.chan_nr;
|
|
|
|
link_id = l1sap->u.data.link_id;
|
|
|
|
|
|
|
|
if (L1SAP_IS_CHAN_TCHF(chan_nr)) {
|
|
|
|
*chan_type = GSMTAP_CHANNEL_TCH_F;
|
|
|
|
} else if (L1SAP_IS_CHAN_TCHH(chan_nr)) {
|
|
|
|
*ss = L1SAP_CHAN2SS_TCHH(chan_nr);
|
|
|
|
*chan_type = GSMTAP_CHANNEL_TCH_H;
|
|
|
|
} else if (L1SAP_IS_CHAN_SDCCH4(chan_nr)) {
|
|
|
|
*ss = L1SAP_CHAN2SS_SDCCH4(chan_nr);
|
|
|
|
*chan_type = GSMTAP_CHANNEL_SDCCH;
|
|
|
|
} else if (L1SAP_IS_CHAN_SDCCH8(chan_nr)) {
|
|
|
|
*ss = L1SAP_CHAN2SS_SDCCH8(chan_nr);
|
|
|
|
*chan_type = GSMTAP_CHANNEL_SDCCH;
|
|
|
|
} else if (L1SAP_IS_CHAN_BCCH(chan_nr)) {
|
|
|
|
*chan_type = GSMTAP_CHANNEL_BCCH;
|
|
|
|
} else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) {
|
|
|
|
/* The sapi depends on DSP configuration, not
|
|
|
|
* on the actual SYSTEM INFORMATION 3. */
|
2016-10-12 14:24:32 +00:00
|
|
|
if (L1SAP_FN2CCCHBLOCK(fn) >= num_agch)
|
2013-07-29 07:45:22 +00:00
|
|
|
*chan_type = GSMTAP_CHANNEL_PCH;
|
|
|
|
else
|
|
|
|
*chan_type = GSMTAP_CHANNEL_AGCH;
|
2017-07-30 14:55:00 +00:00
|
|
|
} else if (L1SAP_IS_CHAN_PDCH(chan_nr)) {
|
|
|
|
*chan_type = GSMTAP_CHANNEL_PDTCH;
|
2013-07-29 07:45:22 +00:00
|
|
|
}
|
|
|
|
if (L1SAP_IS_LINK_SACCH(link_id))
|
|
|
|
*chan_type |= GSMTAP_CHANNEL_ACCH;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gsmtap_pdch(struct osmo_phsap_prim *l1sap, uint8_t *chan_type,
|
2016-11-11 11:22:11 +00:00
|
|
|
uint8_t *ss, uint32_t fn, uint8_t **data, int *len)
|
2013-07-29 07:45:22 +00:00
|
|
|
{
|
|
|
|
struct msgb *msg = l1sap->oph.msg;
|
|
|
|
|
|
|
|
*data = msg->data + sizeof(struct osmo_phsap_prim);
|
|
|
|
*len = msg->len - sizeof(struct osmo_phsap_prim);
|
|
|
|
|
2016-11-11 11:22:11 +00:00
|
|
|
if (L1SAP_IS_PTCCH(fn)) {
|
2013-07-29 07:45:22 +00:00
|
|
|
*chan_type = GSMTAP_CHANNEL_PTCCH;
|
2016-11-11 11:22:11 +00:00
|
|
|
*ss = L1SAP_FN2PTCCHBLOCK(fn);
|
2013-07-29 07:45:22 +00:00
|
|
|
if (l1sap->oph.primitive
|
|
|
|
== PRIM_OP_INDICATION) {
|
|
|
|
if ((*data[0]) == 7)
|
|
|
|
return -EINVAL;
|
|
|
|
(*data)++;
|
|
|
|
(*len)--;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
*chan_type = GSMTAP_CHANNEL_PACCH;
|
|
|
|
|
|
|
|
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, int *len)
|
|
|
|
{
|
|
|
|
uint8_t chan_nr;
|
|
|
|
|
|
|
|
*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;
|
|
|
|
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);
|
2016-08-17 10:57:31 +00:00
|
|
|
*data = (uint8_t *)&l1sap->u.rach_ind.ra;
|
2013-07-29 07:45:22 +00:00
|
|
|
*len = 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-28 12:38:28 +00:00
|
|
|
/* Paging Request 1 with "no identity" content, i.e. empty/dummy paging */
|
|
|
|
static const uint8_t paging_fill[GSM_MACBLOCK_LEN] = {
|
|
|
|
0x15, 0x06, 0x21, 0x00, 0x01, 0xf0, 0x2b, 0x2b, 0x2b, 0x2b,
|
|
|
|
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
|
|
|
|
0x2b, 0x2b, 0x2b };
|
|
|
|
|
|
|
|
static bool is_fill_frame(uint8_t chan_type, const uint8_t *data, unsigned int len)
|
|
|
|
{
|
|
|
|
switch (chan_type) {
|
|
|
|
case GSMTAP_CHANNEL_AGCH:
|
|
|
|
if (!memcmp(data, fill_frame, GSM_MACBLOCK_LEN))
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case GSMTAP_CHANNEL_PCH:
|
|
|
|
if (!memcmp(data, paging_fill, GSM_MACBLOCK_LEN))
|
|
|
|
return true;
|
|
|
|
break;
|
2017-11-05 18:46:24 +00:00
|
|
|
/* don't use 'default' case here as the above only conditionally return true */
|
2017-07-28 12:38:28 +00:00
|
|
|
}
|
2017-11-05 18:46:24 +00:00
|
|
|
return false;
|
2017-07-28 12:38:28 +00:00
|
|
|
}
|
|
|
|
|
2013-07-29 07:45:22 +00:00
|
|
|
static int to_gsmtap(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
|
|
|
|
{
|
|
|
|
uint8_t *data;
|
|
|
|
int len;
|
|
|
|
uint8_t chan_type = 0, tn = 0, ss = 0;
|
|
|
|
uint32_t fn;
|
|
|
|
uint16_t uplink = GSMTAP_ARFCN_F_UPLINK;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (!gsmtap)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (OSMO_PRIM_HDR(&l1sap->oph)) {
|
|
|
|
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST):
|
|
|
|
uplink = 0;
|
|
|
|
/* fall through */
|
|
|
|
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_INDICATION):
|
2016-11-11 11:22:11 +00:00
|
|
|
fn = l1sap->u.data.fn;
|
|
|
|
tn = L1SAP_CHAN2TS(l1sap->u.data.chan_nr);
|
2016-07-27 13:52:55 +00:00
|
|
|
if (ts_is_pdch(&trx->ts[tn]))
|
2016-11-11 11:22:11 +00:00
|
|
|
rc = gsmtap_pdch(l1sap, &chan_type, &ss, fn, &data,
|
|
|
|
&len);
|
2013-07-29 07:45:22 +00:00
|
|
|
else
|
2016-11-11 11:22:11 +00:00
|
|
|
rc = gsmtap_ph_data(l1sap, &chan_type, &ss, fn, &data,
|
2016-10-12 14:24:32 +00:00
|
|
|
&len, num_agch(trx, "GSMTAP"));
|
2013-07-29 07:45:22 +00:00
|
|
|
break;
|
|
|
|
case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_INDICATION):
|
|
|
|
rc = gsmtap_ph_rach(l1sap, &chan_type, &tn, &ss, &fn, &data,
|
|
|
|
&len);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
rc = -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (len == 0)
|
|
|
|
return 0;
|
|
|
|
if ((chan_type & GSMTAP_CHANNEL_ACCH)) {
|
|
|
|
if (!gsmtap_sapi_acch)
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
if (!((1 << (chan_type & 31)) & gsmtap_sapi_mask))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-28 12:38:28 +00:00
|
|
|
/* don't log fill frames via GSMTAP; they serve no purpose other than
|
|
|
|
* to clog up your logs */
|
|
|
|
if (is_fill_frame(chan_type, data, len))
|
|
|
|
return 0;
|
|
|
|
|
2013-07-29 07:45:22 +00:00
|
|
|
gsmtap_send(gsmtap, trx->arfcn | uplink, tn, chan_type, ss, fn, 0, 0,
|
|
|
|
data, len);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-11 15:01:14 +00:00
|
|
|
/* Calculate the number of RACH slots that expire in a certain GSM frame
|
|
|
|
* See also 3GPP TS 05.02 Clause 7 Table 5 of 9 */
|
|
|
|
static unsigned int calc_exprd_rach_frames(struct gsm_bts *bts, uint32_t fn)
|
|
|
|
{
|
|
|
|
int rach_frames_expired = 0;
|
|
|
|
uint8_t ccch_conf;
|
|
|
|
struct gsm48_system_information_type_3 *si3;
|
|
|
|
unsigned int blockno;
|
|
|
|
|
|
|
|
si3 = GSM_BTS_SI(bts, SYSINFO_TYPE_3);
|
|
|
|
ccch_conf = si3->control_channel_desc.ccch_conf;
|
|
|
|
|
|
|
|
if (ccch_conf == RSL_BCCH_CCCH_CONF_1_C) {
|
|
|
|
/* It is possible to combine a CCCH with an SDCCH4, in this
|
|
|
|
* case the CCCH will have to share the available frames with
|
|
|
|
* the other channel, this results in a limited number of
|
|
|
|
* available rach slots */
|
|
|
|
blockno = fn % 51;
|
|
|
|
if (blockno == 4 || blockno == 5
|
|
|
|
|| (blockno >= 15 && blockno <= 36) || blockno == 45
|
|
|
|
|| blockno == 46)
|
|
|
|
rach_frames_expired = 1;
|
|
|
|
} else {
|
|
|
|
/* It is possible to have multiple CCCH channels on
|
|
|
|
* different physical channels (large cells), this
|
|
|
|
* also multiplies the available/expired RACH channels.
|
|
|
|
* See also TS 04.08, Chapter 10.5.2.11, table 10.29 */
|
|
|
|
if (ccch_conf == RSL_BCCH_CCCH_CONF_2_NC)
|
|
|
|
rach_frames_expired = 2;
|
2017-01-16 17:36:24 +00:00
|
|
|
else if (ccch_conf == RSL_BCCH_CCCH_CONF_3_NC)
|
2017-01-11 15:01:14 +00:00
|
|
|
rach_frames_expired = 3;
|
2017-01-16 17:36:24 +00:00
|
|
|
else if (ccch_conf == RSL_BCCH_CCCH_CONF_4_NC)
|
2017-01-11 15:01:14 +00:00
|
|
|
rach_frames_expired = 4;
|
|
|
|
else
|
|
|
|
rach_frames_expired = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Each Frame has room for 4 RACH slots, since RACH
|
|
|
|
* slots are short enough to fit into a single radio
|
|
|
|
* burst, so we need to multiply the final result by 4 */
|
|
|
|
return rach_frames_expired * 4;
|
|
|
|
}
|
|
|
|
|
2013-08-31 17:49:12 +00:00
|
|
|
/* time information received from bts model */
|
2013-07-08 16:34:14 +00:00
|
|
|
static int l1sap_info_time_ind(struct gsm_bts *bts,
|
2017-01-11 15:01:14 +00:00
|
|
|
struct osmo_phsap_prim *l1sap,
|
|
|
|
struct info_time_ind_param *info_time_ind)
|
2013-08-31 17:49:12 +00:00
|
|
|
{
|
|
|
|
struct gsm_bts_role_bts *btsb = bts->role;
|
2017-01-11 15:01:14 +00:00
|
|
|
int frames_expired;
|
2014-08-27 17:57:51 +00:00
|
|
|
|
2018-02-22 10:44:20 +00:00
|
|
|
DEBUGPFN(DL1P, info_time_ind->fn, "Rx MPH_INFO time ind\n");
|
2013-08-31 17:49:12 +00:00
|
|
|
|
2017-01-11 15:01:14 +00:00
|
|
|
/* Calculate and check frame difference */
|
|
|
|
frames_expired = info_time_ind->fn - btsb->gsm_time.fn;
|
|
|
|
if (frames_expired > 1) {
|
2018-02-08 15:50:20 +00:00
|
|
|
if (btsb->gsm_time.fn)
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPFN(DL1P, LOGL_ERROR, info_time_ind->fn,
|
2018-02-08 15:50:20 +00:00
|
|
|
"Invalid condition detected: Frame difference is %"PRIu32"-%"PRIu32"=%d > 1!\n",
|
|
|
|
info_time_ind->fn, btsb->gsm_time.fn, frames_expired);
|
2017-01-11 15:01:14 +00:00
|
|
|
}
|
|
|
|
|
2013-08-31 17:49:12 +00:00
|
|
|
/* Update our data structures with the current GSM time */
|
|
|
|
gsm_fn2gsmtime(&btsb->gsm_time, info_time_ind->fn);
|
|
|
|
|
|
|
|
/* Update time on PCU interface */
|
|
|
|
pcu_tx_time_ind(info_time_ind->fn);
|
|
|
|
|
2014-08-27 17:57:51 +00:00
|
|
|
/* increment number of RACH slots that have passed by since the
|
|
|
|
* last time indication */
|
2017-01-11 15:01:14 +00:00
|
|
|
btsb->load.rach.total +=
|
|
|
|
calc_exprd_rach_frames(bts, info_time_ind->fn) * frames_expired;
|
2013-08-31 17:49:12 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2017-03-07 16:30:35 +00:00
|
|
|
|
|
|
|
static inline void set_ms_to_data(struct gsm_lchan *lchan, int16_t data, bool set_ms_to)
|
|
|
|
{
|
|
|
|
if (!lchan)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (data + 63 > 255) { /* According to 3GPP TS 48.058 §9.3.37 Timing Offset field cannot exceed 255 */
|
|
|
|
LOGP(DL1P, LOGL_ERROR, "Attempting to set invalid Timing Offset value %d (MS TO = %u)!\n",
|
|
|
|
data, set_ms_to);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (set_ms_to) {
|
|
|
|
lchan->ms_t_offs = data + 63;
|
|
|
|
lchan->p_offs = -1;
|
|
|
|
} else {
|
|
|
|
lchan->p_offs = data + 63;
|
|
|
|
lchan->ms_t_offs = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-31 18:30:40 +00:00
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
struct bts_ul_meas ulm;
|
|
|
|
struct gsm_lchan *lchan;
|
|
|
|
|
2015-12-05 10:54:08 +00:00
|
|
|
lchan = get_active_lchan_by_chan_nr(trx, info_meas_ind->chan_nr);
|
2017-06-23 16:57:28 +00:00
|
|
|
if (!lchan) {
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPFN(DL1P, LOGL_ERROR, info_meas_ind->fn,
|
|
|
|
"No lchan for MPH INFO MEAS IND (chan_nr=%u)\n", info_meas_ind->chan_nr);
|
2015-12-05 10:54:08 +00:00
|
|
|
return 0;
|
2017-06-23 16:57:28 +00:00
|
|
|
}
|
|
|
|
|
2018-02-22 10:44:20 +00:00
|
|
|
DEBUGPFN(DL1P, info_meas_ind->fn,
|
2018-02-27 15:58:46 +00:00
|
|
|
"%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,
|
2017-06-23 16:57:28 +00:00
|
|
|
info_meas_ind->ber10k, info_meas_ind->inv_rssi);
|
2013-08-31 18:30:40 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
memset(&ulm, 0, sizeof(ulm));
|
2018-02-27 15:58:46 +00:00
|
|
|
ulm.ta_offs_256bits = info_meas_ind->ta_offs_256bits;
|
2013-08-31 18:30:40 +00:00
|
|
|
ulm.ber10k = info_meas_ind->ber10k;
|
|
|
|
ulm.inv_rssi = info_meas_ind->inv_rssi;
|
2018-02-23 12:53:33 +00:00
|
|
|
ulm.is_sub = info_meas_ind->is_sub;
|
2013-08-31 18:30:40 +00:00
|
|
|
|
2017-03-07 16:30:35 +00:00
|
|
|
/* we assume that symbol period is 1 bit: */
|
2018-02-27 15:58:46 +00:00
|
|
|
set_ms_to_data(lchan, info_meas_ind->ta_offs_256bits / 256, true);
|
2017-03-07 16:30:35 +00:00
|
|
|
|
2018-02-23 12:52:44 +00:00
|
|
|
lchan_new_ul_meas(lchan, &ulm, info_meas_ind->fn);
|
2013-08-31 18:30:40 +00:00
|
|
|
|
2017-04-19 15:09:50 +00:00
|
|
|
/* Check measurement period end and prepare the UL measurment
|
|
|
|
* report at Meas period End*/
|
|
|
|
lchan_meas_check_compute(lchan, info_meas_ind->fn);
|
|
|
|
|
2013-08-31 18:30:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-08-31 17:49:12 +00:00
|
|
|
/* any L1 MPH_INFO indication prim recevied from bts model */
|
|
|
|
static int l1sap_mph_info_ind(struct gsm_bts_trx *trx,
|
|
|
|
struct osmo_phsap_prim *l1sap, struct mph_info_param *info)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
switch (info->type) {
|
|
|
|
case PRIM_INFO_TIME:
|
2016-01-20 18:02:51 +00:00
|
|
|
if (trx != trx->bts->c0) {
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPFN(DL1P, LOGL_NOTICE, info->u.time_ind.fn,
|
|
|
|
"BTS model is sending us PRIM_INFO_TIME for TRX %u, please fix it\n",
|
|
|
|
trx->nr);
|
2016-01-20 18:02:51 +00:00
|
|
|
rc = -1;
|
|
|
|
} else
|
|
|
|
rc = l1sap_info_time_ind(trx->bts, l1sap,
|
|
|
|
&info->u.time_ind);
|
2013-08-31 17:49:12 +00:00
|
|
|
break;
|
2013-08-31 18:30:40 +00:00
|
|
|
case PRIM_INFO_MEAS:
|
|
|
|
rc = l1sap_info_meas_ind(trx, l1sap, &info->u.meas_ind);
|
|
|
|
break;
|
2013-08-31 17:49:12 +00:00
|
|
|
default:
|
|
|
|
LOGP(DL1P, LOGL_NOTICE, "unknown MPH_INFO ind type %d\n",
|
|
|
|
info->type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2013-09-01 07:19:45 +00:00
|
|
|
/* activation confirm received from bts model */
|
|
|
|
static int l1sap_info_act_cnf(struct gsm_bts_trx *trx,
|
|
|
|
struct osmo_phsap_prim *l1sap,
|
|
|
|
struct info_act_cnf_param *info_act_cnf)
|
|
|
|
{
|
|
|
|
struct gsm_lchan *lchan;
|
|
|
|
|
2017-12-02 16:31:45 +00:00
|
|
|
LOGP(DL1C, LOGL_INFO, "activate confirm chan_nr=0x%02x trx=%d\n",
|
2013-09-01 07:19:45 +00:00
|
|
|
info_act_cnf->chan_nr, trx->nr);
|
|
|
|
|
2015-12-05 10:54:08 +00:00
|
|
|
lchan = get_lchan_by_chan_nr(trx, info_act_cnf->chan_nr);
|
2013-09-01 07:19:45 +00:00
|
|
|
|
2016-08-22 16:23:03 +00:00
|
|
|
rsl_tx_chan_act_acknack(lchan, info_act_cnf->cause);
|
2013-09-01 07:19:45 +00:00
|
|
|
|
2016-06-16 15:32:32 +00:00
|
|
|
/* During PDCH ACT, this is where we know that the PCU is done
|
|
|
|
* activating a PDCH, and PDCH switchover is complete. See
|
|
|
|
* rsl_rx_dyn_pdch() */
|
2016-07-16 20:29:28 +00:00
|
|
|
if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH
|
|
|
|
&& (lchan->ts->flags & TS_F_PDCH_ACT_PENDING))
|
|
|
|
ipacc_dyn_pdch_complete(lchan->ts,
|
|
|
|
info_act_cnf->cause? -EIO : 0);
|
2016-06-16 15:32:32 +00:00
|
|
|
|
2013-09-01 07:19:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* activation confirm received from bts model */
|
|
|
|
static int l1sap_info_rel_cnf(struct gsm_bts_trx *trx,
|
|
|
|
struct osmo_phsap_prim *l1sap,
|
|
|
|
struct info_act_cnf_param *info_act_cnf)
|
|
|
|
{
|
|
|
|
struct gsm_lchan *lchan;
|
|
|
|
|
2017-12-02 16:31:45 +00:00
|
|
|
LOGP(DL1C, LOGL_INFO, "deactivate confirm chan_nr=0x%02x trx=%d\n",
|
2013-09-01 07:19:45 +00:00
|
|
|
info_act_cnf->chan_nr, trx->nr);
|
|
|
|
|
2015-12-05 10:54:08 +00:00
|
|
|
lchan = get_lchan_by_chan_nr(trx, info_act_cnf->chan_nr);
|
2013-09-01 07:19:45 +00:00
|
|
|
|
|
|
|
rsl_tx_rf_rel_ack(lchan);
|
|
|
|
|
2016-06-16 15:32:32 +00:00
|
|
|
/* During PDCH DEACT, this marks the deactivation of the PDTCH as
|
|
|
|
* requested by the PCU. Next up, we disconnect the TS completely and
|
2016-07-26 13:22:58 +00:00
|
|
|
* call back to cb_ts_disconnected(). See rsl_rx_dyn_pdch(). */
|
2016-07-16 20:29:28 +00:00
|
|
|
if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH
|
|
|
|
&& (lchan->ts->flags & TS_F_PDCH_DEACT_PENDING))
|
2016-06-16 15:32:32 +00:00
|
|
|
bts_model_ts_disconnect(lchan->ts);
|
|
|
|
|
2013-09-01 07:19:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* any L1 MPH_INFO confirm prim recevied from bts model */
|
|
|
|
static int l1sap_mph_info_cnf(struct gsm_bts_trx *trx,
|
|
|
|
struct osmo_phsap_prim *l1sap, struct mph_info_param *info)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
switch (info->type) {
|
|
|
|
case PRIM_INFO_ACTIVATE:
|
|
|
|
rc = l1sap_info_act_cnf(trx, l1sap, &info->u.act_cnf);
|
|
|
|
break;
|
|
|
|
case PRIM_INFO_DEACTIVATE:
|
|
|
|
rc = l1sap_info_rel_cnf(trx, l1sap, &info->u.act_cnf);
|
|
|
|
break;
|
|
|
|
default:
|
2017-12-02 16:31:45 +00:00
|
|
|
LOGP(DL1C, LOGL_NOTICE, "unknown MPH_INFO cnf type %d\n",
|
2013-09-01 07:19:45 +00:00
|
|
|
info->type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2017-06-23 16:26:24 +00:00
|
|
|
/*! handling for PDTCH loopback mode, used for BER testing
|
|
|
|
* \param[in] lchan logical channel on which we operate
|
|
|
|
* \param[in] rts_ind PH-RTS.ind from PHY which we process
|
|
|
|
* \param[out] msg Message buffer to which we write data
|
|
|
|
*
|
|
|
|
* The function will fill \a msg, from which the caller can then
|
|
|
|
* subsequently build a PH-DATA.req */
|
|
|
|
static int lchan_pdtch_ph_rts_ind_loop(struct gsm_lchan *lchan,
|
|
|
|
const struct ph_data_param *rts_ind,
|
|
|
|
struct msgb *msg, const struct gsm_time *tm)
|
|
|
|
{
|
|
|
|
struct msgb *loop_msg;
|
|
|
|
uint8_t *p;
|
|
|
|
|
|
|
|
/* de-queue response message (loopback) */
|
|
|
|
loop_msg = msgb_dequeue(&lchan->dl_tch_queue);
|
|
|
|
if (!loop_msg) {
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPGT(DL1P, LOGL_NOTICE, tm, "%s: no looped PDTCH message, sending empty\n",
|
|
|
|
gsm_lchan_name(lchan));
|
2017-06-23 16:26:24 +00:00
|
|
|
/* empty downlink message */
|
|
|
|
p = msgb_put(msg, GSM_MACBLOCK_LEN);
|
|
|
|
memset(p, 0, GSM_MACBLOCK_LEN);
|
|
|
|
} else {
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPGT(DL1P, LOGL_NOTICE, tm, "%s: looped PDTCH message of %u bytes\n",
|
|
|
|
gsm_lchan_name(lchan), msgb_l2len(loop_msg));
|
2017-06-23 16:26:24 +00:00
|
|
|
/* 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));
|
|
|
|
msgb_free(loop_msg);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-27 13:32:46 +00:00
|
|
|
/* PH-RTS-IND prim received from bts model */
|
2013-07-29 07:42:23 +00:00
|
|
|
static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
|
|
|
|
struct osmo_phsap_prim *l1sap, struct ph_data_param *rts_ind)
|
|
|
|
{
|
|
|
|
struct msgb *msg = l1sap->oph.msg;
|
|
|
|
struct gsm_time g_time;
|
2013-09-01 09:09:20 +00:00
|
|
|
struct gsm_lchan *lchan;
|
2013-07-29 07:42:23 +00:00
|
|
|
uint8_t chan_nr, link_id;
|
2015-12-05 10:54:08 +00:00
|
|
|
uint8_t tn;
|
2013-07-29 07:42:23 +00:00
|
|
|
uint32_t fn;
|
|
|
|
uint8_t *p, *si;
|
2013-09-01 09:09:20 +00:00
|
|
|
struct lapdm_entity *le;
|
|
|
|
struct osmo_phsap_prim pp;
|
2016-10-03 15:37:45 +00:00
|
|
|
bool dtxd_facch = false;
|
2013-08-30 06:03:09 +00:00
|
|
|
int rc;
|
2013-07-29 07:42:23 +00:00
|
|
|
|
|
|
|
chan_nr = rts_ind->chan_nr;
|
|
|
|
link_id = rts_ind->link_id;
|
|
|
|
fn = rts_ind->fn;
|
|
|
|
tn = L1SAP_CHAN2TS(chan_nr);
|
|
|
|
|
|
|
|
gsm_fn2gsmtime(&g_time, fn);
|
|
|
|
|
2018-02-22 10:44:20 +00:00
|
|
|
DEBUGPGT(DL1P, &g_time, "Rx PH-RTS.ind chan_nr=0x%02x link_id=0x%02xd\n", chan_nr, link_id);
|
2013-08-30 06:48:38 +00:00
|
|
|
|
2013-07-29 07:42:23 +00:00
|
|
|
/* reuse PH-RTS.ind for PH-DATA.req */
|
|
|
|
if (!msg) {
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPGT(DL1P, LOGL_FATAL, &g_time, "RTS without msg to be reused. Please fix!\n");
|
2013-07-29 07:42:23 +00:00
|
|
|
abort();
|
|
|
|
}
|
|
|
|
msgb_trim(msg, sizeof(*l1sap));
|
|
|
|
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_REQUEST,
|
|
|
|
msg);
|
|
|
|
msg->l2h = msg->l1h + sizeof(*l1sap);
|
|
|
|
|
2017-06-23 16:26:24 +00:00
|
|
|
if (ts_is_pdch(&trx->ts[tn])) {
|
|
|
|
lchan = get_active_lchan_by_chan_nr(trx, chan_nr);
|
|
|
|
if (lchan && lchan->loopback) {
|
|
|
|
if (!L1SAP_IS_PTCCH(rts_ind->fn))
|
|
|
|
lchan_pdtch_ph_rts_ind_loop(lchan, rts_ind, msg, &g_time);
|
|
|
|
/* continue below like for SACCH/FACCH/... */
|
|
|
|
} else {
|
|
|
|
/* forward RTS.ind to PCU */
|
|
|
|
if (L1SAP_IS_PTCCH(rts_ind->fn)) {
|
|
|
|
pcu_tx_rts_req(&trx->ts[tn], 1, fn, 1 /* ARFCN */,
|
|
|
|
L1SAP_FN2PTCCHBLOCK(fn));
|
|
|
|
} else {
|
|
|
|
pcu_tx_rts_req(&trx->ts[tn], 0, fn, 0 /* ARFCN */,
|
|
|
|
L1SAP_FN2MACBLOCK(fn));
|
|
|
|
}
|
|
|
|
/* return early, PCU takes care of rest */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else if (L1SAP_IS_CHAN_BCCH(chan_nr)) {
|
2013-07-29 07:42:23 +00:00
|
|
|
p = msgb_put(msg, GSM_MACBLOCK_LEN);
|
|
|
|
/* get them from bts->si_buf[] */
|
|
|
|
si = bts_sysinfo_get(trx->bts, &g_time);
|
|
|
|
if (si)
|
|
|
|
memcpy(p, si, GSM_MACBLOCK_LEN);
|
|
|
|
else
|
|
|
|
memcpy(p, fill_frame, GSM_MACBLOCK_LEN);
|
2013-09-01 09:09:20 +00:00
|
|
|
} else if (!(chan_nr & 0x80)) { /* only TCH/F, TCH/H, SDCCH/4 and SDCCH/8 have C5 bit cleared */
|
2015-12-05 10:54:08 +00:00
|
|
|
lchan = get_active_lchan_by_chan_nr(trx, chan_nr);
|
2017-06-23 16:57:28 +00:00
|
|
|
if (!lchan) {
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPGT(DL1P, LOGL_ERROR, &g_time, "No lchan for PH-RTS.ind (chan_nr=%u)\n", chan_nr);
|
2015-12-05 10:54:08 +00:00
|
|
|
return 0;
|
2017-06-23 16:57:28 +00:00
|
|
|
}
|
2013-09-01 09:09:20 +00:00
|
|
|
if (L1SAP_IS_LINK_SACCH(link_id)) {
|
|
|
|
p = msgb_put(msg, GSM_MACBLOCK_LEN);
|
|
|
|
/* L1-header, if not set/modified by layer 1 */
|
2014-08-07 06:32:30 +00:00
|
|
|
p[0] = lchan->ms_power_ctrl.current;
|
2013-09-01 09:09:20 +00:00
|
|
|
p[1] = lchan->rqd_ta;
|
|
|
|
le = &lchan->lapdm_ch.lapdm_acch;
|
2016-10-03 15:37:45 +00:00
|
|
|
} else {
|
|
|
|
if (lchan->ts->trx->bts->dtxd)
|
|
|
|
dtxd_facch = true;
|
2013-09-01 09:09:20 +00:00
|
|
|
le = &lchan->lapdm_ch.lapdm_dcch;
|
2016-10-03 15:37:45 +00:00
|
|
|
}
|
2013-09-01 09:09:20 +00:00
|
|
|
rc = lapdm_phsap_dequeue_prim(le, &pp);
|
|
|
|
if (rc < 0) {
|
|
|
|
if (L1SAP_IS_LINK_SACCH(link_id)) {
|
|
|
|
/* No SACCH data from LAPDM pending, send SACCH filling */
|
|
|
|
uint8_t *si = lchan_sacch_get(lchan);
|
|
|
|
if (si) {
|
|
|
|
/* The +2 is empty space where the DSP inserts the L1 hdr */
|
|
|
|
memcpy(p + 2, si, GSM_MACBLOCK_LEN - 2);
|
|
|
|
} else
|
|
|
|
memcpy(p + 2, fill_frame, GSM_MACBLOCK_LEN - 2);
|
|
|
|
} else if ((!L1SAP_IS_CHAN_TCHF(chan_nr) && !L1SAP_IS_CHAN_TCHH(chan_nr))
|
|
|
|
|| lchan->rsl_cmode == RSL_CMOD_SPD_SIGN) {
|
|
|
|
/* send fill frame only, if not TCH/x != Signalling, otherwise send empty frame */
|
|
|
|
p = msgb_put(msg, GSM_MACBLOCK_LEN);
|
|
|
|
memcpy(p, fill_frame, GSM_MACBLOCK_LEN);
|
|
|
|
} /* else the message remains empty, so TCH frames are sent */
|
|
|
|
} 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);
|
|
|
|
else {
|
|
|
|
p = msgb_put(msg, GSM_MACBLOCK_LEN);
|
|
|
|
memcpy(p, pp.oph.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);
|
2016-11-04 15:52:35 +00:00
|
|
|
if (dtxd_facch)
|
|
|
|
dtx_dispatch(lchan, E_FACCH);
|
2013-09-01 09:09:20 +00:00
|
|
|
}
|
|
|
|
msgb_free(pp.oph.msg);
|
|
|
|
}
|
2013-08-30 06:03:09 +00:00
|
|
|
} else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) {
|
|
|
|
p = msgb_put(msg, GSM_MACBLOCK_LEN);
|
|
|
|
rc = bts_ccch_copy_msg(trx->bts, p, &g_time,
|
2016-10-12 14:24:32 +00:00
|
|
|
(L1SAP_FN2CCCHBLOCK(fn) <
|
|
|
|
num_agch(trx, "PH-RTS-IND")));
|
2013-08-30 06:03:09 +00:00
|
|
|
if (rc <= 0)
|
|
|
|
memcpy(p, fill_frame, GSM_MACBLOCK_LEN);
|
2013-07-29 07:42:23 +00:00
|
|
|
}
|
|
|
|
|
2018-02-22 10:44:20 +00:00
|
|
|
DEBUGPGT(DL1P, &g_time, "Tx PH-DATA.req chan_nr=0x%02x link_id=0x%02x\n", chan_nr, link_id);
|
2013-07-29 07:42:23 +00:00
|
|
|
|
|
|
|
l1sap_down(trx, l1sap);
|
|
|
|
|
|
|
|
/* don't free, because we forwarded data */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-08-30 05:46:30 +00:00
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
struct gsm_lchan *lchan;
|
|
|
|
|
2015-12-05 10:54:08 +00:00
|
|
|
lchan = get_lchan_by_chan_nr(trx, rach_ind->chan_nr);
|
2013-08-30 05:46:30 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-02-09 11:08:38 +00:00
|
|
|
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)
|
|
|
|
|
|
|
|
if(payload_len < 2 || AMR_PADDING1(rtp_pl) || AMR_PADDING2(rtp_pl))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool rtppayload_is_valid(struct gsm_lchan *lchan, struct msgb *resp_msg)
|
|
|
|
{
|
|
|
|
/* 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)) {
|
|
|
|
LOGP(DL1P, LOGL_NOTICE,
|
|
|
|
"%s RTP->L1: Dropping unexpected AMR encoding (bw-efficient?) %s\n",
|
|
|
|
gsm_lchan_name(lchan), osmo_hexdump(resp_msg->data, resp_msg->len));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-09-01 08:08:15 +00:00
|
|
|
/* TCH-RTS-IND prim recevied 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)
|
|
|
|
{
|
|
|
|
struct msgb *resp_msg;
|
|
|
|
struct osmo_phsap_prim *resp_l1sap, empty_l1sap;
|
|
|
|
struct gsm_time g_time;
|
|
|
|
struct gsm_lchan *lchan;
|
2016-09-17 12:15:03 +00:00
|
|
|
uint8_t chan_nr, marker = 0;
|
2018-02-01 18:28:09 +00:00
|
|
|
uint32_t fn;
|
2017-01-18 16:52:27 +00:00
|
|
|
int rc;
|
2013-09-01 08:08:15 +00:00
|
|
|
|
|
|
|
chan_nr = rts_ind->chan_nr;
|
|
|
|
fn = rts_ind->fn;
|
|
|
|
|
|
|
|
gsm_fn2gsmtime(&g_time, fn);
|
|
|
|
|
2018-02-22 10:44:20 +00:00
|
|
|
DEBUGPGT(DL1P, &g_time, "Rx TCH-RTS.ind chan_nr=0x%02x\n", chan_nr);
|
2013-09-01 08:08:15 +00:00
|
|
|
|
2015-12-05 10:54:08 +00:00
|
|
|
lchan = get_active_lchan_by_chan_nr(trx, chan_nr);
|
2017-06-23 16:57:28 +00:00
|
|
|
if (!lchan) {
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPGT(DL1P, LOGL_ERROR, &g_time, "No lchan for PH-RTS.ind (chan_nr=%u)\n", chan_nr);
|
2015-12-05 10:54:08 +00:00
|
|
|
return 0;
|
2017-06-23 16:57:28 +00:00
|
|
|
}
|
2013-09-01 08:08:15 +00:00
|
|
|
|
|
|
|
if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
|
|
|
|
osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
|
|
|
|
/* FIXME: we _assume_ that we never miss TDMA
|
|
|
|
* frames and that we always get to this point
|
|
|
|
* for every to-be-transmitted voice frame. A
|
|
|
|
* better solution would be to compute
|
|
|
|
* rx_user_ts based on how many TDMA frames have
|
|
|
|
* elapsed since the last call */
|
|
|
|
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);
|
|
|
|
if (!resp_msg) {
|
2018-02-22 10:44:20 +00:00
|
|
|
DEBUGPGT(DL1P, &g_time, "%s DL TCH Tx queue underrun\n", gsm_lchan_name(lchan));
|
2013-09-01 08:08:15 +00:00
|
|
|
resp_l1sap = &empty_l1sap;
|
2018-02-09 11:08:38 +00:00
|
|
|
} else if(!rtppayload_is_valid(lchan, resp_msg)) {
|
|
|
|
msgb_free(resp_msg);
|
|
|
|
resp_msg = NULL;
|
|
|
|
resp_l1sap = &empty_l1sap;
|
2013-09-01 08:08:15 +00:00
|
|
|
} else {
|
2016-09-17 12:15:03 +00:00
|
|
|
/* Obtain RTP header Marker bit from control buffer */
|
|
|
|
marker = rtpmsg_marker_bit(resp_msg);
|
|
|
|
|
2013-09-01 08:08:15 +00:00
|
|
|
resp_msg->l2h = resp_msg->data;
|
|
|
|
msgb_push(resp_msg, sizeof(*resp_l1sap));
|
|
|
|
resp_msg->l1h = resp_msg->data;
|
|
|
|
resp_l1sap = msgb_l1sap_prim(resp_msg);
|
|
|
|
}
|
|
|
|
|
2017-01-18 16:52:27 +00:00
|
|
|
/* check for pending REL_IND */
|
|
|
|
if (lchan->pending_rel_ind_msg) {
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPGT(DRSL, LOGL_INFO, &g_time, "%s Forward REL_IND to L3\n", gsm_lchan_name(lchan));
|
2017-01-18 16:52:27 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2013-09-01 08:08:15 +00:00
|
|
|
memset(resp_l1sap, 0, sizeof(*resp_l1sap));
|
|
|
|
osmo_prim_init(&resp_l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_REQUEST,
|
|
|
|
resp_msg);
|
|
|
|
resp_l1sap->u.tch.chan_nr = chan_nr;
|
|
|
|
resp_l1sap->u.tch.fn = fn;
|
2016-09-17 12:15:03 +00:00
|
|
|
resp_l1sap->u.tch.marker = marker;
|
2013-09-01 08:08:15 +00:00
|
|
|
|
2018-02-22 10:44:20 +00:00
|
|
|
DEBUGPGT(DL1P, &g_time, "Tx TCH.req chan_nr=0x%02x\n", chan_nr);
|
2013-09-01 08:08:15 +00:00
|
|
|
|
|
|
|
l1sap_down(trx, resp_l1sap);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-05-27 09:11:19 +00:00
|
|
|
/* 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." */
|
2013-09-01 09:09:20 +00:00
|
|
|
static void radio_link_timeout(struct gsm_lchan *lchan, int bad_frame)
|
|
|
|
{
|
|
|
|
struct gsm_bts_role_bts *btsb = lchan->ts->trx->bts->role;
|
|
|
|
|
2017-05-27 10:12:39 +00:00
|
|
|
/* Bypass radio link timeout if set to -1 */
|
|
|
|
if (btsb->radio_link_timeout < 0)
|
|
|
|
return;
|
|
|
|
|
2013-09-01 09:09:20 +00:00
|
|
|
/* if link loss criterion already reached */
|
|
|
|
if (lchan->s == 0) {
|
|
|
|
DEBUGP(DMEAS, "%s radio link counter S already 0.\n",
|
|
|
|
gsm_lchan_name(lchan));
|
|
|
|
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)
|
|
|
|
rsl_tx_conn_fail(lchan, RSL_ERR_RADIO_LINK_FAIL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lchan->s < btsb->radio_link_timeout) {
|
|
|
|
/* count up radio link counter S */
|
|
|
|
lchan->s += 2;
|
|
|
|
if (lchan->s > btsb->radio_link_timeout)
|
|
|
|
lchan->s = btsb->radio_link_timeout;
|
|
|
|
DEBUGP(DMEAS, "%s counting up radio link counter S=%d\n",
|
|
|
|
gsm_lchan_name(lchan), lchan->s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-27 20:13:18 +00:00
|
|
|
static inline int check_for_first_ciphrd(struct gsm_lchan *lchan,
|
|
|
|
uint8_t *data, int len)
|
|
|
|
{
|
|
|
|
uint8_t n_s;
|
|
|
|
|
|
|
|
/* if this is the first valid message after enabling Rx
|
|
|
|
* decryption, we have to enable Tx encryption */
|
|
|
|
if (lchan->ciph_state != LCHAN_CIPH_RX_CONF)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* HACK: check if it's an I frame, in order to
|
|
|
|
* ignore some still buffered/queued UI frames received
|
|
|
|
* before decryption was enabled */
|
|
|
|
if (data[0] != 0x01)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((data[1] & 0x01) != 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
n_s = data[1] >> 5;
|
|
|
|
if (lchan->ciph_ns != n_s)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* public helper for the test */
|
|
|
|
int bts_check_for_first_ciphrd(struct gsm_lchan *lchan,
|
|
|
|
uint8_t *data, int len)
|
|
|
|
{
|
|
|
|
return check_for_first_ciphrd(lchan, data, len);
|
|
|
|
}
|
|
|
|
|
2013-08-30 06:48:38 +00:00
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
struct msgb *msg = l1sap->oph.msg;
|
|
|
|
struct gsm_time g_time;
|
2013-09-01 09:09:20 +00:00
|
|
|
struct gsm_lchan *lchan;
|
|
|
|
struct lapdm_entity *le;
|
2013-08-30 06:48:38 +00:00
|
|
|
uint8_t *data = msg->l2h;
|
|
|
|
int len = msgb_l2len(msg);
|
|
|
|
uint8_t chan_nr, link_id;
|
2015-12-05 10:54:08 +00:00
|
|
|
uint8_t tn;
|
2013-08-30 06:48:38 +00:00
|
|
|
uint32_t fn;
|
|
|
|
int8_t rssi;
|
2016-02-18 16:48:07 +00:00
|
|
|
enum osmo_ph_pres_info_type pr_info = data_ind->pdch_presence_info;
|
2013-08-30 06:48:38 +00:00
|
|
|
|
|
|
|
rssi = data_ind->rssi;
|
|
|
|
chan_nr = data_ind->chan_nr;
|
|
|
|
link_id = data_ind->link_id;
|
|
|
|
fn = data_ind->fn;
|
|
|
|
tn = L1SAP_CHAN2TS(chan_nr);
|
|
|
|
|
|
|
|
gsm_fn2gsmtime(&g_time, fn);
|
|
|
|
|
2018-02-22 10:44:20 +00:00
|
|
|
DEBUGPGT(DL1P, &g_time, "Rx PH-DATA.ind chan_nr=0x%02x link_id=0x%02x len=%d\n",
|
|
|
|
chan_nr, link_id, len);
|
2013-08-30 06:48:38 +00:00
|
|
|
|
2016-07-27 13:52:55 +00:00
|
|
|
if (ts_is_pdch(&trx->ts[tn])) {
|
2017-06-23 16:26:24 +00:00
|
|
|
lchan = get_lchan_by_chan_nr(trx, chan_nr);
|
|
|
|
if (!lchan)
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPGT(DL1P, LOGL_ERROR, &g_time, "No lchan for chan_nr=0x%02x\n", chan_nr);
|
2017-06-29 14:44:01 +00:00
|
|
|
if (lchan && lchan->loopback && !L1SAP_IS_PTCCH(fn)) {
|
2017-06-23 16:26:24 +00:00
|
|
|
/* 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);
|
|
|
|
|
|
|
|
/* 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 */
|
2013-08-30 06:48:38 +00:00
|
|
|
if (len == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
if (L1SAP_IS_PTCCH(fn)) {
|
2017-08-31 11:52:10 +00:00
|
|
|
pcu_tx_data_ind(&trx->ts[tn], PCU_IF_SAPI_PTCCH, fn,
|
2017-06-23 16:26:24 +00:00
|
|
|
0 /* ARFCN */, L1SAP_FN2PTCCHBLOCK(fn),
|
2016-07-28 12:46:00 +00:00
|
|
|
data, len, rssi, data_ind->ber10k,
|
2018-02-27 15:58:46 +00:00
|
|
|
data_ind->ta_offs_256bits/64,
|
2016-07-28 12:46:00 +00:00
|
|
|
data_ind->lqual_cb);
|
2017-06-23 16:26:24 +00:00
|
|
|
} else {
|
|
|
|
/* drop incomplete UL block */
|
|
|
|
if (pr_info != PRES_INFO_BOTH)
|
|
|
|
return 0;
|
|
|
|
/* PDTCH / PACCH frame handling */
|
2017-08-31 11:52:10 +00:00
|
|
|
pcu_tx_data_ind(&trx->ts[tn], PCU_IF_SAPI_PDTCH, fn, 0 /* ARFCN */,
|
2017-06-23 16:26:24 +00:00
|
|
|
L1SAP_FN2MACBLOCK(fn), data, len, rssi, data_ind->ber10k,
|
2018-02-27 15:58:46 +00:00
|
|
|
data_ind->ta_offs_256bits/64, data_ind->lqual_cb);
|
2013-08-30 06:48:38 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-12-05 10:54:08 +00:00
|
|
|
lchan = get_active_lchan_by_chan_nr(trx, chan_nr);
|
2017-06-23 16:57:28 +00:00
|
|
|
if (!lchan) {
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPGT(DL1P, LOGL_ERROR, &g_time, "No lchan for chan_nr=%d\n", chan_nr);
|
2015-12-05 10:54:08 +00:00
|
|
|
return 0;
|
2017-06-23 16:57:28 +00:00
|
|
|
}
|
2013-09-01 09:09:20 +00:00
|
|
|
|
|
|
|
/* bad frame */
|
|
|
|
if (len == 0) {
|
|
|
|
if (L1SAP_IS_LINK_SACCH(link_id))
|
|
|
|
radio_link_timeout(lchan, 1);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2014-04-04 12:11:33 +00:00
|
|
|
/* report first valid received frame to handover process */
|
|
|
|
if (lchan->ho.active == HANDOVER_WAIT_FRAME)
|
|
|
|
handover_frame(lchan);
|
|
|
|
|
2013-09-01 09:09:20 +00:00
|
|
|
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) {
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPGT(DL1P, LOGL_NOTICE, &g_time, "SACCH with size %u<2 !?!\n", len);
|
2013-09-01 09:09:20 +00:00
|
|
|
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;
|
2015-09-06 10:33:16 +00:00
|
|
|
|
|
|
|
lchan_ms_pwr_ctrl(lchan, data[0] & 0x1f, data_ind->rssi);
|
2013-09-01 09:09:20 +00:00
|
|
|
} else
|
|
|
|
le = &lchan->lapdm_ch.lapdm_dcch;
|
|
|
|
|
2014-08-27 20:13:18 +00:00
|
|
|
if (check_for_first_ciphrd(lchan, data, len))
|
|
|
|
l1sap_tx_ciph_req(lchan->ts->trx, chan_nr, 1, 0);
|
2013-09-01 09:09:20 +00:00
|
|
|
|
|
|
|
/* SDCCH, SACCH and FACCH all go to LAPDm */
|
|
|
|
msgb_pull(msg, (msg->l2h - msg->data));
|
|
|
|
msg->l1h = NULL;
|
|
|
|
lapdm_phsap_up(&l1sap->oph, le);
|
|
|
|
|
|
|
|
/* don't free, because we forwarded data */
|
|
|
|
return 1;
|
2013-08-30 06:48:38 +00:00
|
|
|
}
|
|
|
|
|
2013-09-01 08:08:15 +00:00
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
struct msgb *msg = l1sap->oph.msg;
|
|
|
|
struct gsm_time g_time;
|
|
|
|
struct gsm_lchan *lchan;
|
2015-12-05 10:54:08 +00:00
|
|
|
uint8_t chan_nr;
|
2013-09-01 08:08:15 +00:00
|
|
|
uint32_t fn;
|
2017-06-28 13:41:04 +00:00
|
|
|
struct gsm_bts_role_bts *btsb = bts_role_bts(trx->bts);
|
2013-09-01 08:08:15 +00:00
|
|
|
|
|
|
|
chan_nr = tch_ind->chan_nr;
|
|
|
|
fn = tch_ind->fn;
|
|
|
|
|
|
|
|
gsm_fn2gsmtime(&g_time, fn);
|
|
|
|
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPGT(DL1P, LOGL_INFO, &g_time, "Rx TCH.ind chan_nr=0x%02x\n", chan_nr);
|
2013-09-01 08:08:15 +00:00
|
|
|
|
2015-12-05 10:54:08 +00:00
|
|
|
lchan = get_active_lchan_by_chan_nr(trx, chan_nr);
|
2017-06-23 16:57:28 +00:00
|
|
|
if (!lchan) {
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPGT(DL1P, LOGL_ERROR, &g_time, "No lchan for TCH.ind (chan_nr=%u)\n", chan_nr);
|
2015-12-05 10:54:08 +00:00
|
|
|
return 0;
|
2017-06-23 16:57:28 +00:00
|
|
|
}
|
2015-12-05 10:54:08 +00:00
|
|
|
|
2013-09-01 08:08:15 +00:00
|
|
|
msgb_pull(msg, sizeof(*l1sap));
|
|
|
|
|
2017-06-28 13:41:04 +00:00
|
|
|
/* 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 / 10 >= btsb->min_qual_norm) {
|
|
|
|
/* 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);
|
|
|
|
/* 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);
|
|
|
|
/* 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 {
|
2018-02-22 10:44:20 +00:00
|
|
|
DEBUGPGT(DRTP, &g_time, "Skipping RTP frame with lost payload (chan_nr=0x%02x)\n",
|
|
|
|
chan_nr);
|
2017-06-28 13:41:04 +00:00
|
|
|
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;
|
2013-09-01 08:08:15 +00:00
|
|
|
}
|
2016-06-13 09:33:43 +00:00
|
|
|
|
2016-06-03 11:29:29 +00:00
|
|
|
lchan->tch.last_fn = fn;
|
2013-09-01 08:08:15 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-05 13:29:47 +00:00
|
|
|
static bool rach_pass_filter(struct ph_rach_ind_param *rach_ind,
|
|
|
|
struct gsm_bts_role_bts *btsb)
|
|
|
|
{
|
|
|
|
/* Check for RACH exceeding BER threshold (ghost RACH) */
|
|
|
|
if (rach_ind->ber10k > btsb->max_ber10k_rach) {
|
|
|
|
LOGPFN(DL1C, LOGL_INFO, rach_ind->fn, "Ignoring RACH request: "
|
|
|
|
"BER10k(%u) > BER10k_MAX(%u)\n",
|
|
|
|
rach_ind->ber10k, btsb->max_ber10k_rach);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure that ToA (Timing of Arrival) is acceptable */
|
|
|
|
if (rach_ind->acc_delay > btsb->max_ta) {
|
|
|
|
LOGPFN(DL1C, LOGL_INFO, rach_ind->fn, "Ignoring RACH request: "
|
|
|
|
"ToA(%u) exceeds the maximal allowed TA(%u) value\n",
|
|
|
|
rach_ind->acc_delay, btsb->max_ta);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-08-30 05:46:30 +00:00
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = trx->bts;
|
|
|
|
struct gsm_bts_role_bts *btsb = bts->role;
|
|
|
|
struct lapdm_channel *lc;
|
|
|
|
|
2018-02-22 10:44:20 +00:00
|
|
|
DEBUGPFN(DL1P, rach_ind->fn, "Rx PH-RA.ind");
|
2018-02-26 10:57:49 +00:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2018-02-24 11:36:52 +00:00
|
|
|
rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_RCVD);
|
2013-08-30 05:46:30 +00:00
|
|
|
|
2018-02-26 10:57:49 +00:00
|
|
|
/* increment number of busy RACH slots, if required */
|
|
|
|
if (rach_ind->rssi >= btsb->load.rach.busy_thresh)
|
|
|
|
btsb->load.rach.busy++;
|
|
|
|
|
2018-03-05 13:29:47 +00:00
|
|
|
/* Filter out noise / interference / ghosts */
|
|
|
|
if (!rach_pass_filter(rach_ind, btsb)) {
|
2018-02-26 07:49:59 +00:00
|
|
|
rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_DROP);
|
2013-08-30 05:46:30 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-05 13:03:06 +00:00
|
|
|
/* increment number of RACH slots with valid non-handover RACH burst */
|
|
|
|
btsb->load.rach.access++;
|
|
|
|
|
|
|
|
lc = &trx->ts[0].lchan[CCCH_LCHAN].lapdm_ch;
|
|
|
|
|
2017-03-07 16:30:35 +00:00
|
|
|
/* According to 3GPP TS 48.058 § 9.3.17 Access Delay is expressed same way as TA (number of symbols) */
|
2018-03-05 13:29:47 +00:00
|
|
|
set_ms_to_data(get_lchan_by_chan_nr(trx, rach_ind->chan_nr),
|
|
|
|
rach_ind->acc_delay, false);
|
2017-03-07 16:30:35 +00:00
|
|
|
|
2013-08-30 05:46:30 +00:00
|
|
|
/* check for packet access */
|
2016-08-17 10:57:31 +00:00
|
|
|
if ((trx == bts->c0 && L1SAP_IS_PACKET_RACH(rach_ind->ra)) ||
|
|
|
|
(trx == bts->c0 && rach_ind->is_11bit)) {
|
2018-02-26 07:49:59 +00:00
|
|
|
rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_PS);
|
2016-08-17 10:57:31 +00:00
|
|
|
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPFN(DL1P, LOGL_INFO, rach_ind->fn, "RACH for packet access (toa=%d, ra=%d)\n",
|
2017-03-02 12:50:19 +00:00
|
|
|
rach_ind->acc_delay, rach_ind->ra);
|
|
|
|
|
2013-08-30 05:46:30 +00:00
|
|
|
pcu_tx_rach_ind(bts, rach_ind->acc_delay << 2,
|
2016-08-17 10:57:31 +00:00
|
|
|
rach_ind->ra, rach_ind->fn,
|
|
|
|
rach_ind->is_11bit, rach_ind->burst_type);
|
2013-08-30 05:46:30 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-22 10:44:20 +00:00
|
|
|
LOGPFN(DL1P, LOGL_INFO, rach_ind->fn, "RACH for RR access (toa=%d, ra=%d)\n",
|
2013-08-30 05:46:30 +00:00
|
|
|
rach_ind->acc_delay, rach_ind->ra);
|
2018-02-26 07:49:59 +00:00
|
|
|
rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_CS);
|
2013-08-30 05:46:30 +00:00
|
|
|
lapdm_phsap_up(&l1sap->oph, &lc->lapdm_dcch);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-04 19:05:41 +00:00
|
|
|
/* any L1 prim received from bts model, takes ownership of the msgb */
|
2013-07-29 07:42:23 +00:00
|
|
|
int l1sap_up(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
|
|
|
|
{
|
|
|
|
struct msgb *msg = l1sap->oph.msg;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
switch (OSMO_PRIM_HDR(&l1sap->oph)) {
|
2013-08-31 17:49:12 +00:00
|
|
|
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_INDICATION):
|
|
|
|
rc = l1sap_mph_info_ind(trx, l1sap, &l1sap->u.info);
|
|
|
|
break;
|
2013-09-01 07:19:45 +00:00
|
|
|
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_CONFIRM):
|
|
|
|
rc = l1sap_mph_info_cnf(trx, l1sap, &l1sap->u.info);
|
|
|
|
break;
|
2013-07-29 07:42:23 +00:00
|
|
|
case OSMO_PRIM(PRIM_PH_RTS, PRIM_OP_INDICATION):
|
|
|
|
rc = l1sap_ph_rts_ind(trx, l1sap, &l1sap->u.data);
|
|
|
|
break;
|
2013-09-01 08:08:15 +00:00
|
|
|
case OSMO_PRIM(PRIM_TCH_RTS, PRIM_OP_INDICATION):
|
|
|
|
rc = l1sap_tch_rts_ind(trx, l1sap, &l1sap->u.tch);
|
|
|
|
break;
|
2013-08-30 06:48:38 +00:00
|
|
|
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_INDICATION):
|
2013-07-29 07:45:22 +00:00
|
|
|
to_gsmtap(trx, l1sap);
|
2013-08-30 06:48:38 +00:00
|
|
|
rc = l1sap_ph_data_ind(trx, l1sap, &l1sap->u.data);
|
|
|
|
break;
|
2013-09-01 08:08:15 +00:00
|
|
|
case OSMO_PRIM(PRIM_TCH, PRIM_OP_INDICATION):
|
|
|
|
rc = l1sap_tch_ind(trx, l1sap, &l1sap->u.tch);
|
|
|
|
break;
|
2013-08-30 05:46:30 +00:00
|
|
|
case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_INDICATION):
|
2013-07-29 07:45:22 +00:00
|
|
|
to_gsmtap(trx, l1sap);
|
2013-08-30 05:46:30 +00:00
|
|
|
rc = l1sap_ph_rach_ind(trx, l1sap, &l1sap->u.rach_ind);
|
|
|
|
break;
|
2013-07-29 07:42:23 +00:00
|
|
|
default:
|
|
|
|
LOGP(DL1P, LOGL_NOTICE, "unknown prim %d op %d\n",
|
|
|
|
l1sap->oph.primitive, l1sap->oph.operation);
|
2017-01-24 15:27:06 +00:00
|
|
|
oml_fail_rep(OSMO_EVT_MAJ_UKWN_MSG, "unknown prim %d op %d",
|
|
|
|
l1sap->oph.primitive, l1sap->oph.operation);
|
2013-07-29 07:42:23 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Special return value '1' means: do not free */
|
|
|
|
if (rc != 1)
|
|
|
|
msgb_free(msg);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* any L1 prim sent to bts model */
|
|
|
|
static int l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
|
|
|
|
{
|
2013-07-29 07:45:22 +00:00
|
|
|
if (OSMO_PRIM_HDR(&l1sap->oph) ==
|
|
|
|
OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST))
|
|
|
|
to_gsmtap(trx, l1sap);
|
|
|
|
|
2013-07-29 07:42:23 +00:00
|
|
|
return bts_model_l1sap_down(trx, l1sap);
|
|
|
|
}
|
|
|
|
|
2013-08-30 06:48:38 +00:00
|
|
|
/* pcu (socket interface) sends us a data request primitive */
|
|
|
|
int l1sap_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
|
|
|
|
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
|
|
|
|
{
|
|
|
|
struct msgb *msg;
|
|
|
|
struct osmo_phsap_prim *l1sap;
|
|
|
|
struct gsm_time g_time;
|
|
|
|
|
|
|
|
gsm_fn2gsmtime(&g_time, fn);
|
|
|
|
|
2017-06-30 16:57:02 +00:00
|
|
|
DEBUGP(DL1P, "TX packet data %s is_ptcch=%d trx=%d ts=%d "
|
|
|
|
"block_nr=%d, arfcn=%d, len=%d\n", osmo_dump_gsmtime(&g_time),
|
|
|
|
is_ptcch, ts->trx->nr, ts->nr, block_nr, arfcn, len);
|
2013-08-30 06:48:38 +00:00
|
|
|
|
|
|
|
msg = l1sap_msgb_alloc(len);
|
|
|
|
l1sap = msgb_l1sap_prim(msg);
|
|
|
|
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_REQUEST,
|
|
|
|
msg);
|
2017-07-30 14:55:00 +00:00
|
|
|
l1sap->u.data.chan_nr = RSL_CHAN_OSMO_PDCH | ts->nr;
|
2013-08-30 06:48:38 +00:00
|
|
|
l1sap->u.data.link_id = 0x00;
|
|
|
|
l1sap->u.data.fn = fn;
|
|
|
|
msg->l2h = msgb_put(msg, len);
|
|
|
|
memcpy(msg->l2h, data, len);
|
|
|
|
|
|
|
|
return l1sap_down(ts->trx, l1sap);
|
|
|
|
}
|
2013-09-01 07:19:45 +00:00
|
|
|
|
2013-09-01 08:08:15 +00:00
|
|
|
/*! \brief call-back function for incoming RTP */
|
|
|
|
void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
2016-10-21 17:53:34 +00:00
|
|
|
unsigned int rtp_pl_len, uint16_t seq_number,
|
|
|
|
uint32_t timestamp, bool marker)
|
2013-09-01 08:08:15 +00:00
|
|
|
{
|
|
|
|
struct gsm_lchan *lchan = rs->priv;
|
2017-06-23 19:08:09 +00:00
|
|
|
struct msgb *msg;
|
2013-09-01 08:08:15 +00:00
|
|
|
struct osmo_phsap_prim *l1sap;
|
|
|
|
|
2017-06-23 22:36:54 +00:00
|
|
|
/* if we're in loopback mode, we don't accept frames from the
|
|
|
|
* RTP socket anymore */
|
|
|
|
if (lchan->loopback)
|
|
|
|
return;
|
|
|
|
|
2013-09-01 08:08:15 +00:00
|
|
|
msg = l1sap_msgb_alloc(rtp_pl_len);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len);
|
|
|
|
msgb_pull(msg, sizeof(*l1sap));
|
|
|
|
|
2016-09-17 12:15:03 +00:00
|
|
|
/* Store RTP header Marker bit in control buffer */
|
|
|
|
rtpmsg_marker_bit(msg) = marker;
|
2016-11-28 16:27:05 +00:00
|
|
|
/* Store RTP header Sequence Number in control buffer */
|
|
|
|
rtpmsg_seq(msg) = seq_number;
|
|
|
|
/* Store RTP header Timestamp in control buffer */
|
|
|
|
rtpmsg_ts(msg) = timestamp;
|
2013-09-01 08:08:15 +00:00
|
|
|
|
2016-11-28 16:27:05 +00:00
|
|
|
/* make sure the queue doesn't get too long */
|
2017-06-23 19:08:09 +00:00
|
|
|
queue_limit_to(gsm_lchan_name(lchan), &lchan->dl_tch_queue, 1);
|
2013-09-01 08:08:15 +00:00
|
|
|
|
|
|
|
msgb_enqueue(&lchan->dl_tch_queue, msg);
|
|
|
|
}
|
|
|
|
|
2013-09-01 07:19:45 +00:00
|
|
|
static int l1sap_chan_act_dact_modify(struct gsm_bts_trx *trx, uint8_t chan_nr,
|
|
|
|
enum osmo_mph_info_type type, uint8_t sacch_only)
|
|
|
|
{
|
|
|
|
struct osmo_phsap_prim l1sap;
|
|
|
|
|
|
|
|
memset(&l1sap, 0, sizeof(l1sap));
|
|
|
|
osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_REQUEST,
|
|
|
|
NULL);
|
|
|
|
l1sap.u.info.type = type;
|
|
|
|
l1sap.u.info.u.act_req.chan_nr = chan_nr;
|
|
|
|
l1sap.u.info.u.act_req.sacch_only = sacch_only;
|
|
|
|
|
|
|
|
return l1sap_down(trx, &l1sap);
|
|
|
|
}
|
|
|
|
|
|
|
|
int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *tp)
|
|
|
|
{
|
|
|
|
struct gsm_bts_role_bts *btsb = trx->bts->role;
|
2015-12-05 10:54:08 +00:00
|
|
|
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
|
2013-09-01 07:19:45 +00:00
|
|
|
struct gsm48_chan_desc *cd;
|
|
|
|
int rc;
|
|
|
|
|
2017-12-02 16:31:45 +00:00
|
|
|
LOGP(DL1C, LOGL_INFO, "activating channel chan_nr=0x%02x trx=%d\n",
|
2013-09-01 07:19:45 +00:00
|
|
|
chan_nr, trx->nr);
|
|
|
|
|
2014-08-27 18:05:59 +00:00
|
|
|
/* osmo-pcu calls this without a valid 'tp' parameter, so we
|
|
|
|
* need to make sure ew don't crash here */
|
2013-09-01 07:19:45 +00:00
|
|
|
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)) {
|
2017-12-02 16:31:45 +00:00
|
|
|
LOGP(DL1C, LOGL_ERROR, "lchan TSC %u != BSIC-TSC %u\n",
|
2013-09-01 07:19:45 +00:00
|
|
|
cd->h0.tsc, lchan->ts->trx->bts->bsic & 7);
|
|
|
|
return -RSL_ERR_SERV_OPT_UNIMPL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lchan->sacch_deact = 0;
|
|
|
|
lchan->s = btsb->radio_link_timeout;
|
|
|
|
|
|
|
|
rc = l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_ACTIVATE, 0);
|
|
|
|
if (rc)
|
|
|
|
return -RSL_ERR_EQUIPMENT_FAIL;
|
2016-10-03 15:37:45 +00:00
|
|
|
|
|
|
|
/* Init DTX DL FSM if necessary */
|
DTX: avoid illegal character contained in DTX FSM allocation which causes BTS crash
Problem:
lchan->tch.dtx.dl_amr_fsm struct failed to allocate in l1sap_chan_act routine
in l1sap.c due to illegal characters contained in lchan->name which are passed to
osmo_fsm_inst_alloc routine. As a result, lchan->tch.dtx.dl_amr_fsm is NULL
causing BTS crashed (SEG FAULT) when trying to access this struct.
Below is snapshot of crash log obtained by GDB:
...
Fri Nov 24 18:13:55 2017 <0000> rsl.c:1653 payload type: 98
Fri Nov 24 18:13:55 2017 <0000> rsl.c:1463 (bts=0,trx=0,ts=2,ss=0)
RSL Tx IPAC_MDCX_ACK (local 127.0.0.1:11538, remote 127.0.0.1:30012)
Program received signal SIGSEGV, Segmentation fault.
0x00031930 in dtx_dl_amr_fsm_step (lchan=lchan@entry=0xb69592a8,
rtp_pl=rtp_pl@entry=0x87ae8 " \024\351Y\363_\337\345\351f\177\373\300\210\201\200\210",
rtp_pl_len=17, fn=1728481, l1_payload=0x10dd25 "", marker=marker@entry=true,
len=len@entry=0x10ddc4 "\024", ft_out=0xbefff7d7 "\002",
ft_out@entry=0xbefff7cf "\276\341_\032") at msg_utils.c:233
233 msg_utils.c: No such file or directory.
...
Fix:
* Use different formatting for lchan name passed to osmo_fsm_inst_alloc routine
* Refuse channel activation if FSM could not be generated (as opposed to crash)
Related: OS#2606
Reported-by: Minh-Quang Nguyen <minh-quang.nguyen@nutaq.com>
Change-Id: I929ce3703dc57acf8db569ae0e346265644d0b3c
2017-10-31 19:29:35 +00:00
|
|
|
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);
|
2016-10-28 14:52:48 +00:00
|
|
|
lchan->tch.dtx.dl_amr_fsm = osmo_fsm_inst_alloc(&dtx_dl_amr_fsm,
|
|
|
|
tall_bts_ctx,
|
|
|
|
lchan,
|
|
|
|
LOGL_DEBUG,
|
DTX: avoid illegal character contained in DTX FSM allocation which causes BTS crash
Problem:
lchan->tch.dtx.dl_amr_fsm struct failed to allocate in l1sap_chan_act routine
in l1sap.c due to illegal characters contained in lchan->name which are passed to
osmo_fsm_inst_alloc routine. As a result, lchan->tch.dtx.dl_amr_fsm is NULL
causing BTS crashed (SEG FAULT) when trying to access this struct.
Below is snapshot of crash log obtained by GDB:
...
Fri Nov 24 18:13:55 2017 <0000> rsl.c:1653 payload type: 98
Fri Nov 24 18:13:55 2017 <0000> rsl.c:1463 (bts=0,trx=0,ts=2,ss=0)
RSL Tx IPAC_MDCX_ACK (local 127.0.0.1:11538, remote 127.0.0.1:30012)
Program received signal SIGSEGV, Segmentation fault.
0x00031930 in dtx_dl_amr_fsm_step (lchan=lchan@entry=0xb69592a8,
rtp_pl=rtp_pl@entry=0x87ae8 " \024\351Y\363_\337\345\351f\177\373\300\210\201\200\210",
rtp_pl_len=17, fn=1728481, l1_payload=0x10dd25 "", marker=marker@entry=true,
len=len@entry=0x10ddc4 "\024", ft_out=0xbefff7d7 "\002",
ft_out@entry=0xbefff7cf "\276\341_\032") at msg_utils.c:233
233 msg_utils.c: No such file or directory.
...
Fix:
* Use different formatting for lchan name passed to osmo_fsm_inst_alloc routine
* Refuse channel activation if FSM could not be generated (as opposed to crash)
Related: OS#2606
Reported-by: Minh-Quang Nguyen <minh-quang.nguyen@nutaq.com>
Change-Id: I929ce3703dc57acf8db569ae0e346265644d0b3c
2017-10-31 19:29:35 +00:00
|
|
|
name);
|
|
|
|
if (!lchan->tch.dtx.dl_amr_fsm) {
|
|
|
|
l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE, 0);
|
|
|
|
return -RSL_ERR_EQUIPMENT_FAIL;
|
|
|
|
}
|
|
|
|
}
|
2013-09-01 07:19:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int l1sap_chan_rel(struct gsm_bts_trx *trx, uint8_t chan_nr)
|
|
|
|
{
|
2016-10-03 15:37:45 +00:00
|
|
|
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
|
2017-12-02 16:31:45 +00:00
|
|
|
LOGP(DL1C, LOGL_INFO, "deactivating channel chan_nr=0x%02x trx=%d\n",
|
2013-09-01 07:19:45 +00:00
|
|
|
chan_nr, trx->nr);
|
|
|
|
|
2016-10-03 15:37:45 +00:00
|
|
|
if (lchan->tch.dtx.dl_amr_fsm) {
|
|
|
|
osmo_fsm_inst_free(lchan->tch.dtx.dl_amr_fsm);
|
|
|
|
lchan->tch.dtx.dl_amr_fsm = NULL;
|
|
|
|
}
|
|
|
|
|
2013-09-01 07:19:45 +00:00
|
|
|
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE,
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int l1sap_chan_deact_sacch(struct gsm_bts_trx *trx, uint8_t chan_nr)
|
|
|
|
{
|
2015-12-05 10:54:08 +00:00
|
|
|
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
|
2013-09-01 07:19:45 +00:00
|
|
|
|
2017-12-02 16:31:45 +00:00
|
|
|
LOGP(DL1C, LOGL_INFO, "deactivating sacch chan_nr=0x%02x trx=%d\n",
|
2013-09-01 07:19:45 +00:00
|
|
|
chan_nr, trx->nr);
|
|
|
|
|
|
|
|
lchan->sacch_deact = 1;
|
|
|
|
|
|
|
|
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE,
|
|
|
|
1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int l1sap_chan_modify(struct gsm_bts_trx *trx, uint8_t chan_nr)
|
|
|
|
{
|
2017-12-02 16:31:45 +00:00
|
|
|
LOGP(DL1C, LOGL_INFO, "modifying channel chan_nr=0x%02x trx=%d\n",
|
2013-09-01 07:19:45 +00:00
|
|
|
chan_nr, trx->nr);
|
|
|
|
|
|
|
|
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_MODIFY, 0);
|
|
|
|
}
|