osmo-bts/src/common/l1sap.c

2007 lines
62 KiB
C
Raw Normal View History

/* 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>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <inttypes.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/l1sap.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/rsl.h>
#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>
#include <osmo-bts/dtx_dl_amr_fsm.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/measurement.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/oml.h>
#include <osmo-bts/abis.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/handover.h>
#include <osmo-bts/msg_utils.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 */
};
/* 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];
/* if FN is negative, we were called for something that's not CCCH! */
OSMO_ASSERT(rc >= 0);
return rc;
}
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);
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(ts->lchan));
return &ts->lchan[ss];
}
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);
if (lchan && lchan->state != LCHAN_S_ACTIVE) {
LOGPLCHAN(lchan, DL1P, LOGL_NOTICE, "assuming active lchan, but state is %s\n",
gsm_lchans_name(lchan->state));
return NULL;
}
return lchan;
}
static int l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap);
static uint32_t fn_ms_adj(uint32_t fn, const struct gsm_lchan *lchan)
{
uint32_t samples_passed, r;
if (lchan->tch.last_fn != LCHAN_FN_DUMMY) {
/* 12/13 frames usable for audio in TCH,
160 samples per RTP packet,
1 RTP packet per 4 frames */
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;
r -= r % GSM_RTP_DURATION;
if (r != GSM_RTP_DURATION)
LOGPLCHAN(lchan, DRTP, LOGL_ERROR, "RTP clock out of sync with lower layer:"
" %"PRIu32" vs %d (%"PRIu32"->%"PRIu32")\n",
r, GSM_RTP_DURATION, lchan->tch.last_fn, fn);
}
return GSM_RTP_DURATION;
}
/* allocate a msgb containing a osmo_phsap_prim + optional l2 data
* 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)
{
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)
return NULL;
msg->l1h = msgb_put(msg, sizeof(struct osmo_phsap_prim));
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, int8_t rssi,
int16_t ta_offs, uint8_t is_sub)
{
struct osmo_phsap_prim *l1sap;
LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "Rx -> RTP: %s\n", osmo_hexdump(rmsg->data, rmsg->len));
rmsg->l2h = rmsg->data;
rmsg->l1h = msgb_push(rmsg, sizeof(*l1sap));
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;
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);
}
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)
{
uint8_t n_s;
/* only do this if we are in the right state */
switch (lchan->ciph_state) {
case LCHAN_CIPH_NONE:
case LCHAN_CIPH_RX_REQ:
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;
/* Remember N(S) + 1 to find the first ciphered frame */
n_s = (msg->data[1] >> 1) & 0x7;
lchan->ciph_ns = (n_s + 1) % 8;
l1sap_tx_ciph_req(lchan->ts->trx, chan_nr, 0, 1);
return 1;
}
/* 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);
}
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(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;
l1sap: Avoid assumption that l1sap is at head of msgb This assumption used while sending the rx data to gsmtap in l1sap_up was making osmo-bts-virtual crash, since that bts model is allocating the l1sap in the stack rather than inside the msgb. Instead, let's use the assumption that l2h is set correctly in msgb by the bts model lower layer. crash report: Program received signal SIGSEGV, Segmentation fault. 0x00007ffff6234ec3 in __memmove_sse2_unaligned_erms () from /usr/lib/libc.so.6 (gdb) bt 0 0x00007ffff6234ec3 in __memmove_sse2_unaligned_erms () from /usr/lib/libc.so.6 1 0x00007ffff6dbf4c8 in gsmtap_makemsg_ex (type=<optimized out>, arfcn=arfcn@entry=17255, ts=ts@entry=6 '\006', chan_type=<optimized out>, ss=ss@entry=0 '\000', fn=fn@entry=11249, signal_dbm=0 '\000', snr=0 '\000', data=0x5555557d5b50 "", len=4294967263) at libosmocore/src/gsmtap_util.c:179 2 0x00007ffff6dbf6d8 in gsmtap_send_ex (gti=0x555555877f10, type=type@entry=1 '\001', arfcn=arfcn@entry=17255, ts=ts@entry=6 '\006', chan_type=<optimized out>, ss=<optimized out>, fn=11249, signal_dbm=0 '\000', snr=0 '\000', data=0x5555557d5b50 "", len=4294967263) at libosmocore/src/gsmtap_util.c:311 3 0x00007ffff6dbf765 in gsmtap_send (gti=<optimized out>, arfcn=arfcn@entry=17255, ts=ts@entry=6 '\006', chan_type=<optimized out>, ss=<optimized out>, fn=fn@entry=11249, signal_dbm=0 '\000', snr=0 '\000', data=0x5555557d5b50 "", len=4294967263) at libosmocore/src/gsmtap_util.c:330 4 0x0000555555573571 in to_gsmtap (trx=0x7ffff7ef8070, l1sap=0x7fffffffde80) at osmo-bts/src/common/l1sap.c:397 5 0x0000555555573b9c in l1sap_up (trx=0x7ffff7ef8070, l1sap=l1sap@entry=0x7fffffffde80) at osmo-bts/src/common/l1sap.c:1285 6 0x000055555555ec06 in virt_um_rcv_cb (vui=<optimized out>, msg=<optimized out>) at osmo-bts/src/osmo-bts-virtual/l1_if.c:170 7 0x000055555555f5c6 in virt_um_fd_cb (ofd=0x55555587cc30, what=<optimized out>) at osmo-bts/src/osmo-bts-virtual/virtual_um.c:50 8 0x00007ffff6db6991 in osmo_fd_disp_fds (_eset=0x7fffffffe090, _wset=0x7fffffffe010, _rset=0x7fffffffdf90) at libosmocore/src/select.c:216 9 osmo_select_main (polling=polling@entry=0) at libosmocore/src/select.c:256 10 0x0000555555576fbc in bts_main (argc=5, argv=0x7fffffffe288) at osmo-bts/src/common/main.c:364 11 0x00007ffff61b5f4a in __libc_start_main () from /usr/lib/libc.so.6 12 0x000055555555c4ca in _start () In the old code when the sizeof(osmo_phsap_prim) was being substracted it resulted on a negative len which later was casted to unsigned int and became a really big number. Fixes: OS#3092 Change-Id: I51a880328497673a06d153bfb76c428265b8cbb8
2018-03-21 18:51:46 +00:00
*data = msgb_l2(msg);
*len = msgb_l2len(msg);
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. */
if (l1sap_fn2ccch_block(fn) >= num_agch)
*chan_type = GSMTAP_CHANNEL_PCH;
else
*chan_type = GSMTAP_CHANNEL_AGCH;
} else if (L1SAP_IS_CHAN_CBCH(chan_nr)) {
*chan_type = GSMTAP_CHANNEL_CBCH51;
} else if (L1SAP_IS_CHAN_PDCH(chan_nr)) {
*chan_type = GSMTAP_CHANNEL_PDTCH;
}
if (L1SAP_IS_LINK_SACCH(link_id))
*chan_type |= GSMTAP_CHANNEL_ACCH;
return 0;
}
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;
l1sap: Avoid assumption that l1sap is at head of msgb This assumption used while sending the rx data to gsmtap in l1sap_up was making osmo-bts-virtual crash, since that bts model is allocating the l1sap in the stack rather than inside the msgb. Instead, let's use the assumption that l2h is set correctly in msgb by the bts model lower layer. crash report: Program received signal SIGSEGV, Segmentation fault. 0x00007ffff6234ec3 in __memmove_sse2_unaligned_erms () from /usr/lib/libc.so.6 (gdb) bt 0 0x00007ffff6234ec3 in __memmove_sse2_unaligned_erms () from /usr/lib/libc.so.6 1 0x00007ffff6dbf4c8 in gsmtap_makemsg_ex (type=<optimized out>, arfcn=arfcn@entry=17255, ts=ts@entry=6 '\006', chan_type=<optimized out>, ss=ss@entry=0 '\000', fn=fn@entry=11249, signal_dbm=0 '\000', snr=0 '\000', data=0x5555557d5b50 "", len=4294967263) at libosmocore/src/gsmtap_util.c:179 2 0x00007ffff6dbf6d8 in gsmtap_send_ex (gti=0x555555877f10, type=type@entry=1 '\001', arfcn=arfcn@entry=17255, ts=ts@entry=6 '\006', chan_type=<optimized out>, ss=<optimized out>, fn=11249, signal_dbm=0 '\000', snr=0 '\000', data=0x5555557d5b50 "", len=4294967263) at libosmocore/src/gsmtap_util.c:311 3 0x00007ffff6dbf765 in gsmtap_send (gti=<optimized out>, arfcn=arfcn@entry=17255, ts=ts@entry=6 '\006', chan_type=<optimized out>, ss=<optimized out>, fn=fn@entry=11249, signal_dbm=0 '\000', snr=0 '\000', data=0x5555557d5b50 "", len=4294967263) at libosmocore/src/gsmtap_util.c:330 4 0x0000555555573571 in to_gsmtap (trx=0x7ffff7ef8070, l1sap=0x7fffffffde80) at osmo-bts/src/common/l1sap.c:397 5 0x0000555555573b9c in l1sap_up (trx=0x7ffff7ef8070, l1sap=l1sap@entry=0x7fffffffde80) at osmo-bts/src/common/l1sap.c:1285 6 0x000055555555ec06 in virt_um_rcv_cb (vui=<optimized out>, msg=<optimized out>) at osmo-bts/src/osmo-bts-virtual/l1_if.c:170 7 0x000055555555f5c6 in virt_um_fd_cb (ofd=0x55555587cc30, what=<optimized out>) at osmo-bts/src/osmo-bts-virtual/virtual_um.c:50 8 0x00007ffff6db6991 in osmo_fd_disp_fds (_eset=0x7fffffffe090, _wset=0x7fffffffe010, _rset=0x7fffffffdf90) at libosmocore/src/select.c:216 9 osmo_select_main (polling=polling@entry=0) at libosmocore/src/select.c:256 10 0x0000555555576fbc in bts_main (argc=5, argv=0x7fffffffe288) at osmo-bts/src/common/main.c:364 11 0x00007ffff61b5f4a in __libc_start_main () from /usr/lib/libc.so.6 12 0x000055555555c4ca in _start () In the old code when the sizeof(osmo_phsap_prim) was being substracted it resulted on a negative len which later was casted to unsigned int and became a really big number. Fixes: OS#3092 Change-Id: I51a880328497673a06d153bfb76c428265b8cbb8
2018-03-21 18:51:46 +00:00
*data = msgb_l2(msg);
*len = msgb_l2len(msg);
if (L1SAP_IS_PTCCH(fn)) {
*chan_type = GSMTAP_CHANNEL_PTCCH;
*ss = L1SAP_FN2PTCCHBLOCK(fn);
} else {
/* TODO: distinguish PACCH */
*chan_type = GSMTAP_CHANNEL_PDTCH;
}
return 0;
}
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 = 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(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);
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;
}
/* 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)
{
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;
case GSMTAP_CHANNEL_PCH:
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;
}
static int to_gsmtap(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
{
uint8_t *data;
l1sap: Avoid assumption that l1sap is at head of msgb This assumption used while sending the rx data to gsmtap in l1sap_up was making osmo-bts-virtual crash, since that bts model is allocating the l1sap in the stack rather than inside the msgb. Instead, let's use the assumption that l2h is set correctly in msgb by the bts model lower layer. crash report: Program received signal SIGSEGV, Segmentation fault. 0x00007ffff6234ec3 in __memmove_sse2_unaligned_erms () from /usr/lib/libc.so.6 (gdb) bt 0 0x00007ffff6234ec3 in __memmove_sse2_unaligned_erms () from /usr/lib/libc.so.6 1 0x00007ffff6dbf4c8 in gsmtap_makemsg_ex (type=<optimized out>, arfcn=arfcn@entry=17255, ts=ts@entry=6 '\006', chan_type=<optimized out>, ss=ss@entry=0 '\000', fn=fn@entry=11249, signal_dbm=0 '\000', snr=0 '\000', data=0x5555557d5b50 "", len=4294967263) at libosmocore/src/gsmtap_util.c:179 2 0x00007ffff6dbf6d8 in gsmtap_send_ex (gti=0x555555877f10, type=type@entry=1 '\001', arfcn=arfcn@entry=17255, ts=ts@entry=6 '\006', chan_type=<optimized out>, ss=<optimized out>, fn=11249, signal_dbm=0 '\000', snr=0 '\000', data=0x5555557d5b50 "", len=4294967263) at libosmocore/src/gsmtap_util.c:311 3 0x00007ffff6dbf765 in gsmtap_send (gti=<optimized out>, arfcn=arfcn@entry=17255, ts=ts@entry=6 '\006', chan_type=<optimized out>, ss=<optimized out>, fn=fn@entry=11249, signal_dbm=0 '\000', snr=0 '\000', data=0x5555557d5b50 "", len=4294967263) at libosmocore/src/gsmtap_util.c:330 4 0x0000555555573571 in to_gsmtap (trx=0x7ffff7ef8070, l1sap=0x7fffffffde80) at osmo-bts/src/common/l1sap.c:397 5 0x0000555555573b9c in l1sap_up (trx=0x7ffff7ef8070, l1sap=l1sap@entry=0x7fffffffde80) at osmo-bts/src/common/l1sap.c:1285 6 0x000055555555ec06 in virt_um_rcv_cb (vui=<optimized out>, msg=<optimized out>) at osmo-bts/src/osmo-bts-virtual/l1_if.c:170 7 0x000055555555f5c6 in virt_um_fd_cb (ofd=0x55555587cc30, what=<optimized out>) at osmo-bts/src/osmo-bts-virtual/virtual_um.c:50 8 0x00007ffff6db6991 in osmo_fd_disp_fds (_eset=0x7fffffffe090, _wset=0x7fffffffe010, _rset=0x7fffffffdf90) at libosmocore/src/select.c:216 9 osmo_select_main (polling=polling@entry=0) at libosmocore/src/select.c:256 10 0x0000555555576fbc in bts_main (argc=5, argv=0x7fffffffe288) at osmo-bts/src/common/main.c:364 11 0x00007ffff61b5f4a in __libc_start_main () from /usr/lib/libc.so.6 12 0x000055555555c4ca in _start () In the old code when the sizeof(osmo_phsap_prim) was being substracted it resulted on a negative len which later was casted to unsigned int and became a really big number. Fixes: OS#3092 Change-Id: I51a880328497673a06d153bfb76c428265b8cbb8
2018-03-21 18:51:46 +00:00
unsigned int len;
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;
struct gsmtap_inst *inst = trx->bts->gsmtap.inst;
if (!inst)
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):
fn = l1sap->u.data.fn;
tn = L1SAP_CHAN2TS(l1sap->u.data.chan_nr);
if (ts_is_pdch(&trx->ts[tn]))
rc = gsmtap_pdch(l1sap, &chan_type, &ss, fn, &data,
&len);
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;
}
if (rc)
return rc;
if (len == 0)
return 0;
if ((chan_type & GSMTAP_CHANNEL_ACCH)) {
if (!trx->bts->gsmtap.sapi_acch)
return 0;
} else {
if (!((1 << (chan_type & 31)) & trx->bts->gsmtap.sapi_mask))
return 0;
}
/* 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;
gsmtap_send(inst, trx->arfcn | uplink, tn, chan_type, ss, fn,
signal_dbm, 0 /* TODO: SNR */, data, len);
return 0;
}
/* 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;
else if (ccch_conf == RSL_BCCH_CCCH_CONF_3_NC)
rach_frames_expired = 3;
else if (ccch_conf == RSL_BCCH_CCCH_CONF_4_NC)
rach_frames_expired = 4;
else
rach_frames_expired = 1;
}
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) {
/* 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)
{
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 = 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"=%u > 1!\n",
info_time_ind->fn, bts->gsm_time.fn, frames_expired);
}
/* Update our data structures with the current GSM time */
gsm_fn2gsmtime(&bts->gsm_time, info_time_ind->fn);
/* Update time on PCU interface */
pcu_tx_time_ind(info_time_ind->fn);
/* increment number of RACH slots that have passed by since the
* last time indication */
for (i = 0; i < frames_expired; i++) {
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;
}
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 */
LOGPLCHAN(lchan, 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;
}
}
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 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;
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;
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);
}
DEBUGPFN(DL1P, fn,
"%s %s meas ind, ta_offs_256bits=%d, ber10k=%d, inv_rssi=%u, C/I=%d cB\n",
gsm_lchan_name(lchan), 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, ulm.ta_offs_256bits / 256, true);
lchan_meas_process_measurement(lchan, &ulm, fn);
return;
}
/* 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) {
case PRIM_INFO_TIME:
if (trx != trx->bts->c0) {
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);
rc = -1;
} else
rc = l1sap_info_time_ind(trx->bts, l1sap,
&info->u.time_ind);
break;
case PRIM_INFO_MEAS:
/* 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",
info->type);
break;
}
return rc;
}
/* 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 = 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);
rsl_tx_chan_act_acknack(lchan, info_act_cnf->cause);
/* 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() */
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);
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 = 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);
rsl_tx_rf_rel_ack(lchan);
/* During PDCH DEACT, this marks the deactivation of the PDTCH as
* requested by the PCU. Next up, we disconnect the TS completely and
* call back to cb_ts_disconnected(). See rsl_rx_dyn_pdch(). */
if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH
&& (lchan->ts->flags & TS_F_PDCH_DEACT_PENDING))
bts_model_ts_disconnect(lchan->ts);
return 0;
}
/* 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)
{
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:
LOGP(DL1C, LOGL_NOTICE, "unknown MPH_INFO cnf type %d\n",
info->type);
break;
}
return rc;
}
/*! 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_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));
/* 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));
/* 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;
}
/* Check if given CCCH frame number is for a 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) {
/* 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
* transferred to osmo-bts via RSL */
return l1sap_fn2ccch_block(fn) < num_agch(trx, "PH-RTS-IND");
}
/* return the measured average of frame numbers that the RTS clock is running in advance */
int32_t bts_get_avg_fn_advance(const struct gsm_bts *bts)
{
if (bts->fn_stats.avg_count == 0)
return 0;
return bts->fn_stats.avg256 / bts->fn_stats.avg_count;
}
static void l1sap_update_fnstats(struct gsm_bts *bts, uint32_t rts_fn)
{
int32_t delta = GSM_TDMA_FN_SUB(rts_fn, bts->gsm_time.fn);
if (delta < bts->fn_stats.min)
bts->fn_stats.min = delta;
if (delta > bts->fn_stats.max)
bts->fn_stats.max = delta;
if (bts->fn_stats.avg_count > bts->fn_stats.avg_window) {
/* reset and start old average and new sample */
bts->fn_stats.avg256 = (bts->fn_stats.avg256 / bts->fn_stats.avg_count) + delta;
bts->fn_stats.avg_count = 2;
} else {
bts->fn_stats.avg256 += delta;
bts->fn_stats.avg_count++;
}
}
/* Common dequeueing function */
static inline struct msgb *lapdm_phsap_dequeue_msg(struct lapdm_entity *le)
{
struct osmo_phsap_prim pp;
if (lapdm_phsap_dequeue_prim(le, &pp) < 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(le, &pp) < 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)
{
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(le, &pp) < 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)
{
struct msgb *msg = l1sap->oph.msg;
struct gsm_time g_time;
struct gsm_lchan *lchan;
uint8_t chan_nr, link_id;
uint8_t tn;
uint32_t fn;
uint8_t *p = NULL;
uint8_t *si;
struct lapdm_entity *le;
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;
fn = rts_ind->fn;
tn = L1SAP_CHAN2TS(chan_nr);
gsm_fn2gsmtime(&g_time, fn);
DEBUGPGT(DL1P, &g_time, "Rx PH-RTS.ind chan_nr=%s link_id=0x%02xd\n", rsl_chan_nr_str(chan_nr), link_id);
l1sap_update_fnstats(trx->bts, fn);
/* reuse PH-RTS.ind for PH-DATA.req */
if (!msg) {
LOGPGT(DL1P, LOGL_FATAL, &g_time, "RTS without msg to be reused. Please fix!\n");
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);
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, trx->arfcn,
L1SAP_FN2PTCCHBLOCK(fn));
} else {
pcu_tx_rts_req(&trx->ts[tn], 0, fn, trx->arfcn,
L1SAP_FN2MACBLOCK(fn));
}
/* return early, PCU takes care of rest */
return 0;
}
} else if (L1SAP_IS_CHAN_BCCH(chan_nr)) {
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);
} else if (L1SAP_IS_CHAN_CBCH(chan_nr)) {
p = msgb_put(msg, GSM_MACBLOCK_LEN);
bts_cbch_get(trx->bts, p, &g_time);
} else if (!(chan_nr & 0x80)) { /* only TCH/F, TCH/H, SDCCH/4 and SDCCH/8 have C5 bit cleared */
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;
}
if (lchan->pending_rel_ind_msg) {
LOGPGT(DRSL, LOGL_INFO, &g_time,
"%s Forward RLL RELease INDication to the BSC\n",
gsm_lchan_name(lchan));
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;
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);
} else {
pp_msg = lapdm_phsap_dequeue_msg(le);
}
} 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);
}
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);
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_SDCCH4(chan_nr) || L1SAP_IS_CHAN_SDCCH8(chan_nr) ||
(lchan->rsl_cmode == RSL_CMOD_SPD_SIGN && !lchan->ts->trx->bts->dtxd)) {
/*
* SDCCH or TCH in signalling mode without DTX.
*
* Send fill frame according to GSM 05.08, section 8.3: "On the SDCCH and on the
* half rate speech traffic channel in signalling only mode DTX is not allowed.
* In these cases and during signalling on the TCH when DTX is not used, the same
* L2 fill frame shall be transmitted in case there is nothing else to transmit."
*/
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_msg->data + 2, GSM_MACBLOCK_LEN - 2);
else {
p = msgb_put(msg, 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_msg, lchan, chan_nr);
if (dtxd_facch)
dtx_dispatch(lchan, E_FACCH);
}
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);
if (rc <= 0)
memcpy(p, fill_frame, GSM_MACBLOCK_LEN);
}
DEBUGPGT(DL1P, &g_time, "Tx PH-DATA.req chan_nr=%s link_id=0x%02x\n", rsl_chan_nr_str(chan_nr), link_id);
l1sap_down(trx, l1sap);
/* don't free, because we forwarded data */
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)
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)) {
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;
}
return true;
}
/* 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)
{
struct msgb *resp_msg;
struct osmo_phsap_prim *resp_l1sap, empty_l1sap;
struct gsm_time g_time;
struct gsm_lchan *lchan;
uint8_t chan_nr, marker = 0;
uint32_t fn;
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;
}
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_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;
resp_l1sap = &empty_l1sap;
} else {
/* Obtain RTP header Marker bit from control buffer */
marker = rtpmsg_marker_bit(resp_msg);
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);
}
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;
resp_l1sap->u.tch.marker = marker;
DEBUGPGT(DL1P, &g_time, "Tx TCH.req chan_nr=%s\n", rsl_chan_nr_str(chan_nr));
l1sap_down(trx, resp_l1sap);
return 0;
}
/* 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, bool bad_frame)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
/* Bypass radio link timeout if set to -1 */
if (bts->radio_link_timeout.current < 0)
return;
/* if link loss criterion already reached */
if (lchan->s == 0) {
LOGPLCHAN(lchan, DMEAS, LOGL_DEBUG,
"radio link timeout counter S is already 0\n");
return;
}
if (bad_frame) {
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.current) {
/* count up radio link counter 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;
}
}
static inline int check_for_first_ciphrd(struct gsm_lchan *lchan,
uint8_t *data, int len)
{
uint8_t n_r;
/* 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_r = data[1] >> 5;
if (lchan->ciph_ns != n_r)
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);
}
/* 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)
{
struct msgb *msg = l1sap->oph.msg;
struct gsm_time g_time;
struct gsm_lchan *lchan;
struct lapdm_entity *le;
uint8_t *data = msg->l2h;
int len = msgb_l2len(msg);
uint8_t chan_nr, link_id;
uint8_t tn;
uint32_t fn;
enum osmo_ph_pres_info_type pr_info = data_ind->pdch_presence_info;
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);
DEBUGPGT(DL1P, &g_time, "Rx PH-DATA.ind chan_nr=%s link_id=0x%02x len=%d\n",
rsl_chan_nr_str(chan_nr), link_id, len);
if (ts_is_pdch(&trx->ts[tn])) {
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) {
/* we are in loopback mode (for BER testing)
* mode and need to enqeue the frame to be
* returned in downlink */
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;
}
/* 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)) {
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;
}
lchan = get_active_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));
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) {
LOGPGT(DL1P, LOGL_INFO, &g_time, "%s Lost SACCH block\n",
gsm_lchan_name(lchan));
radio_link_timeout(lchan, true);
} else {
radio_link_timeout(lchan, false);
}
/* Trigger the measurement reporting/processing logic */
lchan_meas_handle_sacch(lchan, msg);
}
/* bad frame */
if (len == 0)
return -EINVAL;
/* report first valid received frame to handover process */
if (lchan->ho.active == HANDOVER_WAIT_FRAME)
handover_frame(lchan);
if (L1SAP_IS_LINK_SACCH(link_id))
le = &lchan->lapdm_ch.lapdm_acch;
else
le = &lchan->lapdm_ch.lapdm_dcch;
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_to_l2(msg);
lapdm_phsap_up(&l1sap->oph, le);
/* don't free, because we forwarded data */
return 1;
}
/* 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 gsm_bts *bts = trx->bts;
struct msgb *msg = l1sap->oph.msg;
struct gsm_time g_time;
struct gsm_lchan *lchan;
uint8_t chan_nr;
uint32_t fn;
chan_nr = tch_ind->chan_nr;
fn = tch_ind->fn;
gsm_fn2gsmtime(&g_time, fn);
LOGPGT(DL1P, LOGL_DEBUG, &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;
}
/* 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) {
/* hand msg to RTP code for transmission */
if (lchan->abis_ip.osmux.use) {
lchan_osmux_send_frame(lchan, msg->data, msg->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,
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) {
/* 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.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;
}
lchan->tch.last_fn = fn;
return 0;
}
#define RACH_MIN_TOA256 -2 * 256
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_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;
}
/**
* Make sure that ToA (Timing of Arrival) is acceptable.
* We allow early arrival up to 2 symbols, and delay
* according to maximal allowed Timing Advance value.
*/
if (toa256 < RACH_MIN_TOA256 || toa256 > bts->max_ta * 256) {
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_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;
}
return true;
}
/* Special case where handover RACH is detected */
static int l1sap_handover_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, "handover")) {
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);
/* 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)) {
2019-10-02 16:00:54 +00:00
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,
2019-10-02 16:00:54 +00:00
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;
2019-10-02 16:00:54 +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 lapdm_channel *lc;
DEBUGPFN(DL1P, rach_ind->fn, "Rx PH-RA.ind\n");
2019-10-02 16:00:54 +00:00
/* 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:
rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_HO);
return l1sap_handover_rach(trx, rach_ind);
}
rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_RCVD);
/* increment number of busy RACH slots, if required */
if (rach_ind->rssi >= bts->load.rach.busy_thresh)
bts->load.rach.busy++;
/* Filter out noise / interference / ghosts */
if (!rach_pass_filter(rach_ind, bts, "CCCH")) {
rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_DROP);
return 0;
}
/* increment number of RACH slots with valid non-handover RACH burst */
bts->load.rach.access++;
lc = &trx->ts[0].lchan[CCCH_LCHAN].lapdm_ch;
/* According to 3GPP TS 48.058 § 9.3.17 Access Delay is expressed same way as TA (number of symbols) */
set_ms_to_data(get_lchan_by_chan_nr(trx, rach_ind->chan_nr),
rach_ind->acc_delay, false);
/* check for packet access */
if ((trx == bts->c0 && L1SAP_IS_PACKET_RACH(rach_ind->ra)) ||
(trx == bts->c0 && rach_ind->is_11bit)) {
rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_PS);
LOGPFN(DL1P, LOGL_INFO, rach_ind->fn, "RACH for packet access (toa=%d, ra=%d)\n",
rach_ind->acc_delay, rach_ind->ra);
/* 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,
2019-10-02 16:00:54 +00:00
rach_ind->ra, rach_ind->fn, rach_ind->is_11bit,
rach_ind->burst_type, PCU_IF_SAPI_RACH);
return 0;
}
LOGPFN(DL1P, LOGL_INFO, rach_ind->fn, "RACH for RR access (toa=%d, ra=%d)\n",
rach_ind->acc_delay, rach_ind->ra);
rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_CS);
lapdm_phsap_up(&l1sap->oph, &lc->lapdm_dcch);
return 0;
}
l1sap: Avoid assumption that l1sap is at head of msgb This assumption used while sending the rx data to gsmtap in l1sap_up was making osmo-bts-virtual crash, since that bts model is allocating the l1sap in the stack rather than inside the msgb. Instead, let's use the assumption that l2h is set correctly in msgb by the bts model lower layer. crash report: Program received signal SIGSEGV, Segmentation fault. 0x00007ffff6234ec3 in __memmove_sse2_unaligned_erms () from /usr/lib/libc.so.6 (gdb) bt 0 0x00007ffff6234ec3 in __memmove_sse2_unaligned_erms () from /usr/lib/libc.so.6 1 0x00007ffff6dbf4c8 in gsmtap_makemsg_ex (type=<optimized out>, arfcn=arfcn@entry=17255, ts=ts@entry=6 '\006', chan_type=<optimized out>, ss=ss@entry=0 '\000', fn=fn@entry=11249, signal_dbm=0 '\000', snr=0 '\000', data=0x5555557d5b50 "", len=4294967263) at libosmocore/src/gsmtap_util.c:179 2 0x00007ffff6dbf6d8 in gsmtap_send_ex (gti=0x555555877f10, type=type@entry=1 '\001', arfcn=arfcn@entry=17255, ts=ts@entry=6 '\006', chan_type=<optimized out>, ss=<optimized out>, fn=11249, signal_dbm=0 '\000', snr=0 '\000', data=0x5555557d5b50 "", len=4294967263) at libosmocore/src/gsmtap_util.c:311 3 0x00007ffff6dbf765 in gsmtap_send (gti=<optimized out>, arfcn=arfcn@entry=17255, ts=ts@entry=6 '\006', chan_type=<optimized out>, ss=<optimized out>, fn=fn@entry=11249, signal_dbm=0 '\000', snr=0 '\000', data=0x5555557d5b50 "", len=4294967263) at libosmocore/src/gsmtap_util.c:330 4 0x0000555555573571 in to_gsmtap (trx=0x7ffff7ef8070, l1sap=0x7fffffffde80) at osmo-bts/src/common/l1sap.c:397 5 0x0000555555573b9c in l1sap_up (trx=0x7ffff7ef8070, l1sap=l1sap@entry=0x7fffffffde80) at osmo-bts/src/common/l1sap.c:1285 6 0x000055555555ec06 in virt_um_rcv_cb (vui=<optimized out>, msg=<optimized out>) at osmo-bts/src/osmo-bts-virtual/l1_if.c:170 7 0x000055555555f5c6 in virt_um_fd_cb (ofd=0x55555587cc30, what=<optimized out>) at osmo-bts/src/osmo-bts-virtual/virtual_um.c:50 8 0x00007ffff6db6991 in osmo_fd_disp_fds (_eset=0x7fffffffe090, _wset=0x7fffffffe010, _rset=0x7fffffffdf90) at libosmocore/src/select.c:216 9 osmo_select_main (polling=polling@entry=0) at libosmocore/src/select.c:256 10 0x0000555555576fbc in bts_main (argc=5, argv=0x7fffffffe288) at osmo-bts/src/common/main.c:364 11 0x00007ffff61b5f4a in __libc_start_main () from /usr/lib/libc.so.6 12 0x000055555555c4ca in _start () In the old code when the sizeof(osmo_phsap_prim) was being substracted it resulted on a negative len which later was casted to unsigned int and became a really big number. Fixes: OS#3092 Change-Id: I51a880328497673a06d153bfb76c428265b8cbb8
2018-03-21 18:51:46 +00:00
/* Process any L1 prim received from bts model.
*
* This function takes ownership of the msgb.
* If l1sap contains a msgb, it assumes that msgb->l2h was set by lower layer.
*/
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)) {
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_INDICATION):
rc = l1sap_mph_info_ind(trx, l1sap, &l1sap->u.info);
break;
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_CONFIRM):
rc = l1sap_mph_info_cnf(trx, l1sap, &l1sap->u.info);
break;
case OSMO_PRIM(PRIM_PH_RTS, PRIM_OP_INDICATION):
rc = l1sap_ph_rts_ind(trx, l1sap, &l1sap->u.data);
break;
case OSMO_PRIM(PRIM_TCH_RTS, PRIM_OP_INDICATION):
rc = l1sap_tch_rts_ind(trx, l1sap, &l1sap->u.tch);
break;
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_INDICATION):
to_gsmtap(trx, l1sap);
rc = l1sap_ph_data_ind(trx, l1sap, &l1sap->u.data);
break;
case OSMO_PRIM(PRIM_TCH, PRIM_OP_INDICATION):
rc = l1sap_tch_ind(trx, l1sap, &l1sap->u.tch);
break;
case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_INDICATION):
to_gsmtap(trx, l1sap);
rc = l1sap_ph_rach_ind(trx, l1sap, &l1sap->u.rach_ind);
break;
default:
LOGP(DL1P, LOGL_NOTICE, "unknown prim %d op %d\n",
l1sap->oph.primitive, l1sap->oph.operation);
oml_tx_failure_event_rep(&trx->mo, NM_SEVER_MAJOR, OSMO_EVT_MAJ_UKWN_MSG,
"unknown prim %d op %d",
l1sap->oph.primitive,
l1sap->oph.operation);
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)
{
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);
return bts_model_l1sap_down(trx, l1sap);
}
/* 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, const uint8_t *data, uint8_t len)
{
struct msgb *msg;
struct osmo_phsap_prim *l1sap;
struct gsm_time g_time;
gsm_fn2gsmtime(&g_time, fn);
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);
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);
l1sap->u.data.chan_nr = RSL_CHAN_OSMO_PDCH | ts->nr;
l1sap->u.data.link_id = 0x00;
l1sap->u.data.fn = fn;
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);
}
/*! \brief call-back function for incoming RTP */
void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
unsigned int rtp_pl_len, uint16_t seq_number,
uint32_t timestamp, bool marker)
{
struct gsm_lchan *lchan = rs->priv;
struct msgb *msg;
struct osmo_phsap_prim *l1sap;
/* if we're in loopback mode, we don't accept frames from the
* RTP socket anymore */
if (lchan->loopback)
return;
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));
/* Store RTP header Marker bit in control buffer */
rtpmsg_marker_bit(msg) = marker;
/* Store RTP header Sequence Number in control buffer */
rtpmsg_seq(msg) = seq_number;
/* Store RTP header Timestamp in control buffer */
rtpmsg_ts(msg) = timestamp;
/* make sure the queue doesn't get too long */
lchan_dl_tch_queue_enqueue(lchan, msg, 1);
}
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 gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
int rc;
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;
}
LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Activating channel %s\n", rsl_chan_nr_str(chan_nr));
lchan->s = trx->bts->radio_link_timeout.current;
rc = l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_ACTIVATE, 0);
if (rc)
return -RSL_ERR_EQUIPMENT_FAIL;
/* Init DTX DL FSM if necessary */
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,
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;
}
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);
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;
}
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)
{
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
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);
}
int l1sap_chan_modify(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, "Modifying channel %s\n",
rsl_chan_nr_str(chan_nr));
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_MODIFY, 0);
}