aboutsummaryrefslogtreecommitdiffstats
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
parent1eaa3d72eaa10bb854b2adff48d3f7f3e06dcb8c (diff)
parent819b50e1a7b506a0a394cc71a795f0a9ce4083c1 (diff)
Merge branch '201509-l1sap'
-rw-r--r--include/osmo-bts/Makefile.am3
-rw-r--r--include/osmo-bts/bts.h1
-rw-r--r--include/osmo-bts/bts_model.h13
-rw-r--r--include/osmo-bts/gsm_data.h8
-rw-r--r--include/osmo-bts/l1sap.h4
-rw-r--r--include/osmo-bts/power_control.h7
-rw-r--r--include/osmo-bts/rsl.h2
-rw-r--r--include/osmo-bts/vty.h2
-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
-rw-r--r--src/osmo-bts-sysmo/l1_if.c1235
-rw-r--r--src/osmo-bts-sysmo/l1_if.h18
-rw-r--r--src/osmo-bts-sysmo/main.c24
-rw-r--r--src/osmo-bts-sysmo/oml.c91
-rw-r--r--src/osmo-bts-sysmo/sysmobts_vty.c91
-rw-r--r--src/osmo-bts-sysmo/tch.c120
-rw-r--r--tests/stubs.c2
-rw-r--r--tests/sysmobts/sysmobts_test.c25
24 files changed, 2066 insertions, 964 deletions
diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am
index cd3d8c04..bf037ae3 100644
--- a/include/osmo-bts/Makefile.am
+++ b/include/osmo-bts/Makefile.am
@@ -1,3 +1,4 @@
noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \
oml.h paging.h rsl.h signal.h vty.h amr.h pcu_if.h pcuif_proto.h \
- handover.h msg_utils.h tx_power.h control_if.h cbch.h l1sap.h
+ handover.h msg_utils.h tx_power.h control_if.h cbch.h l1sap.h \
+ power_control.h
diff --git a/include/osmo-bts/bts.h b/include/osmo-bts/bts.h
index bfa2dc3e..0b91ccbc 100644
--- a/include/osmo-bts/bts.h
+++ b/include/osmo-bts/bts.h
@@ -41,6 +41,7 @@ void bts_update_status(enum bts_global_status which, int on);
int trx_ms_pwr_ctrl_is_osmo(struct gsm_bts_trx *trx);
+struct gsm_time *get_time(struct gsm_bts *bts);
#endif /* _BTS_H */
diff --git a/include/osmo-bts/bts_model.h b/include/osmo-bts/bts_model.h
index 176b7877..de846365 100644
--- a/include/osmo-bts/bts_model.h
+++ b/include/osmo-bts/bts_model.h
@@ -12,8 +12,6 @@
int bts_model_init(struct gsm_bts *bts);
-struct gsm_time *bts_model_get_time(struct gsm_bts *bts);
-
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
struct tlv_parsed *old_attr, struct tlv_parsed *new_attr,
void *obj);
@@ -27,18 +25,9 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
void *obj, uint8_t adm_state);
-int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp);
-int bts_model_rsl_chan_rel(struct gsm_lchan *lchan);
-int bts_model_rsl_chan_mod(struct gsm_lchan *lchan);
-int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan);
-int bts_model_rsl_mode_modify(struct gsm_lchan *lchan);
-
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx);
int bts_model_trx_close(struct gsm_bts_trx *trx);
-void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
- unsigned int rtp_pl_len);
-
int bts_model_vty_init(struct gsm_bts *bts);
void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts);
@@ -49,4 +38,6 @@ int bts_model_oml_estab(struct gsm_bts *bts);
int bts_model_change_power(struct gsm_bts_trx *trx, int p_trxout_mdBm);
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan);
+int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap);
+
#endif
diff --git a/include/osmo-bts/gsm_data.h b/include/osmo-bts/gsm_data.h
index 0562ecaf..bfa5285b 100644
--- a/include/osmo-bts/gsm_data.h
+++ b/include/osmo-bts/gsm_data.h
@@ -81,8 +81,11 @@ struct gsm_bts_role_bts {
struct {
uint8_t tc4_ctr;
} si;
+ struct gsm_time gsm_time;
uint8_t radio_link_timeout;
+ int ul_power_target; /* Uplink Rx power target */
+
/* used by the sysmoBTS to adjust band */
uint8_t auto_band;
@@ -96,8 +99,9 @@ enum lchan_ciph_state {
LCHAN_CIPH_NONE,
LCHAN_CIPH_RX_REQ,
LCHAN_CIPH_RX_CONF,
- LCHAN_CIPH_TXRX_REQ,
- LCHAN_CIPH_TXRX_CONF,
+ LCHAN_CIPH_RXTX_REQ,
+ LCHAN_CIPH_RX_CONF_TX_REQ,
+ LCHAN_CIPH_RXTX_CONF,
};
#define bts_role_bts(x) ((struct gsm_bts_role_bts *)(x)->role)
diff --git a/include/osmo-bts/l1sap.h b/include/osmo-bts/l1sap.h
index a1dc3039..e76aca61 100644
--- a/include/osmo-bts/l1sap.h
+++ b/include/osmo-bts/l1sap.h
@@ -56,7 +56,7 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
unsigned int rtp_pl_len);
/* channel control */
-int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr);
+int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *tp);
int l1sap_chan_rel(struct gsm_bts_trx *trx, uint8_t chan_nr);
int l1sap_chan_deact_sacch(struct gsm_bts_trx *trx, uint8_t chan_nr);
int l1sap_chan_modify(struct gsm_bts_trx *trx, uint8_t chan_nr);
@@ -68,4 +68,6 @@ extern uint8_t gsmtap_sapi_acch;
#define msgb_l1sap_prim(msg) ((struct osmo_phsap_prim *)(msg)->l1h)
+int bts_check_for_first_ciphrd(struct gsm_lchan *lchan,
+ uint8_t *data, int len);
#endif /* L1SAP_H */
diff --git a/include/osmo-bts/power_control.h b/include/osmo-bts/power_control.h
new file mode 100644
index 00000000..43d4b591
--- /dev/null
+++ b/include/osmo-bts/power_control.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <stdint.h>
+#include <osmo-bts/gsm_data.h>
+
+int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
+ const uint8_t ms_power, const int rxLevel);
diff --git a/include/osmo-bts/rsl.h b/include/osmo-bts/rsl.h
index 6e8122fe..42ea6ef7 100644
--- a/include/osmo-bts/rsl.h
+++ b/include/osmo-bts/rsl.h
@@ -17,7 +17,7 @@ int rsl_tx_chan_rqd(struct gsm_bts_trx *trx, struct gsm_time *gtime,
uint8_t ra, uint8_t acc_delay);
int rsl_tx_est_ind(struct gsm_lchan *lchan, uint8_t link_id, uint8_t *data, int len);
-int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime);
+int rsl_tx_chan_act_ack(struct gsm_lchan *lchan);
int rsl_tx_chan_act_nack(struct gsm_lchan *lchan, uint8_t cause);
int rsl_tx_conn_fail(struct gsm_lchan *lchan, uint8_t cause);
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan);
diff --git a/include/osmo-bts/vty.h b/include/osmo-bts/vty.h
index 1bc7e718..510abab1 100644
--- a/include/osmo-bts/vty.h
+++ b/include/osmo-bts/vty.h
@@ -15,7 +15,7 @@ extern struct cmd_element ournode_end_cmd;
enum node_type bts_vty_go_parent(struct vty *vty);
int bts_vty_is_config_node(struct vty *vty, int node);
-int bts_vty_init(const struct log_info *cat);
+int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat);
extern struct vty_app_info bts_vty_info;
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);
diff --git a/src/osmo-bts-sysmo/l1_if.c b/src/osmo-bts-sysmo/l1_if.c
index 0159607a..e2ad500f 100644
--- a/src/osmo-bts-sysmo/l1_if.c
+++ b/src/osmo-bts-sysmo/l1_if.c
@@ -1,6 +1,6 @@
/* Interface handler for Sysmocom L1 */
-/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2011-2014 by Harald Welte <laforge@gnumonks.org>
* (C) 2014 by Holger Hans Peter Freyther
*
* All Rights Reserved
@@ -33,13 +33,9 @@
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/write_queue.h>
-#include <osmocom/core/gsmtap.h>
-#include <osmocom/core/gsmtap_util.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/lapdm.h>
-#include <osmocom/trau/osmo_ortp.h>
-
#include <osmo-bts/logging.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/oml.h>
@@ -51,6 +47,7 @@
#include <osmo-bts/handover.h>
#include <osmo-bts/cbch.h>
#include <osmo-bts/bts_model.h>
+#include <osmo-bts/l1sap.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h>
@@ -70,97 +67,6 @@ extern int pcu_direct;
#define MIN_QUAL_RACH 5.0f /* at least 5 dB C/I */
#define MIN_QUAL_NORM -0.5f /* at least -1 dB C/I */
-/* mapping from femtobts L1 SAPI to GSMTAP channel type */
-static const uint8_t l1sapi2gsmtap_cht[GsmL1_Sapi_NUM] = {
- [GsmL1_Sapi_Idle] = 255,
- [GsmL1_Sapi_Fcch] = 255,
- [GsmL1_Sapi_Sch] = 255,
- [GsmL1_Sapi_Sacch] = GSMTAP_CHANNEL_SDCCH | GSMTAP_CHANNEL_ACCH,
- [GsmL1_Sapi_Sdcch] = GSMTAP_CHANNEL_SDCCH,
- [GsmL1_Sapi_Bcch] = GSMTAP_CHANNEL_BCCH,
- [GsmL1_Sapi_Pch] = GSMTAP_CHANNEL_PCH,
- [GsmL1_Sapi_Agch] = GSMTAP_CHANNEL_AGCH,
- [GsmL1_Sapi_Cbch] = GSMTAP_CHANNEL_CBCH51,
- [GsmL1_Sapi_Rach] = GSMTAP_CHANNEL_RACH,
- [GsmL1_Sapi_TchF] = 255,
- [GsmL1_Sapi_FacchF] = GSMTAP_CHANNEL_TCH_F,
- [GsmL1_Sapi_TchH] = 255,
- [GsmL1_Sapi_FacchH] = GSMTAP_CHANNEL_TCH_H,
- [GsmL1_Sapi_Nch] = GSMTAP_CHANNEL_CCCH,
- [GsmL1_Sapi_Pdtch] = GSMTAP_CHANNEL_PACCH,
- [GsmL1_Sapi_Pacch] = GSMTAP_CHANNEL_PACCH,
- [GsmL1_Sapi_Pbcch] = 255,
- [GsmL1_Sapi_Pagch] = 255,
- [GsmL1_Sapi_Ppch] = 255,
- [GsmL1_Sapi_Pnch] = 255,
- [GsmL1_Sapi_Ptcch] = GSMTAP_CHANNEL_PTCCH,
- [GsmL1_Sapi_Prach] = 255,
-};
-
-static void tx_to_gsmtap(struct femtol1_hdl *fl1h, struct msgb *msg)
-{
- struct gsm_bts_trx *trx = fl1h->priv;
- GsmL1_Prim_t *l1p = msgb_l1prim(msg);
- GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
-
- if (fl1h->gsmtap) {
- uint8_t ss, chan_type;
- if (data_req->subCh == 0x1f)
- ss = 0;
- else
- ss = data_req->subCh;
-
- if (!(fl1h->gsmtap_sapi_mask & (1 << data_req->sapi)))
- return;
-
- chan_type = l1sapi2gsmtap_cht[data_req->sapi];
- if (chan_type == 255)
- return;
-
- gsmtap_send(fl1h->gsmtap, trx->arfcn, data_req->u8Tn,
- chan_type, ss, data_req->u32Fn, 0, 0,
- data_req->msgUnitParam.u8Buffer,
- data_req->msgUnitParam.u8Size);
- }
-}
-
-static void ul_to_gsmtap(struct femtol1_hdl *fl1h, struct msgb *msg)
-{
- struct gsm_bts_trx *trx = fl1h->priv;
- GsmL1_Prim_t *l1p = msgb_l1prim(msg);
- GsmL1_PhDataInd_t *data_ind = &l1p->u.phDataInd;
- int skip = 0;
-
- if (fl1h->gsmtap) {
- uint8_t ss, chan_type;
- if (data_ind->subCh == 0x1f)
- ss = 0;
- else
- ss = data_ind->subCh;
-
- if (!(fl1h->gsmtap_sapi_mask & (1 << data_ind->sapi)))
- return;
-
- chan_type = l1sapi2gsmtap_cht[data_ind->sapi];
- if (chan_type == 255)
- return;
- if (chan_type == GSMTAP_CHANNEL_PACCH
- || chan_type == GSMTAP_CHANNEL_PDCH) {
- if (data_ind->msgUnitParam.u8Buffer[0]
- != GsmL1_PdtchPlType_Full)
- return;
- skip = 1;
- }
-
- gsmtap_send(fl1h->gsmtap, trx->arfcn | GSMTAP_ARFCN_F_UPLINK,
- data_ind->u8Tn, chan_type, ss, data_ind->u32Fn,
- data_ind->measParam.fRssi,
- data_ind->measParam.fLinkQuality,
- data_ind->msgUnitParam.u8Buffer + skip,
- data_ind->msgUnitParam.u8Size - skip);
- }
-}
-
struct wait_l1_conf {
struct llist_head list; /* internal linked list */
@@ -326,19 +232,6 @@ empty_req_from_rts_ind(GsmL1_Prim_t *l1p,
return empty_req;
}
-/* obtain a ptr to the lapdm_channel for a given hLayer2 */
-static struct lapdm_channel *
-get_lapdm_chan_by_hl2(struct gsm_bts_trx *trx, uint32_t hLayer2)
-{
- struct gsm_lchan *lchan;
-
- lchan = l1if_hLayer_to_lchan(trx, hLayer2);
- if (!lchan)
- return NULL;
-
- return &lchan->lapdm_ch;
-}
-
/* 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)
@@ -377,31 +270,6 @@ static int check_for_ciph_cmd(struct femtol1_hdl *fl1h,
return 1;
}
-static inline void check_for_first_ciphrd(struct femtol1_hdl *fl1h,
- GsmL1_MsgUnitParam_t *msgUnitParam,
- struct gsm_lchan *lchan)
-{
- 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;
-
- /* HACK: check if it's an I frame, in order to
- * ignore some still buffered/queued UI frames received
- * before decryption was enabled */
- if (msgUnitParam->u8Buffer[0] != 0x01)
- return;
- if ((msgUnitParam->u8Buffer[1] & 0x01) != 0)
- return;
- n_s = msgUnitParam->u8Buffer[1] >> 5;
- if (lchan->ciph_ns != n_s)
- return;
- lchan->ciph_state = LCHAN_CIPH_TXRX_REQ;
- l1if_set_ciphering(fl1h, lchan, 1);
-}
-
/* public helpers for the test */
int bts_check_for_ciph_cmd(struct femtol1_hdl *fl1h,
struct msgb *msg, struct gsm_lchan *lchan)
@@ -409,34 +277,482 @@ int bts_check_for_ciph_cmd(struct femtol1_hdl *fl1h,
return check_for_ciph_cmd(fl1h, msg, lchan);
}
-void bts_check_for_first_ciphrd(struct femtol1_hdl *fl1h,
- GsmL1_MsgUnitParam_t *msgUnitParam,
- struct gsm_lchan *lchan)
-{
- return check_for_first_ciphrd(fl1h, msgUnitParam, lchan);
-}
-
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
};
+/* fill PH-DATA.req from l1sap primitive */
+static GsmL1_PhDataReq_t *
+data_req_from_l1sap(GsmL1_Prim_t *l1p, struct femtol1_hdl *fl1,
+ uint8_t tn, uint32_t fn, uint8_t sapi, uint8_t sub_ch,
+ uint8_t block_nr, uint8_t len)
+{
+ GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
+
+ l1p->id = GsmL1_PrimId_PhDataReq;
+
+ /* copy fields from PH-RSS.ind */
+ data_req->hLayer1 = fl1->hLayer1;
+ data_req->u8Tn = tn;
+ data_req->u32Fn = fn;
+ data_req->sapi = sapi;
+ data_req->subCh = sub_ch;
+ data_req->u8BlockNbr = block_nr;
+
+ data_req->msgUnitParam.u8Size = len;
+
+ return data_req;
+}
+
+/* fill PH-EMPTY_FRAME.req from l1sap primitive */
+static GsmL1_PhEmptyFrameReq_t *
+empty_req_from_l1sap(GsmL1_Prim_t *l1p, struct femtol1_hdl *fl1,
+ uint8_t tn, uint32_t fn, uint8_t sapi,
+ uint8_t subch, uint8_t block_nr)
+{
+ GsmL1_PhEmptyFrameReq_t *empty_req = &l1p->u.phEmptyFrameReq;
+
+ l1p->id = GsmL1_PrimId_PhEmptyFrameReq;
+
+ empty_req->hLayer1 = fl1->hLayer1;
+ empty_req->u8Tn = tn;
+ empty_req->u32Fn = fn;
+ empty_req->sapi = sapi;
+ empty_req->subCh = subch;
+ empty_req->u8BlockNbr = block_nr;
+
+ return empty_req;
+}
+
+static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
+ struct osmo_phsap_prim *l1sap)
+{
+ struct femtol1_hdl *fl1 = trx_femtol1_hdl(trx);
+ uint32_t u32Fn;
+ uint8_t u8Tn, subCh, u8BlockNbr = 0, sapi = 0;
+ uint8_t chan_nr, link_id;
+ GsmL1_Prim_t *l1p;
+ int len;
+
+ if (!msg) {
+ LOGP(DL1C, LOGL_FATAL, "PH-DATA.req without msg. "
+ "Please fix!\n");
+ abort();
+ }
+ chan_nr = l1sap->u.data.chan_nr;
+ link_id = l1sap->u.data.link_id;
+ u32Fn = l1sap->u.data.fn;
+ u8Tn = L1SAP_CHAN2TS(chan_nr);
+ subCh = 0x1f;
+ if (L1SAP_IS_LINK_SACCH(link_id)) {
+ sapi = GsmL1_Sapi_Sacch;
+ if (!L1SAP_IS_CHAN_TCHF(chan_nr))
+ subCh = l1sap_chan2ss(chan_nr);
+ } else if (L1SAP_IS_CHAN_TCHF(chan_nr)) {
+ if (trx->ts[u8Tn].pchan == GSM_PCHAN_PDCH) {
+ if (L1SAP_IS_PTCCH(u32Fn)) {
+ sapi = GsmL1_Sapi_Ptcch;
+ u8BlockNbr = L1SAP_FN2PTCCHBLOCK(u32Fn);
+ } else {
+ sapi = GsmL1_Sapi_Pdtch;
+ u8BlockNbr = L1SAP_FN2MACBLOCK(u32Fn);
+ }
+ } else {
+ sapi = GsmL1_Sapi_FacchF;
+ u8BlockNbr = (u32Fn % 13) >> 2;
+ }
+ } else if (L1SAP_IS_CHAN_TCHH(chan_nr)) {
+ subCh = L1SAP_CHAN2SS_TCHH(chan_nr);
+ sapi = GsmL1_Sapi_FacchH;
+ u8BlockNbr = (u32Fn % 26) >> 3;
+ } else if (L1SAP_IS_CHAN_SDCCH4(chan_nr)) {
+ subCh = L1SAP_CHAN2SS_SDCCH4(chan_nr);
+ sapi = GsmL1_Sapi_Sdcch;
+ } else if (L1SAP_IS_CHAN_SDCCH8(chan_nr)) {
+ subCh = L1SAP_CHAN2SS_SDCCH8(chan_nr);
+ sapi = GsmL1_Sapi_Sdcch;
+ } else if (L1SAP_IS_CHAN_BCCH(chan_nr)) {
+ sapi = GsmL1_Sapi_Bcch;
+ } else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) {
+ /* The sapi depends on DSP configuration, not
+ * on the actual SYSTEM INFORMATION 3. */
+ u8BlockNbr = L1SAP_FN2CCCHBLOCK(u32Fn);
+ if (u8BlockNbr >= 1)
+ sapi = GsmL1_Sapi_Pch;
+ else
+ sapi = GsmL1_Sapi_Agch;
+ } else {
+ LOGP(DL1C, LOGL_NOTICE, "unknown prim %d op %d "
+ "chan_nr %d link_id %d\n", l1sap->oph.primitive,
+ l1sap->oph.operation, chan_nr, link_id);
+ return -EINVAL;
+ }
+
+ /* pull and trim msg to start of payload */
+ msgb_pull(msg, sizeof(*l1sap));
+ len = msg->len;
+ msgb_trim(msg, 0);
+
+ /* convert l1sap message to GsmL1 primitive, keep payload */
+ if (len) {
+ /* data request */
+
+ /* wrap zeroed l1p structure arrount payload
+ * this must be done in three steps, since the actual
+ * payload is not at the end but inside the l1p structure. */
+ msgb_push(msg, offsetof(GsmL1_Prim_t, u.phDataReq.msgUnitParam.u8Buffer));
+ memset(msg->data, 0, msg->len);
+ msgb_put(msg, len);
+ memset(msg->tail, 0, sizeof(*l1p) - msg->len);
+ msgb_put(msg, sizeof(*l1p) - msg->len);
+ msg->l1h = msg->data;
+
+ l1p = msgb_l1prim(msg);
+ data_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr, len);
+ } else {
+ /* empty frame */
+
+ /* put l1p structure */
+ msgb_put(msg, sizeof(*l1p));
+ memset(msg->data, 0, msg->len);
+ msg->l1h = msg->data;
+
+ l1p = msgb_l1prim(msg);
+ empty_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr);
+ }
+
+ /* send message to DSP's queue */
+ if (osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], msg) != 0) {
+ LOGP(DL1P, LOGL_ERROR, "MQ_L1_WRITE queue full. Dropping msg.\n");
+ msgb_free(msg);
+ }
+
+ return 0;
+}
+
+static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
+ struct osmo_phsap_prim *l1sap)
+{
+ struct femtol1_hdl *fl1 = trx_femtol1_hdl(trx);
+ struct gsm_lchan *lchan;
+ uint32_t u32Fn;
+ uint8_t u8Tn, subCh, u8BlockNbr = 0, sapi, ss;
+ uint8_t chan_nr;
+ GsmL1_Prim_t *l1p;
+ struct msgb *nmsg = NULL;
+
+ chan_nr = l1sap->u.tch.chan_nr;
+ u32Fn = l1sap->u.tch.fn;
+ u8Tn = L1SAP_CHAN2TS(chan_nr);
+ u8BlockNbr = (u32Fn % 13) >> 2;
+ if (L1SAP_IS_CHAN_TCHH(chan_nr)) {
+ ss = subCh = L1SAP_CHAN2SS_TCHH(chan_nr);
+ sapi = GsmL1_Sapi_TchH;
+ } else {
+ subCh = 0x1f;
+ ss = 0;
+ sapi = GsmL1_Sapi_TchF;
+ }
+
+ lchan = &trx->ts[u8Tn].lchan[ss];
+
+ /* create new message and fill data */
+ if (msg) {
+ msgb_pull(msg, sizeof(*l1sap));
+ /* create new message */
+ nmsg = l1p_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ l1p = msgb_l1prim(nmsg);
+ l1if_tch_encode(lchan,
+ l1p->u.phDataReq.msgUnitParam.u8Buffer,
+ &l1p->u.phDataReq.msgUnitParam.u8Size,
+ msg->data, msg->len);
+ }
+
+ /* no message/data, we generate an empty traffic msg */
+ if (!nmsg)
+ nmsg = gen_empty_tch_msg(lchan);
+
+ /* no traffic message, we generate an empty msg */
+ if (!nmsg) {
+ nmsg = l1p_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ }
+
+ l1p = msgb_l1prim(nmsg);
+
+ /* if we provide data, or if data is already in nmsg */
+ if (l1p->u.phDataReq.msgUnitParam.u8Size) {
+ /* data request */
+ data_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh,
+ u8BlockNbr,
+ l1p->u.phDataReq.msgUnitParam.u8Size);
+ } else {
+ /* empty frame */
+ empty_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr);
+ }
+ /* send message to DSP's queue */
+ osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg);
+
+ msgb_free(msg);
+ return 0;
+}
+
+static int mph_info_req(struct gsm_bts_trx *trx, struct msgb *msg,
+ struct osmo_phsap_prim *l1sap)
+{
+ struct femtol1_hdl *fl1 = trx_femtol1_hdl(trx);
+ uint8_t u8Tn, ss;
+ uint8_t chan_nr;
+ struct gsm_lchan *lchan;
+ int rc = 0;
+
+ switch (l1sap->u.info.type) {
+ case PRIM_INFO_ACT_CIPH:
+ chan_nr = l1sap->u.info.u.ciph_req.chan_nr;
+ u8Tn = L1SAP_CHAN2TS(chan_nr);
+ ss = l1sap_chan2ss(chan_nr);
+ lchan = &trx->ts[u8Tn].lchan[ss];
+ if (l1sap->u.info.u.ciph_req.uplink) {
+ l1if_set_ciphering(fl1, lchan, 0);
+ lchan->ciph_state = LCHAN_CIPH_RX_REQ;
+ }
+ if (l1sap->u.info.u.ciph_req.downlink) {
+ l1if_set_ciphering(fl1, lchan, 1);
+ lchan->ciph_state = LCHAN_CIPH_RX_CONF_TX_REQ;
+ }
+ if (l1sap->u.info.u.ciph_req.downlink
+ && l1sap->u.info.u.ciph_req.uplink)
+ lchan->ciph_state = LCHAN_CIPH_RXTX_REQ;
+ break;
+ case PRIM_INFO_ACTIVATE:
+ case PRIM_INFO_DEACTIVATE:
+ case PRIM_INFO_MODIFY:
+ chan_nr = l1sap->u.info.u.act_req.chan_nr;
+ u8Tn = L1SAP_CHAN2TS(chan_nr);
+ ss = l1sap_chan2ss(chan_nr);
+ lchan = &trx->ts[u8Tn].lchan[ss];
+ if (l1sap->u.info.type == PRIM_INFO_ACTIVATE)
+ l1if_rsl_chan_act(lchan);
+ else if (l1sap->u.info.type == PRIM_INFO_MODIFY) {
+ if (lchan->ho.active == HANDOVER_WAIT_FRAME)
+ l1if_rsl_chan_mod(lchan);
+ else
+ l1if_rsl_mode_modify(lchan);
+ } else if (l1sap->u.info.u.act_req.sacch_only)
+ l1if_rsl_deact_sacch(lchan);
+ else
+ l1if_rsl_chan_rel(lchan);
+ msgb_free(msg);
+ break;
+ default:
+ LOGP(DL1C, LOGL_NOTICE, "unknown MPH-INFO.req %d\n",
+ l1sap->u.info.type);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+/* primitive from common part */
+int bts_model_l1sap_down(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_PH_DATA, PRIM_OP_REQUEST):
+ rc = ph_data_req(trx, msg, l1sap);
+ break;
+ case OSMO_PRIM(PRIM_TCH, PRIM_OP_REQUEST):
+ rc = ph_tch_req(trx, msg, l1sap);
+ break;
+ case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST):
+ rc = mph_info_req(trx, msg, l1sap);
+ break;
+ default:
+ LOGP(DL1C, LOGL_NOTICE, "unknown prim %d op %d\n",
+ l1sap->oph.primitive, l1sap->oph.operation);
+ rc = -EINVAL;
+ }
+
+ if (rc)
+ msgb_free(msg);
+ return rc;
+}
+
+static int handle_mph_time_ind(struct femtol1_hdl *fl1,
+ GsmL1_MphTimeInd_t *time_ind)
+{
+ struct gsm_bts_trx *trx = fl1->priv;
+ struct gsm_bts *bts = trx->bts;
+ struct osmo_phsap_prim l1sap;
+ uint32_t fn;
+
+ /* increment the primitive count for the alive timer */
+ fl1->alive_prim_cnt++;
+
+ /* ignore every time indication, except for c0 */
+ if (trx != bts->c0) {
+ return 0;
+ }
+
+ fn = time_ind->u32Fn;
+
+ memset(&l1sap, 0, sizeof(l1sap));
+ osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO,
+ PRIM_OP_INDICATION, NULL);
+ l1sap.u.info.type = PRIM_INFO_TIME;
+ l1sap.u.info.u.time_ind.fn = fn;
+
+ return l1sap_up(trx, &l1sap);
+}
+
+static uint8_t chan_nr_by_sapi(enum gsm_phys_chan_config pchan,
+ GsmL1_Sapi_t sapi, GsmL1_SubCh_t subCh,
+ uint8_t u8Tn, uint32_t u32Fn)
+{
+ uint8_t cbits = 0;
+ switch (sapi) {
+ case GsmL1_Sapi_Bcch:
+ cbits = 0x10;
+ break;
+ case GsmL1_Sapi_Sacch:
+ switch(pchan) {
+ case GSM_PCHAN_TCH_F:
+ cbits = 0x01;
+ break;
+ case GSM_PCHAN_TCH_H:
+ cbits = 0x02 + subCh;
+ break;
+ case GSM_PCHAN_CCCH_SDCCH4:
+ cbits = 0x04 + subCh;
+ break;
+ case GSM_PCHAN_SDCCH8_SACCH8C:
+ cbits = 0x08 + subCh;
+ break;
+ default:
+ LOGP(DL1C, LOGL_ERROR, "SACCH for pchan %d?\n",
+ pchan);
+ return 0;
+ }
+ break;
+ case GsmL1_Sapi_Sdcch:
+ switch(pchan) {
+ case GSM_PCHAN_CCCH_SDCCH4:
+ cbits = 0x04 + subCh;
+ break;
+ case GSM_PCHAN_SDCCH8_SACCH8C:
+ cbits = 0x08 + subCh;
+ break;
+ default:
+ LOGP(DL1C, LOGL_ERROR, "SDCCH for pchan %d?\n",
+ pchan);
+ return 0;
+ }
+ break;
+ case GsmL1_Sapi_Agch:
+ case GsmL1_Sapi_Pch:
+ cbits = 0x12;
+ break;
+ case GsmL1_Sapi_Pdtch:
+ case GsmL1_Sapi_Pacch:
+ switch(pchan) {
+ case GSM_PCHAN_PDCH:
+ cbits = 0x01;
+ break;
+ default:
+ LOGP(DL1C, LOGL_ERROR, "PDTCH for pchan %d?\n",
+ pchan);
+ return 0;
+ }
+ break;
+ case GsmL1_Sapi_TchF:
+ cbits = 0x01;
+ break;
+ case GsmL1_Sapi_TchH:
+ cbits = 0x02 + subCh;
+ break;
+ case GsmL1_Sapi_FacchF:
+ cbits = 0x01;
+ break;
+ case GsmL1_Sapi_FacchH:
+ cbits = 0x02 + subCh;
+ break;
+ case GsmL1_Sapi_Ptcch:
+ if (!L1SAP_IS_PTCCH(u32Fn)) {
+ LOGP(DL1C, LOGL_FATAL, "Not expecting PTCCH at frame "
+ "number other than 12, got it at %u (%u). "
+ "Please fix!\n", u32Fn % 52, u32Fn);
+ abort();
+ }
+ switch(pchan) {
+ case GSM_PCHAN_PDCH:
+ cbits = 0x01;
+ break;
+ default:
+ LOGP(DL1C, LOGL_ERROR, "PTCCH for pchan %d?\n",
+ pchan);
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+
+ /* not reached due to default case above */
+ return (cbits << 3) | u8Tn;
+}
+
static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
- GsmL1_PhReadyToSendInd_t *rts_ind)
+ GsmL1_PhReadyToSendInd_t *rts_ind,
+ struct msgb *l1p_msg)
{
struct gsm_bts_trx *trx = fl1->priv;
struct gsm_bts *bts = trx->bts;
struct msgb *resp_msg;
GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param;
- struct lapdm_entity *le;
- struct gsm_lchan *lchan;
struct gsm_time g_time;
uint32_t t3p;
- uint8_t *si;
- struct osmo_phsap_prim pp;
int rc;
+ struct osmo_phsap_prim *l1sap;
+ uint8_t chan_nr, link_id;
+ uint32_t fn;
+
+ /* check if primitive should be handled by common part */
+ chan_nr = chan_nr_by_sapi(trx->ts[rts_ind->u8Tn].pchan, rts_ind->sapi,
+ rts_ind->subCh, rts_ind->u8Tn, rts_ind->u32Fn);
+ if (chan_nr) {
+ fn = rts_ind->u32Fn;
+ if (rts_ind->sapi == GsmL1_Sapi_Sacch)
+ link_id = 0x40;
+ else
+ link_id = 0;
+ rc = msgb_trim(l1p_msg, sizeof(*l1sap));
+ if (rc < 0)
+ MSGB_ABORT(l1p_msg, "No room for primitive\n");
+ l1sap = msgb_l1sap_prim(l1p_msg);
+ if (rts_ind->sapi == GsmL1_Sapi_TchF
+ || rts_ind->sapi == GsmL1_Sapi_TchH) {
+ osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH_RTS,
+ PRIM_OP_INDICATION, l1p_msg);
+ l1sap->u.tch.chan_nr = chan_nr;
+ l1sap->u.tch.fn = fn;
+ } else {
+ osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS,
+ PRIM_OP_INDICATION, l1p_msg);
+ l1sap->u.data.link_id = link_id;
+ l1sap->u.data.chan_nr = chan_nr;
+ l1sap->u.data.fn = fn;
+ }
+
+ return l1sap_up(trx, l1sap);
+ }
gsm_fn2gsmtime(&g_time, rts_ind->u32Fn);
@@ -444,57 +760,6 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
g_time.t1, g_time.t2, g_time.t3,
get_value_string(femtobts_l1sapi_names, rts_ind->sapi));
- /* In case of TCH downlink trasnmission, we already have a l1
- * primitive msgb pre-allocated and pre-formatted in the
- * dl_tch_queue. All we need to do is to pull it off the queue
- * and transmit it */
- switch (rts_ind->sapi) {
- case GsmL1_Sapi_TchF:
- case GsmL1_Sapi_TchH:
- /* resolve the L2 entity using rts_ind->hLayer2 */
- lchan = l1if_hLayer_to_lchan(trx, rts_ind->hLayer2);
- if (!lchan)
- break;
-
- 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 there is none, try to generate empty TCH frame
- * like AMR SID_BAD */
- if (!resp_msg) {
- LOGP(DL1C, LOGL_DEBUG, "%s DL TCH Tx queue underrun\n",
- gsm_lchan_name(lchan));
- resp_msg = gen_empty_tch_msg(lchan);
- /* if there really is none, break here and send empty */
- if (!resp_msg)
- break;
- }
-
- /* fill header */
- data_req_from_rts_ind(msgb_l1prim(resp_msg), rts_ind);
- /* actually transmit it */
- goto tx;
- break;
- case GsmL1_Sapi_Pdtch:
- case GsmL1_Sapi_Pacch:
- return pcu_tx_rts_req(&trx->ts[rts_ind->u8Tn], 0,
- rts_ind->u32Fn, rts_ind->u16Arfcn, rts_ind->u8BlockNbr);
- case GsmL1_Sapi_Ptcch:
- return pcu_tx_rts_req(&trx->ts[rts_ind->u8Tn], 1,
- rts_ind->u32Fn, rts_ind->u16Arfcn, rts_ind->u8BlockNbr);
- default:
- break;
- }
-
/* in all other cases, we need to allocate a new PH-DATA.ind
* primitive msgb and start to fill it */
resp_msg = l1p_msgb_alloc();
@@ -515,83 +780,6 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
msu_param->u8Buffer[2] = (g_time.t1 << 7) | (g_time.t2 << 2) | (t3p >> 1);
msu_param->u8Buffer[3] = (t3p & 1);
break;
- case GsmL1_Sapi_Bcch:
- /* get them from bts->si_buf[] */
- si = bts_sysinfo_get(bts, &g_time);
- if (si)
- memcpy(msu_param->u8Buffer, si, GSM_MACBLOCK_LEN);
- else
- memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
- break;
- case GsmL1_Sapi_Sacch:
- /* resolve the L2 entity using rts_ind->hLayer2 */
- lchan = l1if_hLayer_to_lchan(trx, rts_ind->hLayer2);
- le = &lchan->lapdm_ch.lapdm_acch;
- /*
- * if the DSP is taking care of power control,
- * then this value will be overridden.
- */
- msu_param->u8Buffer[0] = lchan->ms_power_ctrl.current;
- msu_param->u8Buffer[1] = lchan->rqd_ta;
- rc = lapdm_phsap_dequeue_prim(le, &pp);
- if (rc < 0) {
- /* No SACCH data from LAPDM pending, send SACCH filling */
- uint8_t *si = lchan_sacch_get(lchan);
- if (si) {
- /* +2 to not overwrite the ms_power/ta values */
- memcpy(msu_param->u8Buffer+2, si, GSM_MACBLOCK_LEN-2);
- } else {
- /* +2 to not overwrite the ms_power/ta values */
- memcpy(msu_param->u8Buffer+2, fill_frame, GSM_MACBLOCK_LEN-2);
- }
- } else {
- /* +2 to not overwrite the ms_power/ta values */
- memcpy(msu_param->u8Buffer+2, pp.oph.msg->data + 2, GSM_MACBLOCK_LEN-2);
- msgb_free(pp.oph.msg);
- }
- break;
- case GsmL1_Sapi_Sdcch:
- /* resolve the L2 entity using rts_ind->hLayer2 */
- lchan = l1if_hLayer_to_lchan(trx, rts_ind->hLayer2);
- le = &lchan->lapdm_ch.lapdm_dcch;
- rc = lapdm_phsap_dequeue_prim(le, &pp);
- if (rc < 0)
- memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
- else {
- memcpy(msu_param->u8Buffer, 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(fl1, pp.oph.msg, lchan);
- msgb_free(pp.oph.msg);
- }
- break;
- case GsmL1_Sapi_Agch:
- case GsmL1_Sapi_Pch:
- rc = bts_ccch_copy_msg(bts, msu_param->u8Buffer, &g_time,
- rts_ind->sapi == GsmL1_Sapi_Agch);
- if (rc <= 0)
- memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
- break;
- case GsmL1_Sapi_TchF:
- case GsmL1_Sapi_TchH:
- /* only hit in case we have a RTP underflow, as real TCH
- * frames are handled way above */
- goto empty_frame;
- break;
- case GsmL1_Sapi_FacchF:
- case GsmL1_Sapi_FacchH:
- /* resolve the L2 entity using rts_ind->hLayer2 */
- lchan = l1if_hLayer_to_lchan(trx, rts_ind->hLayer2);
- le = &lchan->lapdm_ch.lapdm_dcch;
- rc = lapdm_phsap_dequeue_prim(le, &pp);
- if (rc < 0)
- goto empty_frame;
- else {
- memcpy(msu_param->u8Buffer, 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(fl1, pp.oph.msg, lchan);
- msgb_free(pp.oph.msg);
- }
- break;
case GsmL1_Sapi_Prach:
goto empty_frame;
break;
@@ -605,14 +793,13 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
}
tx:
- tx_to_gsmtap(fl1, resp_msg);
-
/* transmit */
if (osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], resp_msg) != 0) {
LOGP(DL1C, LOGL_ERROR, "MQ_L1_WRITE queue full. Dropping msg.\n");
msgb_free(resp_msg);
}
+ msgb_free(l1p_msg);
return 0;
empty_frame:
@@ -622,65 +809,6 @@ empty_frame:
goto tx;
}
-static int handle_mph_time_ind(struct femtol1_hdl *fl1,
- GsmL1_MphTimeInd_t *time_ind)
-{
- struct gsm_bts_trx *trx = fl1->priv;
- struct gsm_bts *bts = trx->bts;
- struct gsm_bts_role_bts *btsb = bts->role;
-
- int frames_expired = time_ind->u32Fn - fl1->gsm_time.fn;
-
- /* update time on PCU interface */
- pcu_tx_time_ind(time_ind->u32Fn);
-
- /* Update our data structures with the current GSM time */
- gsm_fn2gsmtime(&fl1->gsm_time, time_ind->u32Fn);
-
- /* check if the measurement period of some lchan has ended
- * and pre-compute the respective measurement */
- trx_meas_check_compute(fl1->priv, time_ind->u32Fn -1);
-
- /* increment the primitive count for the alive timer */
- fl1->alive_prim_cnt++;
-
- /* 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;
-}
-
-/* determine LAPDm entity inside LAPDm channel for given L1 sapi */
-static struct lapdm_entity *le_by_l1_sapi(struct lapdm_channel *lc, GsmL1_Sapi_t sapi)
-{
- switch (sapi) {
- case GsmL1_Sapi_Sacch:
- return &lc->lapdm_acch;
- default:
- return &lc->lapdm_dcch;
- }
-}
-
-static uint8_t gen_link_id(GsmL1_Sapi_t l1_sapi, uint8_t lapdm_sapi)
-{
- uint8_t c_bits = 0;
-
- if (l1_sapi == GsmL1_Sapi_Sacch)
- c_bits = 0x40;
-
- return c_bits | (lapdm_sapi & 7);
-}
-
static void dump_meas_res(int ll, GsmL1_MeasParam_t *m)
{
LOGPC(DL1C, ll, ", Meas: RSSI %-3.2f dBm, Qual %-3.2f dB, "
@@ -688,79 +816,50 @@ static void dump_meas_res(int ll, GsmL1_MeasParam_t *m)
m->fBer, m->i16BurstTiming);
}
-static int process_meas_res(struct gsm_lchan *lchan, GsmL1_MeasParam_t *m)
-{
- struct bts_ul_meas ulm;
-
- /* 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;
-
- ulm.ta_offs_qbits = m->i16BurstTiming;
- ulm.ber10k = (unsigned int) (m->fBer * 100);
- ulm.inv_rssi = (uint8_t) (m->fRssi * -1);
-
- return lchan_new_ul_meas(lchan, &ulm);
-}
-
-/* process radio link timeout counter S */
-static void radio_link_timeout(struct gsm_lchan *lchan, int bad_frame)
+static int process_meas_res(struct gsm_bts_trx *trx, uint8_t chan_nr,
+ GsmL1_MeasParam_t *m)
{
- 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);
- }
+ struct osmo_phsap_prim l1sap;
+ memset(&l1sap, 0, sizeof(l1sap));
+ osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO,
+ PRIM_OP_INDICATION, NULL);
+ l1sap.u.info.type = PRIM_INFO_MEAS;
+ l1sap.u.info.u.meas_ind.chan_nr = chan_nr;
+ l1sap.u.info.u.meas_ind.ta_offs_qbits = m->i16BurstTiming;
+ l1sap.u.info.u.meas_ind.ber10k = (unsigned int) (m->fBer * 100);
+ l1sap.u.info.u.meas_ind.inv_rssi = (uint8_t) (m->fRssi * -1);
+
+ return l1sap_up(trx, &l1sap);
}
static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_ind,
struct msgb *l1p_msg)
{
struct gsm_bts_trx *trx = fl1->priv;
- struct osmo_phsap_prim pp;
- struct gsm_lchan *lchan;
- struct lapdm_entity *le;
- struct msgb *msg;
+ uint8_t chan_nr, link_id;
+ struct osmo_phsap_prim *l1sap;
+ uint32_t fn;
+ uint8_t *data, len;
int rc = 0;
- ul_to_gsmtap(fl1, l1p_msg);
-
- lchan = l1if_hLayer_to_lchan(fl1->priv, data_ind->hLayer2);
- if (!lchan) {
- LOGP(DL1C, LOGL_ERROR,
- "unable to resolve lchan by hLayer2 for 0x%x\n",
- data_ind->hLayer2);
- return -ENODEV;
+ chan_nr = chan_nr_by_sapi(trx->ts[data_ind->u8Tn].pchan, data_ind->sapi,
+ data_ind->subCh, data_ind->u8Tn, data_ind->u32Fn);
+ if (!chan_nr) {
+ LOGP(DL1C, LOGL_ERROR, "PH-DATA-INDICATION for unknown sapi "
+ "%d\n", data_ind->sapi);
+ msgb_free(l1p_msg);
+ return ENOTSUP;
}
+ fn = data_ind->u32Fn;
+ link_id = (data_ind->sapi == GsmL1_Sapi_Sacch) ? 0x40 : 0x00;
- process_meas_res(lchan, &data_ind->measParam);
+ process_meas_res(trx, chan_nr, &data_ind->measParam);
if (data_ind->measParam.fLinkQuality < fl1->min_qual_norm
- && data_ind->msgUnitParam.u8Size != 0)
+ && data_ind->msgUnitParam.u8Size != 0) {
+ msgb_free(l1p_msg);
return 0;
+ }
DEBUGP(DL1C, "Rx PH-DATA.ind %s (hL2 %08x): %s",
get_value_string(femtobts_l1sapi_names, data_ind->sapi),
@@ -769,195 +868,96 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
data_ind->msgUnitParam.u8Size));
dump_meas_res(LOGL_DEBUG, &data_ind->measParam);
- if (lchan->ho.active == HANDOVER_WAIT_FRAME)
- handover_frame(lchan);
-
- switch (data_ind->sapi) {
- case GsmL1_Sapi_Sacch:
- radio_link_timeout(lchan, (data_ind->msgUnitParam.u8Size == 0));
- if (data_ind->msgUnitParam.u8Size == 0)
- break;
- /* save the SACCH L1 header in the lchan struct for RSL MEAS RES */
- if (data_ind->msgUnitParam.u8Size < 2) {
- LOGP(DL1C, LOGL_NOTICE, "SACCH with size %u<2 !?!\n",
- data_ind->msgUnitParam.u8Size);
- break;
- }
-
- /*
- * Handle power control
- */
- l1if_ms_pwr_ctrl(lchan, fl1->ul_power_target,
- data_ind->msgUnitParam.u8Buffer[0] & 0x1f,
- data_ind->measParam.fRssi);
-
- /* 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_ind->msgUnitParam.u8Buffer[0] << 3;
- lchan->meas.l1_info[0] |= ((data_ind->msgUnitParam.u8Buffer[0] >> 5) & 1) << 2;
- lchan->meas.l1_info[1] = data_ind->msgUnitParam.u8Buffer[1];
- lchan->meas.flags |= LC_UL_M_F_L1_VALID;
- /* fall-through */
- case GsmL1_Sapi_Sdcch:
- case GsmL1_Sapi_FacchF:
- case GsmL1_Sapi_FacchH:
- /* Check and Re-check for the SACCH */
- if (data_ind->msgUnitParam.u8Size == 0) {
- LOGP(DL1C, LOGL_NOTICE, "%s %s data is null.\n",
- gsm_lchan_name(lchan),
- get_value_string(femtobts_l1sapi_names, data_ind->sapi));
- break;
- }
-
- check_for_first_ciphrd(fl1, &data_ind->msgUnitParam, lchan);
-
- /* SDCCH, SACCH and FACCH all go to LAPDm */
- le = le_by_l1_sapi(&lchan->lapdm_ch, data_ind->sapi);
- /* allocate and fill LAPDm primitive */
- msg = msgb_alloc_headroom(128, 64, "PH-DATA.ind");
- osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA,
- PRIM_OP_INDICATION, msg);
-
- /* copy over actual MAC block */
- msg->l2h = msgb_put(msg, data_ind->msgUnitParam.u8Size);
- memcpy(msg->l2h, data_ind->msgUnitParam.u8Buffer,
- data_ind->msgUnitParam.u8Size);
-
- /* LAPDm requires those... */
- pp.u.data.chan_nr = gsm_lchan2chan_nr(lchan);
- pp.u.data.link_id = gen_link_id(data_ind->sapi, 0);
-
- /* feed into the LAPDm code of libosmogsm */
- rc = lapdm_phsap_up(&pp.oph, le);
- break;
- case GsmL1_Sapi_TchF:
- case GsmL1_Sapi_TchH:
+ /* check for TCH */
+ if (data_ind->sapi == GsmL1_Sapi_TchF
+ || data_ind->sapi == GsmL1_Sapi_TchH) {
/* TCH speech frame handling */
- rc = l1if_tch_rx(lchan, l1p_msg);
- break;
- case GsmL1_Sapi_Pdtch:
- case GsmL1_Sapi_Pacch:
- /* drop incomplete UL block */
- if (!data_ind->msgUnitParam.u8Size
- || data_ind->msgUnitParam.u8Buffer[0]
- != GsmL1_PdtchPlType_Full)
- break;
- /* PDTCH / PACCH frame handling */
- rc = pcu_tx_data_ind(&trx->ts[data_ind->u8Tn], 0,
- data_ind->u32Fn, data_ind->u16Arfcn,
- data_ind->u8BlockNbr,
- data_ind->msgUnitParam.u8Buffer + 1,
- data_ind->msgUnitParam.u8Size - 1,
- (int8_t) (data_ind->measParam.fRssi));
- break;
- case GsmL1_Sapi_Ptcch:
- /* PTCCH frame handling */
- rc = pcu_tx_data_ind(&trx->ts[data_ind->u8Tn], 1,
- data_ind->u32Fn, data_ind->u16Arfcn,
- data_ind->u8BlockNbr,
- data_ind->msgUnitParam.u8Buffer,
- data_ind->msgUnitParam.u8Size,
- (int8_t) (data_ind->measParam.fRssi));
- break;
- case GsmL1_Sapi_Idle:
- /* nothing to send */
- break;
- default:
- LOGP(DL1C, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n",
- get_value_string(femtobts_l1sapi_names, data_ind->sapi));
- break;
- }
-
- return rc;
-}
-
-static int check_acc_delay(GsmL1_PhRaInd_t *ra_ind, struct gsm_bts_role_bts *btsb,
- uint8_t *acc_delay)
-{
- if (ra_ind->measParam.i16BurstTiming < 0)
- *acc_delay = 0;
- else
- *acc_delay = ra_ind->measParam.i16BurstTiming >> 2;
- return *acc_delay <= btsb->max_ta;
-}
-
-static int handle_handover(struct gsm_lchan *lchan, struct gsm_bts_role_bts *btsb,
- GsmL1_PhRaInd_t *ra_ind)
-{
- uint8_t acc_delay;
- if (!check_acc_delay(ra_ind, btsb, &acc_delay)) {
- LOGP(DHO, LOGL_INFO, "%s ignoring RACH request %u > max_ta(%u)\n",
- gsm_lchan_name(lchan), acc_delay, btsb->max_ta);
- return 0;
+ rc = l1if_tch_rx(trx, chan_nr, l1p_msg);
+ msgb_free(l1p_msg);
+ return rc;
}
- handover_rach(lchan, ra_ind->msgUnitParam.u8Buffer[0], acc_delay);
- return 0;
+ /* get data pointer and length */
+ data = data_ind->msgUnitParam.u8Buffer;
+ len = data_ind->msgUnitParam.u8Size;
+ /* pull lower header part before data */
+ msgb_pull(l1p_msg, data - l1p_msg->data);
+ /* trim remaining data to it's size, to get rid of upper header part */
+ rc = msgb_trim(l1p_msg, len);
+ if (rc < 0)
+ MSGB_ABORT(l1p_msg, "No room for primitive data\n");
+ l1p_msg->l2h = l1p_msg->data;
+ /* push new l1 header */
+ l1p_msg->l1h = msgb_push(l1p_msg, sizeof(*l1sap));
+ /* fill header */
+ l1sap = msgb_l1sap_prim(l1p_msg);
+ osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA,
+ PRIM_OP_INDICATION, l1p_msg);
+ l1sap->u.data.rssi = data_ind->measParam.fRssi;
+ l1sap->u.data.link_id = link_id;
+ l1sap->u.data.chan_nr = chan_nr;
+ l1sap->u.data.fn = fn;
+
+ return l1sap_up(trx, l1sap);
}
-static int handle_ph_ra_ind(struct femtol1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind)
+static int handle_ph_ra_ind(struct femtol1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind,
+ struct msgb *l1p_msg)
{
struct gsm_bts_trx *trx = fl1->priv;
struct gsm_bts *bts = trx->bts;
struct gsm_bts_role_bts *btsb = bts->role;
struct gsm_lchan *lchan;
- struct osmo_phsap_prim pp;
- struct lapdm_channel *lc;
- uint8_t acc_delay;
+ struct osmo_phsap_prim *l1sap;
+ uint32_t fn;
+ uint8_t ra, acc_delay = 0;
+ int rc;
/* increment number of busy RACH slots, if required */
if (trx == bts->c0 &&
ra_ind->measParam.fRssi >= btsb->load.rach.busy_thresh)
btsb->load.rach.busy++;
- if (ra_ind->measParam.fLinkQuality < fl1->min_qual_rach)
+ if (ra_ind->measParam.fLinkQuality < fl1->min_qual_rach) {
+ msgb_free(l1p_msg);
return 0;
+ }
- /*
- * Check if this is a handover
- */
- lchan = l1if_hLayer_to_lchan(trx, ra_ind->hLayer2);
- if (lchan && lchan->ho.active == HANDOVER_ENABLED)
- return handle_handover(lchan, btsb, ra_ind);
+ if (ra_ind->measParam.i16BurstTiming > 0)
+ acc_delay = ra_ind->measParam.i16BurstTiming >> 2;
- /* increment number of RACH slots with valid RACH burst */
- if (trx == bts->c0)
+ /* increment number of RACH slots with valid non-handover RACH burst */
+ lchan = l1if_hLayer_to_lchan(trx, ra_ind->hLayer2);
+ if (trx == bts->c0 && !(lchan && lchan->ho.active == HANDOVER_ENABLED))
btsb->load.rach.access++;
- DEBUGP(DL1C, "Rx PH-RA.ind");
dump_meas_res(LOGL_DEBUG, &ra_ind->measParam);
- lc = get_lapdm_chan_by_hl2(fl1->priv, ra_ind->hLayer2);
- if (!lc) {
- LOGP(DL1C, LOGL_ERROR, "unable to resolve LAPD channel by hLayer2\n");
- return -ENODEV;
- }
-
- /* check for under/overflow / sign */
- if (!check_acc_delay(ra_ind, btsb, &acc_delay)) {
- LOGP(DL1C, LOGL_INFO, "ignoring RACH request %u > max_ta(%u)\n",
- acc_delay, btsb->max_ta);
+ if (ra_ind->msgUnitParam.u8Size != 1) {
+ LOGP(DL1C, LOGL_ERROR, "PH-RACH-INDICATION has %d bits\n",
+ ra_ind->sapi);
+ msgb_free(l1p_msg);
return 0;
}
- /* check for packet access */
- if (trx == bts->c0
- && (ra_ind->msgUnitParam.u8Buffer[0] & 0xf0) == 0x70) {
- LOGP(DL1C, LOGL_INFO, "RACH for packet access\n");
- return pcu_tx_rach_ind(bts, ra_ind->measParam.i16BurstTiming,
- ra_ind->msgUnitParam.u8Buffer[0], ra_ind->u32Fn);
- }
-
- osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_RACH,
- PRIM_OP_INDICATION, NULL);
-
- pp.u.rach_ind.ra = ra_ind->msgUnitParam.u8Buffer[0];
- pp.u.rach_ind.fn = ra_ind->u32Fn;
- pp.u.rach_ind.acc_delay = acc_delay;
+ fn = ra_ind->u32Fn;
+ ra = ra_ind->msgUnitParam.u8Buffer[0];
+ rc = msgb_trim(l1p_msg, sizeof(*l1sap));
+ if (rc < 0)
+ MSGB_ABORT(l1p_msg, "No room for primitive data\n");
+ l1sap = msgb_l1sap_prim(l1p_msg);
+ osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_INDICATION,
+ l1p_msg);
+ l1sap->u.rach_ind.ra = ra;
+ l1sap->u.rach_ind.acc_delay = acc_delay;
+ l1sap->u.rach_ind.fn = fn;
+ if (!lchan || lchan->ts->pchan == GSM_PCHAN_CCCH ||
+ lchan->ts->pchan == GSM_PCHAN_CCCH_SDCCH4)
+ l1sap->u.rach_ind.chan_nr = 0x88;
+ else
+ l1sap->u.rach_ind.chan_nr = gsm_lchan2chan_nr(lchan);
- return lapdm_phsap_up(&pp.oph, &lc->lapdm_dcch);
+ return l1sap_up(trx, l1sap);
}
/* handle any random indication from the L1 */
@@ -975,13 +975,12 @@ static int l1if_handle_ind(struct femtol1_hdl *fl1, struct msgb *msg)
case GsmL1_PrimId_PhConnectInd:
break;
case GsmL1_PrimId_PhReadyToSendInd:
- rc = handle_ph_readytosend_ind(fl1, &l1p->u.phReadyToSendInd);
- break;
+ return handle_ph_readytosend_ind(fl1, &l1p->u.phReadyToSendInd,
+ msg);
case GsmL1_PrimId_PhDataInd:
- rc = handle_ph_data_ind(fl1, &l1p->u.phDataInd, msg);
- break;
+ return handle_ph_data_ind(fl1, &l1p->u.phDataInd, msg);
case GsmL1_PrimId_PhRaInd:
- rc = handle_ph_ra_ind(fl1, &l1p->u.phRaInd);
+ return handle_ph_ra_ind(fl1, &l1p->u.phRaInd, msg);
break;
default:
break;
@@ -1437,49 +1436,6 @@ int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags)
return 0;
}
-/* send packet data request to L1 */
-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)
-{
- struct gsm_bts_trx *trx = ts->trx;
- struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
- struct msgb *msg;
- GsmL1_Prim_t *l1p;
- GsmL1_PhDataReq_t *data_req;
- GsmL1_MsgUnitParam_t *msu_param;
- 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 = l1p_msgb_alloc();
- l1p = msgb_l1prim(msg);
- l1p->id = GsmL1_PrimId_PhDataReq;
- data_req = &l1p->u.phDataReq;
- data_req->hLayer1 = fl1h->hLayer1;
- data_req->sapi = (is_ptcch) ? GsmL1_Sapi_Ptcch : GsmL1_Sapi_Pdtch;
- data_req->subCh = GsmL1_SubCh_NA;
- data_req->u8BlockNbr = block_nr;
- data_req->u8Tn = ts->nr;
- data_req->u32Fn = fn;
- msu_param = &data_req->msgUnitParam;
- msu_param->u8Size = len;
- memcpy(msu_param->u8Buffer, data, len);
-
- tx_to_gsmtap(fl1h, msg);
-
- /* transmit */
- if (osmo_wqueue_enqueue(&fl1h->write_q[MQ_L1_WRITE], msg) != 0) {
- LOGP(DL1P, LOGL_ERROR, "MQ_L1_WRITE queue full. Dropping msg.\n");
- msgb_free(msg);
- }
-
- return 0;
-}
-
/* get those femtol1_hdl.hw_info elements that sre in EEPROM */
static int get_hwinfo_eeprom(struct femtol1_hdl *fl1h)
{
@@ -1548,7 +1504,6 @@ struct femtol1_hdl *l1if_open(void *priv)
fl1h->priv = priv;
fl1h->clk_cal = 0;
fl1h->clk_use_eeprom = 1;
- fl1h->ul_power_target = -75; /* dBm default */
fl1h->min_qual_rach = MIN_QUAL_RACH;
fl1h->min_qual_norm = MIN_QUAL_NORM;
get_hwinfo_eeprom(fl1h);
@@ -1581,10 +1536,6 @@ struct femtol1_hdl *l1if_open(void *priv)
return NULL;
}
- fl1h->gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 1);
- if (fl1h->gsmtap)
- gsmtap_source_add_sink(fl1h->gsmtap);
-
return fl1h;
}
@@ -1712,69 +1663,3 @@ int l1if_rf_clock_info_correct(struct femtol1_hdl *fl1h)
}
#endif
-
-/*
- * 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.
- */
-inline int l1if_ms_pwr_ctrl(struct gsm_lchan *lchan, const int ul_power_target,
- const uint8_t ms_power, const float rxLevel)
-{
- float rx;
- int cur_dBm, new_dBm, new_pwr;
- const enum gsm_band band = lchan->ts->trx->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 = ul_power_target - rxLevel;
- if (rx >= 0 && rx < 1.5f)
- return 0;
- if (rx < 0 && rx > -1.5f)
- 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/osmo-bts-sysmo/l1_if.h b/src/osmo-bts-sysmo/l1_if.h
index 5f05f1df..d258f43b 100644
--- a/src/osmo-bts-sysmo/l1_if.h
+++ b/src/osmo-bts-sysmo/l1_if.h
@@ -46,16 +46,12 @@ struct femtol1_hdl {
uint32_t dsp_trace_f;
uint8_t clk_use_eeprom;
int clk_cal;
- int ul_power_target;
uint8_t clk_src;
float min_qual_rach;
float min_qual_norm;
char *calib_path;
struct llist_head wlc_list;
- struct gsmtap_inst *gsmtap;
- uint32_t gsmtap_sapi_mask;
-
void *priv; /* user reference */
struct osmo_timer_list alive_timer;
@@ -110,7 +106,9 @@ uint32_t l1if_lchan_to_hLayer(struct gsm_lchan *lchan);
struct gsm_lchan *l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer);
/* tch.c */
-int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
+void l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
+ const uint8_t *rtp_pl, unsigned int rtp_pl_len);
+int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg);
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
@@ -119,6 +117,13 @@ int l1if_set_ciphering(struct femtol1_hdl *fl1h,
struct gsm_lchan *lchan,
int dir_downlink);
+/* channel control */
+int l1if_rsl_chan_act(struct gsm_lchan *lchan);
+int l1if_rsl_chan_rel(struct gsm_lchan *lchan);
+int l1if_rsl_chan_mod(struct gsm_lchan *lchan);
+int l1if_rsl_deact_sacch(struct gsm_lchan *lchan);
+int l1if_rsl_mode_modify(struct gsm_lchan *lchan);
+
/* calibration loading */
int calib_load(struct femtol1_hdl *fl1h);
@@ -129,9 +134,6 @@ int l1if_rf_clock_info_correct(struct femtol1_hdl *fl1h);
/* public helpers for test */
int bts_check_for_ciph_cmd(struct femtol1_hdl *fl1h,
struct msgb *msg, struct gsm_lchan *lchan);
-void bts_check_for_first_ciphrd(struct femtol1_hdl *fl1h,
- GsmL1_MsgUnitParam_t *msgUnitParam,
- struct gsm_lchan *lchan);
inline int l1if_ms_pwr_ctrl(struct gsm_lchan *lchan, const int uplink_target,
const uint8_t ms_power, const float rxLevel);
#endif /* _FEMTO_L1_H */
diff --git a/src/osmo-bts-sysmo/main.c b/src/osmo-bts-sysmo/main.c
index 1150310c..b9f9fe98 100644
--- a/src/osmo-bts-sysmo/main.c
+++ b/src/osmo-bts-sysmo/main.c
@@ -38,6 +38,8 @@
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/ports.h>
+#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/core/gsmtap.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
@@ -47,6 +49,7 @@
#include <osmo-bts/bts_model.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/control_if.h>
+#include <osmo-bts/l1sap.h>
#define SYSMOBTS_RF_LOCK_PATH "/var/lock/bts_rf_lock"
@@ -62,6 +65,7 @@ static const char *config_file = "osmo-bts.cfg";
static int daemonize = 0;
static unsigned int dsp_trace = 0x71c00020;
static int rt_prio = -1;
+static char *gsmtap_ip = 0;
int bts_model_init(struct gsm_bts *bts)
{
@@ -165,6 +169,7 @@ static void print_help()
" -w --hw-version Print the targeted HW Version\n"
" -M --pcu-direct Force PCU to access message queue for "
"PDCH dchannel directly\n"
+ " -i --gsmtap-ip The destination IP used for GSMTAP.\n"
);
}
@@ -196,10 +201,11 @@ static void handle_options(int argc, char **argv)
{ "hw-version", 0, 0, 'w' },
{ "pcu-direct", 0, 0, 'M' },
{ "realtime", 1, 0, 'r' },
+ { "gsmtap-ip", 1, 0, 'i' },
{ 0, 0, 0, 0 }
};
- c = getopt_long(argc, argv, "hc:d:Dc:sTVe:p:w:Mr:",
+ c = getopt_long(argc, argv, "hc:d:Dc:sTVe:p:w:Mr:i:",
long_options, &option_idx);
if (c == -1)
break;
@@ -244,6 +250,9 @@ static void handle_options(int argc, char **argv)
case 'r':
rt_prio = atoi(optarg);
break;
+ case 'i':
+ gsmtap_ip = optarg;
+ break;
default:
break;
}
@@ -308,8 +317,9 @@ int main(int argc, char **argv)
bts_log_init(NULL);
+ bts = gsm_bts_alloc(tall_bts_ctx);
vty_init(&bts_vty_info);
- bts_vty_init(&bts_log_info);
+ bts_vty_init(bts, &bts_log_info);
handle_options(argc, argv);
@@ -325,7 +335,15 @@ int main(int argc, char **argv)
}
}
- bts = gsm_bts_alloc(tall_bts_ctx);
+ if (gsmtap_ip) {
+ gsmtap = gsmtap_source_init(gsmtap_ip, GSMTAP_UDP_PORT, 1);
+ if (!gsmtap) {
+ fprintf(stderr, "Failed during gsmtap_init()\n");
+ exit(1);
+ }
+ gsmtap_source_add_sink(gsmtap);
+ }
+
if (bts_init(bts) < 0) {
fprintf(stderr, "unable to open bts\n");
exit(1);
diff --git a/src/osmo-bts-sysmo/oml.c b/src/osmo-bts-sysmo/oml.c
index b1e3ac5c..4c9ac497 100644
--- a/src/osmo-bts-sysmo/oml.c
+++ b/src/osmo-bts-sysmo/oml.c
@@ -37,11 +37,27 @@
#include <osmo-bts/bts.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/handover.h>
+#include <osmo-bts/l1sap.h>
#include "l1_if.h"
#include "femtobts.h"
#include "utils.h"
+static int mph_info_chan_confirm(struct gsm_lchan *lchan,
+ enum osmo_mph_info_type type, uint8_t cause)
+{
+ struct osmo_phsap_prim l1sap;
+
+ memset(&l1sap, 0, sizeof(l1sap));
+ osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_CONFIRM,
+ NULL);
+ l1sap.u.info.type = type;
+ l1sap.u.info.u.act_cnf.chan_nr = gsm_lchan2chan_nr(lchan);
+ l1sap.u.info.u.act_cnf.cause = cause;
+
+ return l1sap_up(lchan->ts->trx, &l1sap);
+}
+
enum sapi_cmd_type {
SAPI_CMD_ACTIVATE,
SAPI_CMD_CONFIG_CIPHERING,
@@ -308,6 +324,7 @@ static const uint8_t trx_rqd_attr[] = { NM_ATT_RF_MAXPOWR_R };
static int trx_init(struct gsm_bts_trx *trx)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
+ struct gsm_bts_role_bts *btsb = bts_role_bts(trx->bts);
struct msgb *msg;
GsmL1_MphInitReq_t *mi_req;
GsmL1_DeviceParam_t *dev_par;
@@ -336,7 +353,7 @@ static int trx_init(struct gsm_bts_trx *trx)
dev_par->u16BcchArfcn = trx->bts->c0->arfcn;
dev_par->u8NbTsc = trx->bts->bsic & 7;
dev_par->fRxPowerLevel = trx_ms_pwr_ctrl_is_osmo(trx)
- ? 0.0 : fl1h->ul_power_target;
+ ? 0.0 : btsb->ul_power_target;
dev_par->fTxPowerLevel = 0.0;
LOGP(DL1C, LOGL_NOTICE, "Init TRX (ARFCN %u, TSC %u, RxPower % 2f dBm, "
@@ -974,7 +991,7 @@ static int sapi_activate_cb(struct gsm_lchan *lchan, int status)
gsm_lchan_name(lchan), status);
lchan_set_state(lchan, LCHAN_S_BROKEN);
sapi_clear_queue(&lchan->sapi_cmds);
- rsl_tx_chan_act_nack(lchan, RSL_ERR_PROCESSOR_OVERLOAD);
+ mph_info_chan_confirm(lchan, PRIM_INFO_ACTIVATE, RSL_ERR_PROCESSOR_OVERLOAD);
return -1;
}
@@ -984,14 +1001,16 @@ static int sapi_activate_cb(struct gsm_lchan *lchan, int status)
if (lchan->state != LCHAN_S_ACT_REQ)
return 0;
- struct gsm_time *time;
lchan_set_state(lchan, LCHAN_S_ACTIVE);
- time = bts_model_get_time(lchan->ts->trx->bts);
- rsl_tx_chan_act_ack(lchan, time);
+ mph_info_chan_confirm(lchan, PRIM_INFO_ACTIVATE, 0);
/* set the initial ciphering parameters for both directions */
- l1if_set_ciphering(fl1h, lchan, 0);
l1if_set_ciphering(fl1h, lchan, 1);
+ l1if_set_ciphering(fl1h, lchan, 0);
+ if (lchan->encr.alg_id)
+ lchan->ciph_state = LCHAN_CIPH_RXTX_REQ;
+ else
+ lchan->ciph_state = LCHAN_CIPH_NONE;
return 0;
}
@@ -1009,7 +1028,6 @@ static void enqueue_sapi_act_cmd(struct gsm_lchan *lchan, int sapi, int dir)
int lchan_activate(struct gsm_lchan *lchan)
{
- struct gsm_bts_role_bts *btsb = lchan->ts->trx->bts->role;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
const struct lchan_sapis *s4l = &sapis_for_lchan[lchan->type];
unsigned int i;
@@ -1043,8 +1061,6 @@ int lchan_activate(struct gsm_lchan *lchan)
#warning "FIXME: Should this be in sapi_activate_cb?"
lchan_init_lapdm(lchan);
- lchan->s = btsb->radio_link_timeout;
-
return 0;
}
@@ -1143,9 +1159,16 @@ static int chmod_modif_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
LOGPC(DL1C, LOGL_INFO, "RX_REQ -> RX_CONF\n");
lchan->ciph_state = LCHAN_CIPH_RX_CONF;
break;
- case LCHAN_CIPH_TXRX_REQ:
- LOGPC(DL1C, LOGL_INFO, "TX_REQ -> TX_CONF\n");
- lchan->ciph_state = LCHAN_CIPH_TXRX_CONF;
+ case LCHAN_CIPH_RX_CONF_TX_REQ:
+ LOGPC(DL1C, LOGL_INFO, "RX_CONF_TX_REQ -> RXTX_CONF\n");
+ lchan->ciph_state = LCHAN_CIPH_RXTX_CONF;
+ break;
+ case LCHAN_CIPH_RXTX_REQ:
+ LOGPC(DL1C, LOGL_INFO, "RXTX_REQ -> RX_CONF_TX_REQ\n");
+ lchan->ciph_state = LCHAN_CIPH_RX_CONF_TX_REQ;
+ break;
+ case LCHAN_CIPH_NONE:
+ LOGPC(DL1C, LOGL_INFO, "\n");
break;
default:
LOGPC(DL1C, LOGL_INFO, "unhandled state %u\n", lchan->ciph_state);
@@ -1319,7 +1342,7 @@ int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan)
return 0;
}
-int bts_model_rsl_mode_modify(struct gsm_lchan *lchan)
+int l1if_rsl_mode_modify(struct gsm_lchan *lchan)
{
if (lchan->state != LCHAN_S_ACTIVE)
return -1;
@@ -1429,7 +1452,7 @@ static int sapi_deactivate_cb(struct gsm_lchan *lchan, int status)
gsm_lchan_name(lchan));
lchan_set_state(lchan, LCHAN_S_BROKEN);
sapi_clear_queue(&lchan->sapi_cmds);
- rsl_tx_rf_rel_ack(lchan);
+ mph_info_chan_confirm(lchan, PRIM_INFO_DEACTIVATE, 0);
return -1;
}
@@ -1441,7 +1464,7 @@ static int sapi_deactivate_cb(struct gsm_lchan *lchan, int status)
return 0;
lchan_set_state(lchan, LCHAN_S_NONE);
- rsl_tx_rf_rel_ack(lchan);
+ mph_info_chan_confirm(lchan, PRIM_INFO_DEACTIVATE, 0);
return 0;
}
@@ -1516,7 +1539,7 @@ static int lchan_deactivate_sapis(struct gsm_lchan *lchan)
LOGP(DL1C, LOGL_ERROR, "%s all SAPIs already released?\n",
gsm_lchan_name(lchan));
lchan_set_state(lchan, LCHAN_S_BROKEN);
- rsl_tx_rf_rel_ack(lchan);
+ mph_info_chan_confirm(lchan, PRIM_INFO_DEACTIVATE, 0);
}
return res;
@@ -1556,13 +1579,6 @@ static int lchan_deactivate_sacch(struct gsm_lchan *lchan)
return 0;
}
-struct gsm_time *bts_model_get_time(struct gsm_bts *bts)
-{
- struct femtol1_hdl *fl1h = trx_femtol1_hdl(bts->c0);
-
- return &fl1h->gsm_time;
-}
-
/* callback from OML */
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
struct tlv_parsed *old_attr, struct tlv_parsed *new_attr,
@@ -1686,30 +1702,11 @@ int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
return oml_mo_statechg_nack(mo, NM_NACK_REQ_NOT_GRANT);
}
-int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp)
+
+int l1if_rsl_chan_act(struct gsm_lchan *lchan)
{
//uint8_t mode = *TLVP_VAL(tp, RSL_IE_CHAN_MODE);
//uint8_t type = *TLVP_VAL(tp, RSL_IE_ACT_TYPE);
- struct gsm48_chan_desc *cd;
-
- /* osmo-pcu calls this without a valid 'tp' parameter, so we
- * need to make sure we 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_activate(lchan);
return 0;
}
@@ -1718,7 +1715,7 @@ int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp)
* Modify the given lchan in the handover scenario. This is a lot like
* second channel activation but with some additional activation.
*/
-int bts_model_rsl_chan_mod(struct gsm_lchan *lchan)
+int l1if_rsl_chan_mod(struct gsm_lchan *lchan)
{
const struct lchan_sapis *s4l = &sapis_for_lchan[lchan->type];
unsigned int i;
@@ -1742,7 +1739,7 @@ int bts_model_rsl_chan_mod(struct gsm_lchan *lchan)
return 0;
}
-int bts_model_rsl_chan_rel(struct gsm_lchan *lchan)
+int l1if_rsl_chan_rel(struct gsm_lchan *lchan)
{
/* A duplicate RF Release Request, ignore it */
if (lchan->state == LCHAN_S_REL_REQ) {
@@ -1755,7 +1752,7 @@ int bts_model_rsl_chan_rel(struct gsm_lchan *lchan)
return 0;
}
-int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan)
+int l1if_rsl_deact_sacch(struct gsm_lchan *lchan)
{
/* Only de-activate the SACCH if the lchan is active */
if (lchan->state != LCHAN_S_ACTIVE)
diff --git a/src/osmo-bts-sysmo/sysmobts_vty.c b/src/osmo-bts-sysmo/sysmobts_vty.c
index 8b617d78..3c1dafa4 100644
--- a/src/osmo-bts-sysmo/sysmobts_vty.c
+++ b/src/osmo-bts-sysmo/sysmobts_vty.c
@@ -84,34 +84,6 @@ DEFUN(cfg_bts_no_auto_band, cfg_bts_no_auto_band_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_trx_gsmtap_sapi, cfg_trx_gsmtap_sapi_cmd,
- "HIDDEN", "HIDDEN")
-{
- struct gsm_bts_trx *trx = vty->index;
- struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
- int sapi;
-
- sapi = get_string_value(femtobts_l1sapi_names, argv[0]);
-
- fl1h->gsmtap_sapi_mask |= (1 << sapi);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_no_gsmtap_sapi, cfg_trx_no_gsmtap_sapi_cmd,
- "HIDDEN", "HIDDEN")
-{
- struct gsm_bts_trx *trx = vty->index;
- struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
- int sapi;
-
- sapi = get_string_value(femtobts_l1sapi_names, argv[0]);
-
- fl1h->gsmtap_sapi_mask &= ~(1 << sapi);
-
- return CMD_SUCCESS;
-}
-
DEFUN(cfg_trx_clkcal_eeprom, cfg_trx_clkcal_eeprom_cmd,
"clock-calibration eeprom",
"Use the eeprom clock calibration value\n")
@@ -203,15 +175,15 @@ DEFUN(cfg_trx_cal_path, cfg_trx_cal_path_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_trx_ul_power_target, cfg_trx_ul_power_target_cmd,
+DEFUN_DEPRECATED(cfg_trx_ul_power_target, cfg_trx_ul_power_target_cmd,
"uplink-power-target <-110-0>",
- "Set the nominal target Rx Level for uplink power control loop\n"
+ "Obsolete alias for bts uplink-power-target\n"
"Target uplink Rx level in dBm\n")
{
struct gsm_bts_trx *trx = vty->index;
- struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
+ struct gsm_bts_role_bts *btsb = bts_role_bts(trx->bts);
- fl1h->ul_power_target = atoi(argv[0]);
+ btsb->ul_power_target = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -497,37 +469,9 @@ void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts)
vty_out(vty, " auto-band%s", VTY_NEWLINE);
}
-/* 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;
-}
-
void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
- int i;
if (fl1h->clk_use_eeprom)
vty_out(vty, " clock-calibration eeprom%s", VTY_NEWLINE);
@@ -540,8 +484,6 @@ void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
vty_out(vty, " clock-source %s%s",
get_value_string(femtobts_clksrc_names, fl1h->clk_src),
VTY_NEWLINE);
- vty_out(vty, " uplink-power-target %d%s", fl1h->ul_power_target,
- VTY_NEWLINE);
vty_out(vty, " min-qual-rach %.0f%s", fl1h->min_qual_rach * 10.0f,
VTY_NEWLINE);
vty_out(vty, " min-qual-norm %.0f%s", fl1h->min_qual_norm * 10.0f,
@@ -549,14 +491,6 @@ void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
if (trx->nominal_power != sysmobts_get_nominal_power(trx))
vty_out(vty, " nominal-tx-power %d%s", trx->nominal_power,
VTY_NEWLINE);
-
- for (i = 0; i < 32; i++) {
- if (fl1h->gsmtap_sapi_mask & (1 << i)) {
- const char *name = get_value_string(femtobts_l1sapi_names, i);
- vty_out(vty, " gsmtap-sapi %s%s", osmo_str_tolower(name),
- VTY_NEWLINE);
- }
- }
}
int bts_model_vty_init(struct gsm_bts *bts)
@@ -578,20 +512,6 @@ int bts_model_vty_init(struct gsm_bts *bts)
NO_STR TRX_STR DSP_TRACE_F_STR,
"\n", "", 0);
- cfg_trx_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
- "gsmtap-sapi (",
- "|",")", VTY_DO_LOWER);
- cfg_trx_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
- "GSMTAP SAPI\n",
- "\n", "", 0);
-
- cfg_trx_no_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
- "no gsmtap-sapi (",
- "|",")", VTY_DO_LOWER);
- cfg_trx_no_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
- NO_STR "GSMTAP SAPI\n",
- "\n", "", 0);
-
install_element_ve(&show_dsp_trace_f_cmd);
install_element_ve(&show_sys_info_cmd);
install_element_ve(&show_trx_clksrc_cmd);
@@ -614,9 +534,6 @@ int bts_model_vty_init(struct gsm_bts *bts)
install_element(TRX_NODE, &cfg_trx_clkcal_def_cmd);
install_element(TRX_NODE, &cfg_trx_clksrc_cmd);
install_element(TRX_NODE, &cfg_trx_cal_path_cmd);
- install_element(TRX_NODE, &cfg_trx_gsmtap_sapi_cmd);
- install_element(TRX_NODE, &cfg_trx_no_gsmtap_sapi_cmd);
- install_element(TRX_NODE, &cfg_trx_ul_power_target_cmd);
install_element(TRX_NODE, &cfg_trx_min_qual_rach_cmd);
install_element(TRX_NODE, &cfg_trx_min_qual_norm_cmd);
install_element(TRX_NODE, &cfg_trx_nominal_power_cmd);
diff --git a/src/osmo-bts-sysmo/tch.c b/src/osmo-bts-sysmo/tch.c
index 9e8903b2..7cf49e3c 100644
--- a/src/osmo-bts-sysmo/tch.c
+++ b/src/osmo-bts-sysmo/tch.c
@@ -40,6 +40,7 @@
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/measurement.h>
#include <osmo-bts/amr.h>
+#include <osmo-bts/l1sap.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h>
@@ -425,7 +426,7 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
#define RTP_MSGB_ALLOC_SIZE 512
-/*! \brief call-back function for incoming RTP
+/*! \brief function for incoming RTP via TCH.req
* \param rs RTP Socket
* \param[in] rtp_pl buffer containing RTP payload
* \param[in] rtp_pl_len length of \a rtp_pl
@@ -437,34 +438,18 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
* yet, as things like the frame number, etc. are unknown at the time we
* pre-fill the primtive.
*/
-void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
- unsigned int rtp_pl_len)
+void l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
+ const uint8_t *rtp_pl, unsigned int rtp_pl_len)
{
- struct gsm_lchan *lchan = rs->priv;
- struct msgb *msg;
- GsmL1_Prim_t *l1p;
- GsmL1_PhDataReq_t *data_req;
- GsmL1_MsgUnitParam_t *msu_param;
uint8_t *payload_type;
uint8_t *l1_payload;
int rc;
- /* skip processing of incoming RTP frames if we are in loopback mode */
- if (lchan->loopback)
- return;
-
- msg = l1p_msgb_alloc();
- if (!msg) {
- LOGP(DRTP, LOGL_ERROR, "%s: Failed to allocate Rx payload.\n",
- gsm_lchan_name(lchan));
- return;
- }
+ DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan),
+ osmo_hexdump(rtp_pl, rtp_pl_len));
- l1p = msgb_l1prim(msg);
- data_req = &l1p->u.phDataReq;
- msu_param = &data_req->msgUnitParam;
- payload_type = &msu_param->u8Buffer[0];
- l1_payload = &msu_param->u8Buffer[1];
+ payload_type = &data[0];
+ l1_payload = &data[1];
switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_V1:
@@ -499,34 +484,13 @@ void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
if (rc < 0) {
LOGP(DRTP, LOGL_ERROR, "%s unable to parse RTP payload\n",
gsm_lchan_name(lchan));
- msgb_free(msg);
return;
}
- msu_param->u8Size = rc + 1;
-
+ *len = rc + 1;
- /* make sure the number of entries in the dl_tch_queue is never
- * more than 3 */
- {
- struct msgb *tmp;
- int count = 0;
-
- llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
- count++;
-
- DEBUGP(DL1C, "%s DL TCH queue length = %u\n",
- gsm_lchan_name(lchan), count);
-
- while (count >= 2) {
- tmp = msgb_dequeue(&lchan->dl_tch_queue);
- msgb_free(tmp);
- count--;
- }
- }
-
- /* enqueue msgb to be transmitted to L1 */
- msgb_enqueue(&lchan->dl_tch_queue, msg);
+ DEBUGP(DRTP, "%s RTP->L1: %s\n", gsm_lchan_name(lchan),
+ osmo_hexdump(data, *len));
}
static int is_recv_only(uint8_t speech_mode)
@@ -535,7 +499,7 @@ static int is_recv_only(uint8_t speech_mode)
}
/*! \brief receive a traffic L1 primitive for a given lchan */
-int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
+int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg)
{
GsmL1_Prim_t *l1p = msgb_l1prim(l1p_msg);
GsmL1_PhDataInd_t *data_ind = &l1p->u.phDataInd;
@@ -543,53 +507,18 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
uint8_t *payload = data_ind->msgUnitParam.u8Buffer + 1;
uint8_t payload_len;
struct msgb *rmsg = NULL;
+ struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)].lchan[l1sap_chan2ss(chan_nr)];
if (is_recv_only(lchan->abis_ip.speech_mode))
return -EAGAIN;
if (data_ind->msgUnitParam.u8Size < 1) {
- LOGP(DL1C, LOGL_ERROR, "%s Rx Payload size 0\n",
- gsm_lchan_name(lchan));
+ LOGP(DL1C, LOGL_ERROR, "chan_nr %d Rx Payload size 0\n",
+ chan_nr);
return -EINVAL;
}
payload_len = data_ind->msgUnitParam.u8Size - 1;
- if (lchan->loopback) {
- GsmL1_Prim_t *rl1p;
- GsmL1_PhDataReq_t *data_req;
- GsmL1_MsgUnitParam_t *msu_param;
-
- struct msgb *tmp;
- int count = 0;
-
- /* generate a new msgb from the paylaod */
- rmsg = l1p_msgb_alloc();
- if (!rmsg)
- return -ENOMEM;
-
- rl1p = msgb_l1prim(rmsg);
- data_req = &rl1p->u.phDataReq;
- msu_param = &data_req->msgUnitParam;
-
- memcpy(msu_param->u8Buffer,
- data_ind->msgUnitParam.u8Buffer,
- data_ind->msgUnitParam.u8Size);
- msu_param->u8Size = data_ind->msgUnitParam.u8Size;
-
- /* 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, rmsg);
-
- return 0;
- }
-
switch (payload_type) {
case GsmL1_TchPlType_Fr:
#if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
@@ -633,11 +562,20 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
}
if (rmsg) {
- /* hand rmsg to RTP code for transmission */
- if (lchan->abis_ip.rtp_socket)
- osmo_rtp_send_frame(lchan->abis_ip.rtp_socket,
- rmsg->data, rmsg->len, 160);
- msgb_free(rmsg);
+ struct osmo_phsap_prim *l1sap;
+
+ LOGP(DL1C, LOGL_DEBUG, "%s Rx -> RTP: %s\n",
+ gsm_lchan_name(lchan), osmo_hexdump(rmsg->data, rmsg->len));
+
+ /* add l1sap header */
+ rmsg->l2h = rmsg->data;
+ msgb_push(rmsg, sizeof(*l1sap));
+ rmsg->l1h = rmsg->data;
+ l1sap = msgb_l1sap_prim(rmsg);
+ osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_INDICATION, rmsg);
+ l1sap->u.tch.chan_nr = chan_nr;
+
+ return l1sap_up(trx, l1sap);
}
return 0;
diff --git a/tests/stubs.c b/tests/stubs.c
index 39604ab5..89ab8027 100644
--- a/tests/stubs.c
+++ b/tests/stubs.c
@@ -40,6 +40,8 @@ int bts_model_rsl_chan_mod(struct gsm_lchan *lchan)
{ return 0; }
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
unsigned int rtp_pl_len) {}
+int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
+{ return 0; }
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)
diff --git a/tests/sysmobts/sysmobts_test.c b/tests/sysmobts/sysmobts_test.c
index 7db12485..99fd4c7d 100644
--- a/tests/sysmobts/sysmobts_test.c
+++ b/tests/sysmobts/sysmobts_test.c
@@ -18,6 +18,8 @@
*/
#include <osmo-bts/bts.h>
+#include <osmo-bts/l1sap.h>
+#include <osmo-bts/power_control.h>
#include "femtobts.h"
#include "l1_if.h"
@@ -169,25 +171,30 @@ static void test_sysmobts_cipher(void)
/* Handle message sent before ciphering was received */
memcpy(&unit.u8Buffer[0], too_early_classmark, ARRAY_SIZE(too_early_classmark));
unit.u8Size = ARRAY_SIZE(too_early_classmark);
- bts_check_for_first_ciphrd(&fl1h, &unit, &lchan);
+ rc = bts_check_for_first_ciphrd(&lchan, unit.u8Buffer, unit.u8Size);
+ OSMO_ASSERT(rc == 0);
OSMO_ASSERT(lchan.ciph_state == LCHAN_CIPH_RX_CONF);
/* Now send the first ciphered message */
memcpy(&unit.u8Buffer[0], first_ciphered_cipher_cmpl, ARRAY_SIZE(first_ciphered_cipher_cmpl));
unit.u8Size = ARRAY_SIZE(first_ciphered_cipher_cmpl);
- bts_check_for_first_ciphrd(&fl1h, &unit, &lchan);
- OSMO_ASSERT(lchan.ciph_state == LCHAN_CIPH_TXRX_REQ);
+ rc = bts_check_for_first_ciphrd(&lchan, unit.u8Buffer, unit.u8Size);
+ OSMO_ASSERT(rc == 1);
+ /* we cannot test for lchan.ciph_state == * LCHAN_CIPH_RX_CONF_TX_REQ, as
+ * this happens asynchronously on the other side of the l1sap queue */
}
static void test_sysmobts_loop(void)
{
struct gsm_bts bts;
+ struct gsm_bts_role_bts btsb;
struct gsm_bts_trx trx;
struct gsm_bts_trx_ts ts;
struct gsm_lchan *lchan;
int ret;
memset(&bts, 0, sizeof(bts));
+ memset(&btsb, 0, sizeof(btsb));
memset(&trx, 0, sizeof(trx));
memset(&ts, 0, sizeof(ts));
@@ -195,8 +202,10 @@ static void test_sysmobts_loop(void)
lchan->ts = &ts;
ts.trx = &trx;
trx.bts = &bts;
+ bts.role = &btsb;
bts.band = GSM_BAND_1800;
trx.ms_power_control = 1;
+ btsb.ul_power_target = -75;
printf("Testing sysmobts power control\n");
@@ -204,7 +213,7 @@ static void test_sysmobts_loop(void)
lchan->state = LCHAN_S_NONE;
lchan->ms_power_ctrl.current = ms_pwr_ctl_lvl(GSM_BAND_1800, 0);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
- ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -60);
+ ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -60);
OSMO_ASSERT(ret == 0);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
@@ -213,24 +222,24 @@ static void test_sysmobts_loop(void)
* Now 15 dB too little and we should power it up. Could be a
* power level of 7 or 8 for 15 dBm
*/
- ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -90);
+ ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -90);
OSMO_ASSERT(ret == 1);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 7);
/* It should be clamped to level 0 and 30 dBm */
- ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -100);
+ ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -100);
OSMO_ASSERT(ret == 1);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 0);
/* Fix it and jump down */
lchan->ms_power_ctrl.fixed = 1;
- ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -60);
+ ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -60);
OSMO_ASSERT(ret == 0);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 0);
/* And leave it again */
lchan->ms_power_ctrl.fixed = 0;
- ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -40);
+ ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -40);
OSMO_ASSERT(ret == 1);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
}