aboutsummaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2015-09-22 16:39:55 +0200
committerHarald Welte <laforge@gnumonks.org>2015-09-22 16:39:55 +0200
commit329085a8ff2c1162a32eb617068fa5614efcde06 (patch)
tree290806d3a1bc7f64a0e7a8e1cc5b48bfd6578164 /src/common
parent1eaa3d72eaa10bb854b2adff48d3f7f3e06dcb8c (diff)
parent819b50e1a7b506a0a394cc71a795f0a9ce4083c1 (diff)
Merge branch '201509-l1sap'
Diffstat (limited to 'src/common')
-rw-r--r--src/common/Makefile.am2
-rw-r--r--src/common/bts.c8
-rw-r--r--src/common/handover.c3
-rw-r--r--src/common/l1sap.c1082
-rw-r--r--src/common/pcu_sock.c15
-rw-r--r--src/common/power_control.c99
-rw-r--r--src/common/rsl.c31
-rw-r--r--src/common/vty.c144
8 files changed, 1356 insertions, 28 deletions
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index e45e2c1d..d2580f93 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -8,4 +8,4 @@ libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \
load_indication.c pcu_sock.c handover.c msg_utils.c \
load_indication.c pcu_sock.c handover.c msg_utils.c \
tx_power.c bts_ctrl_commands.c bts_ctrl_lookup.c \
- cbch.c
+ l1sap.c cbch.c power_control.c
diff --git a/src/common/bts.c b/src/common/bts.c
index 65b115a8..77302e2f 100644
--- a/src/common/bts.c
+++ b/src/common/bts.c
@@ -99,6 +99,7 @@ int bts_init(struct gsm_bts *bts)
/* configurable via VTY */
btsb->paging_state = paging_init(btsb, 200, 0);
+ btsb->ul_power_target = -75; /* dBm default */
/* configurable via OML */
btsb->load.ccch.load_ind_period = 112;
@@ -611,3 +612,10 @@ int trx_ms_pwr_ctrl_is_osmo(struct gsm_bts_trx *trx)
{
return trx->ms_power_control == 1;
}
+
+struct gsm_time *get_time(struct gsm_bts *bts)
+{
+ struct gsm_bts_role_bts *btsb = bts->role;
+
+ return &btsb->gsm_time;
+}
diff --git a/src/common/handover.c b/src/common/handover.c
index 26a3f51a..03433ea5 100644
--- a/src/common/handover.c
+++ b/src/common/handover.c
@@ -33,6 +33,7 @@
#include <osmo-bts/rsl.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/handover.h>
+#include <osmo-bts/l1sap.h>
/* Transmit a handover related PHYS INFO on given lchan */
static int ho_tx_phys_info(struct gsm_lchan *lchan)
@@ -114,7 +115,7 @@ void handover_rach(struct gsm_lchan *lchan, uint8_t ra, uint8_t acc_delay)
/* Stop handover detection, wait for valid frame */
lchan->ho.active = HANDOVER_WAIT_FRAME;
- if (bts_model_rsl_chan_mod(lchan) != 0) {
+ if (l1sap_chan_modify(lchan->ts->trx, gsm_lchan2chan_nr(lchan)) != 0) {
LOGP(DHO, LOGL_ERROR,
"%s failed to modify channel after handover\n",
gsm_lchan_name(lchan));
diff --git a/src/common/l1sap.c b/src/common/l1sap.c
new file mode 100644
index 00000000..1cb752e3
--- /dev/null
+++ b/src/common/l1sap.c
@@ -0,0 +1,1082 @@
+/* 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 <sys/types.h>
+#include <sys/stat.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/l1sap.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/pcu_if.h>
+#include <osmo-bts/measurement.h>
+#include <osmo-bts/bts.h>
+#include <osmo-bts/rsl.h>
+#include <osmo-bts/bts_model.h>
+#include <osmo-bts/handover.h>
+
+static int l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap);
+
+static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = {
+ 0x03, 0x03, 0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
+ 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
+ 0x2B, 0x2B, 0x2B
+};
+
+/* 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)
+{
+ struct msgb *msg = msgb_alloc_headroom(512, 128, "l1sap_prim");
+
+ if (!msg)
+ return NULL;
+
+ msg->l1h = msgb_put(msg, sizeof(struct osmo_phsap_prim));
+
+ return msg;
+}
+
+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)
+{
+
+ /* only do this if we are in the right state */
+ switch (lchan->ciph_state) {
+ case LCHAN_CIPH_NONE:
+ 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;
+
+ l1sap_tx_ciph_req(lchan->ts->trx, chan_nr, 0, 1);
+
+ return 1;
+}
+
+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,
+ uint8_t *tn, uint8_t *ss, uint32_t *fn, uint8_t **data, int *len)
+{
+ 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);
+ *fn = l1sap->u.data.fn;
+ *tn = L1SAP_CHAN2TS(l1sap->u.data.chan_nr);
+ 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)) {
+#warning Set BS_AG_BLKS_RES
+ /* The sapi depends on DSP configuration, not
+ * on the actual SYSTEM INFORMATION 3. */
+ if (L1SAP_FN2CCCHBLOCK(*fn) >= 1)
+ *chan_type = GSMTAP_CHANNEL_PCH;
+ else
+ *chan_type = GSMTAP_CHANNEL_AGCH;
+ }
+ 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,
+ uint8_t *tn, uint8_t *ss, uint32_t *fn, uint8_t **data, int *len)
+{
+ struct msgb *msg = l1sap->oph.msg;
+
+ *data = msg->data + sizeof(struct osmo_phsap_prim);
+ *len = msg->len - sizeof(struct osmo_phsap_prim);
+ *fn = l1sap->u.data.fn;
+ *tn = L1SAP_CHAN2TS(l1sap->u.data.chan_nr);
+
+ if (L1SAP_IS_PTCCH(*fn)) {
+ *chan_type = GSMTAP_CHANNEL_PTCCH;
+ *ss = L1SAP_FN2PTCCHBLOCK(*fn);
+ 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);
+ *data = &l1sap->u.rach_ind.ra;
+ *len = 1;
+
+ return 0;
+}
+
+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):
+ if (trx->ts[tn].pchan == GSM_PCHAN_PDCH)
+ rc = gsmtap_pdch(l1sap, &chan_type, &tn, &ss, &fn, &data,
+ &len);
+ else
+ rc = gsmtap_ph_data(l1sap, &chan_type, &tn, &ss, &fn,
+ &data, &len);
+ 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;
+ }
+
+ gsmtap_send(gsmtap, trx->arfcn | uplink, tn, chan_type, ss, fn, 0, 0,
+ data, len);
+
+ return 0;
+}
+
+/* time information received from bts model */
+static int l1sap_info_time_ind(struct gsm_bts_trx *trx,
+ struct osmo_phsap_prim *l1sap,
+ struct info_time_ind_param *info_time_ind)
+{
+ struct gsm_bts *bts = trx->bts;
+ struct gsm_bts_role_bts *btsb = bts->role;
+
+ int frames_expired = info_time_ind->fn - btsb->gsm_time.fn;
+
+ DEBUGP(DL1P, "MPH_INFO time ind %u\n", info_time_ind->fn);
+
+ /* 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);
+
+ /* check if the measurement period of some lchan has ended
+ * and pre-compute the respective measurement */
+ trx_meas_check_compute(trx, info_time_ind->fn - 1);
+
+ /* increment number of RACH slots that have passed by since the
+ * last time indication */
+ if (trx == bts->c0) {
+ unsigned int num_rach_per_frame;
+ /* 27 / 51 taken from TS 05.01 Figure 3 */
+ if (bts->c0->ts[0].pchan == GSM_PCHAN_CCCH_SDCCH4)
+ num_rach_per_frame = 27;
+ else
+ num_rach_per_frame = 51;
+
+ btsb->load.rach.total += frames_expired * num_rach_per_frame;
+ }
+
+ return 0;
+}
+
+/* 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;
+
+ DEBUGP(DL1P, "MPH_INFO meas ind chan_nr=%02x\n",
+ info_meas_ind->chan_nr);
+
+ lchan = &trx->ts[L1SAP_CHAN2TS(info_meas_ind->chan_nr)]
+ .lchan[l1sap_chan2ss(info_meas_ind->chan_nr)];
+
+ /* 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));
+ ulm.ta_offs_qbits = info_meas_ind->ta_offs_qbits;
+ ulm.ber10k = info_meas_ind->ber10k;
+ ulm.inv_rssi = info_meas_ind->inv_rssi;
+
+ lchan_new_ul_meas(lchan, &ulm);
+
+ return 0;
+}
+
+/* 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:
+ rc = l1sap_info_time_ind(trx, l1sap, &info->u.time_ind);
+ break;
+ case PRIM_INFO_MEAS:
+ rc = l1sap_info_meas_ind(trx, l1sap, &info->u.meas_ind);
+ 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;
+
+ LOGP(DL1P, LOGL_INFO, "activate confirm chan_nr=%02x trx=%d\n",
+ info_act_cnf->chan_nr, trx->nr);
+
+ lchan = &trx->ts[L1SAP_CHAN2TS(info_act_cnf->chan_nr)]
+ .lchan[l1sap_chan2ss(info_act_cnf->chan_nr)];
+
+ if (info_act_cnf->cause)
+ rsl_tx_chan_act_nack(lchan, info_act_cnf->cause);
+ else
+ rsl_tx_chan_act_ack(lchan);
+
+ 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;
+
+ LOGP(DL1P, LOGL_INFO, "deactivate confirm chan_nr=%02x trx=%d\n",
+ info_act_cnf->chan_nr, trx->nr);
+
+ lchan = &trx->ts[L1SAP_CHAN2TS(info_act_cnf->chan_nr)]
+ .lchan[l1sap_chan2ss(info_act_cnf->chan_nr)];
+
+ rsl_tx_rf_rel_ack(lchan);
+
+ 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:
+ LOGP(DL1P, LOGL_NOTICE, "unknown MPH_INFO cnf type %d\n",
+ info->type);
+ break;
+ }
+
+ return rc;
+}
+
+/* PH-RTS-IND prim recevied 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, ss;
+ uint32_t fn;
+ uint8_t *p, *si;
+ struct lapdm_entity *le;
+ struct osmo_phsap_prim pp;
+ int rc;
+
+ 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);
+
+ DEBUGP(DL1P, "Rx PH-RTS.ind %02u/%02u/%02u chan_nr=%d link_id=%d\n",
+ g_time.t1, g_time.t2, g_time.t3, chan_nr, link_id);
+
+ if (trx->ts[tn].pchan == GSM_PCHAN_PDCH) {
+ if (L1SAP_IS_PTCCH(rts_ind->fn)) {
+ pcu_tx_rts_req(&trx->ts[tn], 1, fn, 1 /* ARFCN */,
+ L1SAP_FN2PTCCHBLOCK(fn));
+
+ return 0;
+ }
+ pcu_tx_rts_req(&trx->ts[tn], 0, fn, 0 /* ARFCN */,
+ L1SAP_FN2MACBLOCK(fn));
+
+ return 0;
+ }
+
+ /* reuse PH-RTS.ind for PH-DATA.req */
+ if (!msg) {
+ LOGP(DL1P, LOGL_FATAL, "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 (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 (!(chan_nr & 0x80)) { /* only TCH/F, TCH/H, SDCCH/4 and SDCCH/8 have C5 bit cleared */
+ if (L1SAP_IS_CHAN_TCHH(chan_nr))
+ ss = L1SAP_CHAN2SS_TCHH(chan_nr); /* TCH/H */
+ else if (L1SAP_IS_CHAN_SDCCH4(chan_nr))
+ ss = L1SAP_CHAN2SS_SDCCH4(chan_nr); /* SDCCH/4 */
+ else if (L1SAP_IS_CHAN_SDCCH8(chan_nr))
+ ss = L1SAP_CHAN2SS_SDCCH8(chan_nr); /* SDCCH/8 */
+ else
+ ss = 0; /* TCH/F */
+ lchan = &trx->ts[tn].lchan[ss];
+ if (L1SAP_IS_LINK_SACCH(link_id)) {
+ p = msgb_put(msg, GSM_MACBLOCK_LEN);
+ /* L1-header, if not set/modified by layer 1 */
+ p[0] = lchan->ms_power_ctrl.current;
+ p[1] = lchan->rqd_ta;
+ le = &lchan->lapdm_ch.lapdm_acch;
+ } else
+ le = &lchan->lapdm_ch.lapdm_dcch;
+ 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);
+ }
+ msgb_free(pp.oph.msg);
+ }
+ } else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) {
+ p = msgb_put(msg, GSM_MACBLOCK_LEN);
+#warning "TODO: Yet another assumption that BS_AG_BLKS_RES=1"
+ /* if CCCH block is 0, it is AGCH */
+ rc = bts_ccch_copy_msg(trx->bts, p, &g_time,
+ (L1SAP_FN2CCCHBLOCK(fn) < 1));
+ if (rc <= 0)
+ memcpy(p, fill_frame, GSM_MACBLOCK_LEN);
+ }
+
+ DEBUGP(DL1P, "Tx PH-DATA.req %02u/%02u/%02u chan_nr=%d link_id=%d\n",
+ g_time.t1, g_time.t2, g_time.t3, chan_nr, link_id);
+
+ l1sap_down(trx, l1sap);
+
+ /* don't free, because we forwarded data */
+ return 1;
+}
+
+static int check_acc_delay(struct ph_rach_ind_param *rach_ind,
+ struct gsm_bts_role_bts *btsb, uint8_t *acc_delay)
+{
+ *acc_delay = rach_ind->acc_delay;
+ return *acc_delay <= btsb->max_ta;
+}
+
+/* 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;
+ uint8_t chan_nr;
+ uint8_t tn, ss;
+
+ chan_nr = rach_ind->chan_nr;
+ tn = L1SAP_CHAN2TS(chan_nr);
+ ss = l1sap_chan2ss(chan_nr);
+ lchan = &trx->ts[tn].lchan[ss];
+
+ 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;
+}
+
+/* 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;
+ uint8_t chan_nr;
+ uint8_t tn, ss;
+ uint32_t fn;
+
+ chan_nr = rts_ind->chan_nr;
+ fn = rts_ind->fn;
+ tn = L1SAP_CHAN2TS(chan_nr);
+
+ gsm_fn2gsmtime(&g_time, fn);
+
+ DEBUGP(DL1P, "Rx TCH-RTS.ind %02u/%02u/%02u chan_nr=%d\n",
+ g_time.t1, g_time.t2, g_time.t3, chan_nr);
+
+ /* get timeslot and subslot */
+ tn = L1SAP_CHAN2TS(chan_nr);
+ if (L1SAP_IS_CHAN_TCHH(chan_nr))
+ ss = L1SAP_CHAN2SS_TCHH(chan_nr); /* TCH/H */
+ else
+ ss = 0; /* TCH/F */
+ lchan = &trx->ts[tn].lchan[ss];
+
+ 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) {
+ LOGP(DL1P, LOGL_DEBUG, "%s DL TCH Tx queue underrun\n",
+ gsm_lchan_name(lchan));
+ resp_l1sap = &empty_l1sap;
+ } else {
+ 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;
+
+ DEBUGP(DL1P, "Tx TCH.req %02u/%02u/%02u chan_nr=%d\n",
+ g_time.t1, g_time.t2, g_time.t3, chan_nr);
+
+ l1sap_down(trx, resp_l1sap);
+
+ return 0;
+}
+
+/* process radio link timeout counter S */
+static void radio_link_timeout(struct gsm_lchan *lchan, int bad_frame)
+{
+ struct gsm_bts_role_bts *btsb = lchan->ts->trx->bts->role;
+
+ /* 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);
+ }
+}
+
+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);
+}
+
+/* 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, ss;
+ uint32_t fn;
+ int8_t rssi;
+
+ 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);
+ ss = l1sap_chan2ss(chan_nr);
+
+ gsm_fn2gsmtime(&g_time, fn);
+
+ DEBUGP(DL1P, "Rx PH-DATA.ind %02u/%02u/%02u chan_nr=%d link_id=%d\n",
+ g_time.t1, g_time.t2, g_time.t3, chan_nr, link_id);
+
+ if (trx->ts[tn].pchan == GSM_PCHAN_PDCH) {
+ if (len == 0)
+ return -EINVAL;
+ if (L1SAP_IS_PTCCH(fn)) {
+ pcu_tx_data_ind(&trx->ts[tn], 1, fn,
+ 0 /* ARFCN */, L1SAP_FN2PTCCHBLOCK(fn),
+ data, len, rssi);
+
+ return 0;
+ }
+ /* drop incomplete UL block */
+ if (data[0] != 7)
+ return 0;
+ /* PDTCH / PACCH frame handling */
+ pcu_tx_data_ind(&trx->ts[tn], 0, fn, 0 /* ARFCN */,
+ L1SAP_FN2MACBLOCK(fn), data + 1, len - 1, rssi);
+
+ return 0;
+ }
+
+ lchan = &trx->ts[tn].lchan[ss];
+
+ /* bad frame */
+ if (len == 0) {
+ if (L1SAP_IS_LINK_SACCH(link_id))
+ radio_link_timeout(lchan, 1);
+ 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)) {
+ 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) {
+ LOGP(DL1P, LOGL_NOTICE, "SACCH with size %u<2 !?!\n",
+ len);
+ return -EINVAL;
+ }
+ /* Some brilliant engineer decided that the ordering of
+ * fields on the Um interface is different from the
+ * order of fields in RLS. See TS 04.04 (Chapter 7.2)
+ * vs. TS 08.58 (Chapter 9.3.10). */
+ lchan->meas.l1_info[0] = data[0] << 3;
+ lchan->meas.l1_info[0] |= ((data[0] >> 5) & 1) << 2;
+ lchan->meas.l1_info[1] = data[1];
+ lchan->meas.flags |= LC_UL_M_F_L1_VALID;
+
+ lchan_ms_pwr_ctrl(lchan, data[0] & 0x1f, data_ind->rssi);
+ } else
+ le = &lchan->lapdm_ch.lapdm_dcch;
+
+ if (check_for_first_ciphrd(lchan, data, len))
+ l1sap_tx_ciph_req(lchan->ts->trx, chan_nr, 1, 0);
+
+ /* SDCCH, SACCH and FACCH all go to LAPDm */
+ msgb_pull(msg, (msg->l2h - msg->data));
+ msg->l1h = NULL;
+ 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 msgb *msg = l1sap->oph.msg;
+ struct gsm_time g_time;
+ struct gsm_lchan *lchan;
+ uint8_t tn, ss, chan_nr;
+ uint32_t fn;
+
+ chan_nr = tch_ind->chan_nr;
+ fn = tch_ind->fn;
+ tn = L1SAP_CHAN2TS(chan_nr);
+ if (L1SAP_IS_CHAN_TCHH(chan_nr))
+ ss = L1SAP_CHAN2SS_TCHH(chan_nr);
+ else
+ ss = 0;
+ lchan = &trx->ts[tn].lchan[ss];
+
+ gsm_fn2gsmtime(&g_time, fn);
+
+ DEBUGP(DL1P, "Rx TCH.ind %02u/%02u/%02u chan_nr=%d\n",
+ g_time.t1, g_time.t2, g_time.t3, chan_nr);
+
+ msgb_pull(msg, sizeof(*l1sap));
+
+ /* hand msg to RTP code for transmission */
+ if (lchan->abis_ip.rtp_socket)
+ osmo_rtp_send_frame(lchan->abis_ip.rtp_socket,
+ msg->data, msg->len, 160);
+
+ /* if loopback is enabled, also queue received RTP data */
+ if (lchan->loopback) {
+ struct msgb *tmp;
+ int count = 0;
+
+ /* make sure the queue doesn't get too long */
+ llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
+ count++;
+ while (count >= 1) {
+ tmp = msgb_dequeue(&lchan->dl_tch_queue);
+ msgb_free(tmp);
+ count--;
+ }
+
+ msgb_enqueue(&lchan->dl_tch_queue, msg);
+ }
+
+ return 0;
+}
+
+/* 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;
+ uint8_t acc_delay;
+
+ DEBUGP(DL1P, "Rx PH-RA.ind");
+
+ lc = &trx->ts[0].lchan[4].lapdm_ch;
+
+ /* check for under/overflow / sign */
+ if (!check_acc_delay(rach_ind, btsb, &acc_delay)) {
+ LOGP(DL1C, LOGL_INFO, "ignoring RACH request %u > max_ta(%u)\n",
+ acc_delay, btsb->max_ta);
+ return 0;
+ }
+
+ /* check for handover rach */
+ if (!L1SAP_IS_CHAN_RACH(rach_ind->chan_nr))
+ return l1sap_handover_rach(trx, l1sap, rach_ind);
+
+ /* check for packet access */
+ if (trx == bts->c0 && L1SAP_IS_PACKET_RACH(rach_ind->ra)) {
+ LOGP(DL1P, LOGL_INFO, "RACH for packet access\n");
+ pcu_tx_rach_ind(bts, rach_ind->acc_delay << 2,
+ rach_ind->ra, rach_ind->fn);
+ return 0;
+ }
+
+ LOGP(DL1P, LOGL_INFO, "RACH for RR access (toa=%d, ra=%d)\n",
+ rach_ind->acc_delay, rach_ind->ra);
+ lapdm_phsap_up(&l1sap->oph, &lc->lapdm_dcch);
+
+ return 0;
+}
+
+/* any L1 prim received from bts model */
+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);
+ 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)
+{
+ 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, 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 %02u/%02u/%02u is_ptcch=%d trx=%d ts=%d "
+ "block_nr=%d, arfcn=%d, len=%d\n", g_time.t1, g_time.t2,
+ g_time.t3, 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 = 0x08 | ts->nr;
+ 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);
+}
+
+/*! \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)
+{
+ struct gsm_lchan *lchan = rs->priv;
+ struct msgb *msg, *tmp;
+ struct osmo_phsap_prim *l1sap;
+ int count = 0;
+
+ 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));
+
+
+ /* make sure the queue doesn't get too long */
+ llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
+ count++;
+ while (count >= 2) {
+ tmp = msgb_dequeue(&lchan->dl_tch_queue);
+ msgb_free(tmp);
+ count--;
+ }
+
+ msgb_enqueue(&lchan->dl_tch_queue, msg);
+}
+
+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;
+ struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)]
+ .lchan[l1sap_chan2ss(chan_nr)];
+ struct gsm48_chan_desc *cd;
+ int rc;
+
+ LOGP(DL1P, LOGL_INFO, "activating channel chan_nr=%02x trx=%d\n",
+ chan_nr, trx->nr);
+
+ /* osmo-pcu calls this without a valid 'tp' parameter, so we
+ * need to make sure ew don't crash here */
+ if (tp && TLVP_PRESENT(tp, GSM48_IE_CHANDESC_2) &&
+ TLVP_LEN(tp, GSM48_IE_CHANDESC_2) >= sizeof(*cd)) {
+ cd = (struct gsm48_chan_desc *)
+ TLVP_VAL(tp, GSM48_IE_CHANDESC_2);
+
+ /* our L1 only supports one global TSC for all channels
+ * one one TRX, so we need to make sure not to activate
+ * channels with a different TSC!! */
+ if (cd->h0.tsc != (lchan->ts->trx->bts->bsic & 7)) {
+ LOGP(DRSL, LOGL_ERROR, "lchan TSC %u != BSIC-TSC %u\n",
+ 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;
+ return 0;
+}
+
+int l1sap_chan_rel(struct gsm_bts_trx *trx, uint8_t chan_nr)
+{
+ LOGP(DL1P, LOGL_INFO, "deactivating channel chan_nr=%02x trx=%d\n",
+ chan_nr, trx->nr);
+
+ 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 = &trx->ts[L1SAP_CHAN2TS(chan_nr)]
+ .lchan[l1sap_chan2ss(chan_nr)];
+
+ LOGP(DL1P, LOGL_INFO, "deactivating sacch chan_nr=%02x trx=%d\n",
+ 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)
+{
+ LOGP(DL1P, LOGL_INFO, "modifying channel chan_nr=%02x trx=%d\n",
+ chan_nr, trx->nr);
+
+ return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_MODIFY, 0);
+}
diff --git a/src/common/pcu_sock.c b/src/common/pcu_sock.c
index a978e469..a4ca25f1 100644
--- a/src/common/pcu_sock.c
+++ b/src/common/pcu_sock.c
@@ -39,7 +39,7 @@
#include <osmo-bts/bts.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/signal.h>
-#include <osmo-bts/bts_model.h>
+#include <osmo-bts/l1sap.h>
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx);
@@ -57,10 +57,6 @@ static const char *sapi_string[] = {
[PCU_IF_SAPI_PTCCH] = "PTCCH",
};
-/* FIXME: common l1if include ? */
-int l1if_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);
-
static int pcu_sock_send(struct gsm_network *net, struct msgb *msg);
/* FIXME: move this to libosmocore */
int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path);
@@ -512,7 +508,7 @@ static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
}
ts = &trx->ts[data_req->ts_nr];
is_ptcch = (data_req->sapi == PCU_IF_SAPI_PTCCH);
- rc = l1if_pdch_req(ts, is_ptcch, data_req->fn, data_req->arfcn,
+ rc = l1sap_pdch_req(ts, is_ptcch, data_req->fn, data_req->arfcn,
data_req->block_nr, data_req->data, data_req->len);
break;
default:
@@ -546,9 +542,9 @@ static int pcu_rx_act_req(struct gsm_bts *bts,
return -EINVAL;
}
if (act_req->activate)
- bts_model_rsl_chan_act(lchan, NULL);
+ l1sap_chan_act(trx, gsm_lchan2chan_nr(lchan), NULL);
else
- bts_model_rsl_chan_rel(lchan);
+ l1sap_chan_rel(trx, gsm_lchan2chan_nr(lchan));
return 0;
}
@@ -653,7 +649,8 @@ static void pcu_sock_close(struct pcu_sock_state *state)
if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
&& ts->pchan == GSM_PCHAN_PDCH) {
ts->lchan->rel_act_kind = LCHAN_REL_ACT_PCU;
- bts_model_rsl_chan_rel(ts->lchan);
+ l1sap_chan_rel(trx,
+ gsm_lchan2chan_nr(ts->lchan));
}
}
}
diff --git a/src/common/power_control.c b/src/common/power_control.c
new file mode 100644
index 00000000..78d2702b
--- /dev/null
+++ b/src/common/power_control.c
@@ -0,0 +1,99 @@
+/* MS Power Control Loop L1 */
+
+/* (C) 2014 by Holger Hans Peter Freyther
+ *
+ * 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 <osmo-bts/logging.h>
+#include <osmo-bts/bts.h>
+#include <osmo-bts/gsm_data.h>
+#include <osmo-bts/measurement.h>
+#include <osmo-bts/bts_model.h>
+#include <osmo-bts/l1sap.h>
+
+/*
+ * Check if manual power control is needed
+ * Check if fixed power was selected
+ * Check if the MS is already using our level if not
+ * the value is bogus..
+ * TODO: Add a timeout.. e.g. if the ms is not capable of reaching
+ * the value we have set.
+ */
+int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
+ const uint8_t ms_power, const int rxLevel)
+{
+ int rx;
+ int cur_dBm, new_dBm, new_pwr;
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+ struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
+ const enum gsm_band band = bts->band;
+
+ if (!trx_ms_pwr_ctrl_is_osmo(lchan->ts->trx))
+ return 0;
+ if (lchan->ms_power_ctrl.fixed)
+ return 0;
+
+ /* The phone hasn't reached the power level yet */
+ if (lchan->ms_power_ctrl.current != ms_power)
+ return 0;
+
+ /*
+ * What is the difference between what we want and received?
+ * Ignore a margin that is within the range of measurement
+ * and MS output issues.
+ */
+ rx = btsb->ul_power_target - rxLevel;
+ if (rx >= 0 && rx < 1)
+ return 0;
+ if (rx < 0 && rx > -1)
+ return 0;
+
+ /* We don't really care about the truncation of int + float */
+ cur_dBm = ms_pwr_dbm(band, ms_power);
+ new_dBm = cur_dBm + rx;
+
+ /* Clamp negative values and do it depending on the band */
+ if (new_dBm < 0)
+ new_dBm = 0;
+
+ switch (band) {
+ case GSM_BAND_1800:
+ /* If MS_TX_PWR_MAX_CCH is set the values 29,
+ * 30, 31 are not used. Avoid specifying a dBm
+ * that would lead to these power levels. The
+ * phone might not be able to reach them. */
+ if (new_dBm > 30)
+ new_dBm = 30;
+ break;
+ default:
+ break;
+ }
+
+ new_pwr = ms_pwr_ctl_lvl(band, new_dBm);
+ if (lchan->ms_power_ctrl.current != new_pwr) {
+ lchan->ms_power_ctrl.current = new_pwr;
+ bts_model_adjst_ms_pwr(lchan);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/common/rsl.c b/src/common/rsl.c
index 9f1cf29b..0908f1cc 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -42,11 +42,11 @@
#include <osmo-bts/oml.h>
#include <osmo-bts/amr.h>
#include <osmo-bts/signal.h>
-#include <osmo-bts/bts_model.h>
#include <osmo-bts/measurement.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/handover.h>
#include <osmo-bts/cbch.h>
+#include <osmo-bts/l1sap.h>
//#define FAKE_CIPH_MODE_COMPL
@@ -543,8 +543,9 @@ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
}
/* 8.4.2 sending CHANnel ACTIVation ACKnowledge */
-int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime)
+int rsl_tx_chan_act_ack(struct gsm_lchan *lchan)
{
+ struct gsm_time *gtime = get_time(lchan->ts->trx->bts);
struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
uint8_t ie[2];
@@ -856,7 +857,7 @@ static int rsl_rx_chan_activ(struct msgb *msg)
/* actually activate the channel in the BTS */
lchan->rel_act_kind = LCHAN_REL_ACT_RSL;
- rc = bts_model_rsl_chan_act(msg->lchan, &tp);
+ rc = l1sap_chan_act(lchan->ts->trx, dch->chan_nr, &tp);
if (rc < 0)
return rsl_tx_chan_act_nack(lchan, -rc);
@@ -864,10 +865,8 @@ static int rsl_rx_chan_activ(struct msgb *msg)
}
/* 8.4.14 RF CHANnel RELease is received */
-static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan)
+static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan, uint8_t chan_nr)
{
- int rc;
-
if (lchan->abis_ip.rtp_socket) {
rsl_tx_ipac_dlcx_ind(lchan, RSL_ERR_NORMAL_UNSPEC);
osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
@@ -879,9 +878,11 @@ static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan)
handover_reset(lchan);
lchan->rel_act_kind = LCHAN_REL_ACT_RSL;
- rc = bts_model_rsl_chan_rel(lchan);
+ l1sap_chan_rel(lchan->ts->trx, chan_nr);
- return rc;
+ lapdm_channel_exit(&lchan->lapdm_ch);
+
+ return 0;
}
#ifdef FAKE_CIPH_MODE_COMPL
@@ -1064,10 +1065,10 @@ static int rsl_tx_mode_modif_ack(struct gsm_lchan *lchan)
/* 8.4.9 MODE MODIFY */
static int rsl_rx_mode_modif(struct msgb *msg)
{
+ struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
struct gsm_lchan *lchan = msg->lchan;
struct rsl_ie_chan_mode *cm;
struct tlv_parsed tp;
- int rc;
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
@@ -1108,12 +1109,12 @@ static int rsl_rx_mode_modif(struct msgb *msg)
/* 9.3.53 MultiRate Control */
/* 9.3.54 Supported Codec Types */
- rc = bts_model_rsl_mode_modify(msg->lchan);
+ l1sap_chan_modify(lchan->ts->trx, dch->chan_nr);
/* FIXME: delay this until L1 says OK? */
- rsl_tx_mode_modif_ack(msg->lchan);
+ rsl_tx_mode_modif_ack(lchan);
- return rc;
+ return 0;
}
/* 8.4.15 MS POWER CONTROL */
@@ -1479,7 +1480,7 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
OSMO_RTP_P_JITBUF,
btsb->rtp_jitter_buf_ms);
lchan->abis_ip.rtp_socket->priv = lchan;
- lchan->abis_ip.rtp_socket->rx_cb = &bts_model_rtp_rx_cb;
+ lchan->abis_ip.rtp_socket->rx_cb = &l1sap_rtp_rx_cb;
if (connect_ip && connect_port) {
/* if CRCX specifies a remote IP, we can bind()
@@ -1824,13 +1825,13 @@ static int rsl_rx_dchan(struct gsm_bts_trx *trx, struct msgb *msg)
ret = rsl_rx_chan_activ(msg);
break;
case RSL_MT_RF_CHAN_REL:
- ret = rsl_rx_rf_chan_rel(msg->lchan);
+ ret = rsl_rx_rf_chan_rel(msg->lchan, dch->chan_nr);
break;
case RSL_MT_SACCH_INFO_MODIFY:
ret = rsl_rx_sacch_inf_mod(msg);
break;
case RSL_MT_DEACTIVATE_SACCH:
- ret = bts_model_rsl_deact_sacch(msg->lchan);
+ ret = l1sap_chan_deact_sacch(trx, dch->chan_nr);
break;
case RSL_MT_ENCR_CMD:
ret = rsl_rx_encr_cmd(msg);
diff --git a/src/common/vty.c b/src/common/vty.c
index 3354fb98..e3fd57df 100644
--- a/src/common/vty.c
+++ b/src/common/vty.c
@@ -24,12 +24,15 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <ctype.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/abis_nm.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/logging.h>
+#include <osmocom/vty/misc.h>
+#include <osmocom/core/gsmtap.h>
#include <osmocom/trau/osmo_ortp.h>
@@ -44,7 +47,7 @@
#include <osmo-bts/bts_model.h>
#include <osmo-bts/measurement.h>
#include <osmo-bts/vty.h>
-
+#include <osmo-bts/l1sap.h>
enum node_type bts_vty_go_parent(struct vty *vty)
{
@@ -158,10 +161,38 @@ DEFUN(cfg_bts_trx, cfg_bts_trx_cmd,
return CMD_SUCCESS;
}
+/* FIXME: move to libosmocore ? */
+static char buf_casecnvt[256];
+char *osmo_str_tolower(const char *in)
+{
+ int len, i;
+
+ if (!in)
+ return NULL;
+
+ len = strlen(in);
+ if (len > sizeof(buf_casecnvt))
+ len = sizeof(buf_casecnvt);
+
+ for (i = 0; i < len; i++) {
+ buf_casecnvt[i] = tolower(in[i]);
+ if (in[i] == '\0')
+ break;
+ }
+ if (i < sizeof(buf_casecnvt))
+ buf_casecnvt[i] = '\0';
+
+ /* just to make sure we're always zero-terminated */
+ buf_casecnvt[sizeof(buf_casecnvt)-1] = '\0';
+
+ return buf_casecnvt;
+}
+
static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
struct gsm_bts_trx *trx;
+ int i;
vty_out(vty, "bts %u%s", bts->nr, VTY_NEWLINE);
if (bts->description)
@@ -176,6 +207,7 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
VTY_NEWLINE);
vty_out(vty, " paging lifetime %u%s", paging_get_lifetime(btsb->paging_state),
VTY_NEWLINE);
+ vty_out(vty, " uplink-power-target %d%s", btsb->ul_power_target, VTY_NEWLINE);
if (btsb->agch_queue_thresh_level != GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT
|| btsb->agch_queue_low_level != GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT
|| btsb->agch_queue_high_level != GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT)
@@ -183,6 +215,17 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
btsb->agch_queue_thresh_level, btsb->agch_queue_low_level,
btsb->agch_queue_high_level, VTY_NEWLINE);
+ for (i = 0; i < 32; i++) {
+ if (gsmtap_sapi_mask & (1 << i)) {
+ const char *name = get_value_string(gsmtap_sapi_names, i);
+ vty_out(vty, " gsmtap-sapi %s%s", osmo_str_tolower(name), VTY_NEWLINE);
+ }
+ }
+ if (gsmtap_sapi_acch) {
+ const char *name = get_value_string(gsmtap_sapi_names, GSMTAP_CHANNEL_ACCH);
+ vty_out(vty, " gsmtap-sapi %s%s", osmo_str_tolower(name), VTY_NEWLINE);
+ }
+
bts_model_config_write_bts(vty, bts);
llist_for_each_entry(trx, &bts->trx_list, list) {
@@ -399,6 +442,19 @@ DEFUN(cfg_bts_agch_queue_mgmt_default,
return CMD_SUCCESS;
}
+DEFUN(cfg_bts_ul_power_target, cfg_bts_ul_power_target_cmd,
+ "uplink-power-target <-110-0>",
+ "Set the nominal target Rx Level for uplink power control loop\n"
+ "Target uplink Rx level in dBm\n")
+{
+ struct gsm_bts *bts = vty->index;
+ struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
+
+ btsb->ul_power_target = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
#define DB_DBM_STR \
"Unit is dB (decibels)\n" \
"Unit is mdB (milli-decibels, or rather 1/10000 bel)\n"
@@ -614,6 +670,36 @@ static struct gsm_lchan *resolve_lchan(struct gsm_network *net,
"logical channel commands\n" \
"logical channel number\n"
+DEFUN(cfg_trx_gsmtap_sapi, cfg_trx_gsmtap_sapi_cmd,
+ "HIDDEN", "HIDDEN")
+{
+ int sapi;
+
+ sapi = get_string_value(gsmtap_sapi_names, argv[0]);
+
+ if (sapi == GSMTAP_CHANNEL_ACCH)
+ gsmtap_sapi_acch = 1;
+ else
+ gsmtap_sapi_mask |= (1 << sapi);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trx_no_gsmtap_sapi, cfg_trx_no_gsmtap_sapi_cmd,
+ "HIDDEN", "HIDDEN")
+{
+ int sapi;
+
+ sapi = get_string_value(gsmtap_sapi_names, argv[0]);
+
+ if (sapi == GSMTAP_CHANNEL_ACCH)
+ gsmtap_sapi_acch = 0;
+ else
+ gsmtap_sapi_mask &= ~(1 << sapi);
+
+ return CMD_SUCCESS;
+}
+
DEFUN(bts_t_t_l_jitter_buf,
bts_t_t_l_jitter_buf_cmd,
"bts <0-0> trx <0-0> ts <0-7> lchan <0-1> rtp jitter-buffer <0-10000>",
@@ -640,8 +726,58 @@ DEFUN(bts_t_t_l_jitter_buf,
return CMD_SUCCESS;
}
-int bts_vty_init(const struct log_info *cat)
+DEFUN(bts_t_t_l_loopback,
+ bts_t_t_l_loopback_cmd,
+ "bts <0-0> trx <0-0> ts <0-7> lchan <0-1> loopback",
+ BTS_T_T_L_STR "Set loopback\n")
+{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ struct gsm_lchan *lchan;
+
+ lchan = resolve_lchan(net, argv, 0);
+ if (!lchan) {
+ vty_out(vty, "%% can't find BTS%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ lchan->loopback = 1;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_bts_t_t_l_loopback,
+ no_bts_t_t_l_loopback_cmd,
+ "no bts <0-0> trx <0-0> ts <0-7> lchan <0-1> loopback",
+ NO_STR BTS_T_T_L_STR "Set loopback\n")
{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ struct gsm_lchan *lchan;
+
+ lchan = resolve_lchan(net, argv, 0);
+ if (!lchan) {
+ vty_out(vty, "%% can't find BTS%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ lchan->loopback = 0;
+
+ return CMD_SUCCESS;
+}
+
+int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat)
+{
+ cfg_trx_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names,
+ "gsmtap-sapi (",
+ "|",")", VTY_DO_LOWER);
+ cfg_trx_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names,
+ "GSMTAP SAPI\n",
+ "\n", "", 0);
+
+ cfg_trx_no_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names,
+ "no gsmtap-sapi (",
+ "|",")", VTY_DO_LOWER);
+ cfg_trx_no_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names,
+ NO_STR "GSMTAP SAPI\n",
+ "\n", "", 0);
+
install_element_ve(&show_bts_cmd);
logging_vty_add_cmds(cat);
@@ -660,6 +796,10 @@ int bts_vty_init(const struct log_info *cat)
install_element(BTS_NODE, &cfg_bts_paging_lifetime_cmd);
install_element(BTS_NODE, &cfg_bts_agch_queue_mgmt_default_cmd);
install_element(BTS_NODE, &cfg_bts_agch_queue_mgmt_params_cmd);
+ install_element(BTS_NODE, &cfg_bts_ul_power_target_cmd);
+
+ install_element(BTS_NODE, &cfg_trx_gsmtap_sapi_cmd);
+ install_element(BTS_NODE, &cfg_trx_no_gsmtap_sapi_cmd);
/* add and link to TRX config node */
install_element(BTS_NODE, &cfg_bts_trx_cmd);