aboutsummaryrefslogtreecommitdiffstats
path: root/src/common/rsl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/rsl.c')
-rw-r--r--src/common/rsl.c2032
1 files changed, 1496 insertions, 536 deletions
diff --git a/src/common/rsl.c b/src/common/rsl.c
index c0d43d0e..40690f05 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -2,6 +2,7 @@
/* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2011-2019 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
@@ -13,19 +14,18 @@
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-#include "btsconfig.h" /* for PACKAGE_VERSION */
-
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <stdbool.h>
+#include <inttypes.h>
#include <sys/types.h>
#include <arpa/inet.h>
@@ -56,9 +56,39 @@
#include <osmo-bts/l1sap.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/pcuif_proto.h>
+#include <osmo-bts/notification.h>
+#include <osmo-bts/asci.h>
//#define FAKE_CIPH_MODE_COMPL
+/* Parse power attenuation (in dB) from BS Power IE (see 9.3.4) */
+#define BS_POWER2DB(bs_power) \
+ ((bs_power & 0x0f) * 2)
+
+bool rsl_chan_rt_is_asci(enum rsl_cmod_crt chan_rt)
+{
+ switch (chan_rt) {
+ case RSL_CMOD_CRT_TCH_GROUP_Bm:
+ case RSL_CMOD_CRT_TCH_GROUP_Lm:
+ case RSL_CMOD_CRT_TCH_BCAST_Bm:
+ case RSL_CMOD_CRT_TCH_BCAST_Lm:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool rsl_chan_rt_is_vgcs(enum rsl_cmod_crt chan_rt)
+{
+ switch (chan_rt) {
+ case RSL_CMOD_CRT_TCH_GROUP_Bm:
+ case RSL_CMOD_CRT_TCH_GROUP_Lm:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int rsl_tx_error_report(struct gsm_bts_trx *trx, uint8_t cause, const uint8_t *chan_nr,
const uint8_t *link_id, const struct msgb *orig_msg);
@@ -69,6 +99,7 @@ static const unsigned int rsl_sacch_sitypes[] = {
RSL_SYSTEM_INFO_6,
RSL_SYSTEM_INFO_5bis,
RSL_SYSTEM_INFO_5ter,
+ RSL_SYSTEM_INFO_10,
RSL_EXT_MEAS_ORDER,
RSL_MEAS_INFO,
};
@@ -85,19 +116,6 @@ int osmo_in_array(unsigned int search, const unsigned int *arr, unsigned int siz
}
#define OSMO_IN_ARRAY(search, arr) osmo_in_array(search, arr, ARRAY_SIZE(arr))
-int msgb_queue_flush(struct llist_head *list)
-{
- struct msgb *msg, *msg2;
- int count = 0;
-
- llist_for_each_entry_safe(msg, msg2, list, list) {
- msgb_free(msg);
- count++;
- }
-
- return count;
-}
-
/* FIXME: move this to libosmocore */
void gsm48_gen_starting_time(uint8_t *out, struct gsm_time *gtime)
{
@@ -106,33 +124,236 @@ void gsm48_gen_starting_time(uint8_t *out, struct gsm_time *gtime)
out[1] = (gtime->t3 << 5) | gtime->t2;
}
-/* compute lchan->rsl_cmode and lchan->tch_mode from RSL CHAN MODE IE */
-static void lchan_tchmode_from_cmode(struct gsm_lchan *lchan,
- struct rsl_ie_chan_mode *cm)
+/* Handle RSL Channel Mode IE (see section 9.3.6) */
+static int rsl_handle_chan_mod_ie(struct gsm_lchan *lchan,
+ const struct tlv_parsed *tp,
+ uint8_t *cause)
{
+ const struct rsl_ie_chan_mode *cm;
+
+ if (!TLVP_PRES_LEN(tp, RSL_IE_CHAN_MODE, sizeof(*cm))) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Channel Mode IE is not present\n");
+ *cause = RSL_ERR_MAND_IE_ERROR;
+ return -ENODEV;
+ }
+
+ cm = (const struct rsl_ie_chan_mode *) TLVP_VAL(tp, RSL_IE_CHAN_MODE);
lchan->rsl_cmode = cm->spd_ind;
+ lchan->rsl_chan_rt = cm->chan_rt;
lchan->ts->trx->bts->dtxd = (cm->dtx_dtu & RSL_CMOD_DTXd) ? true : false;
- switch (cm->chan_rate) {
- case RSL_CMOD_SP_GSM1:
+ /* Octet 5: Channel rate and type */
+ switch (cm->chan_rt) {
+ case RSL_CMOD_CRT_SDCCH:
+ case RSL_CMOD_CRT_TCH_Bm:
+ case RSL_CMOD_CRT_TCH_Lm:
+ case RSL_CMOD_CRT_TCH_GROUP_Bm:
+ case RSL_CMOD_CRT_TCH_GROUP_Lm:
+ case RSL_CMOD_CRT_TCH_BCAST_Bm:
+ case RSL_CMOD_CRT_TCH_BCAST_Lm:
+ break;
+ case RSL_CMOD_CRT_OSMO_TCH_VAMOS_Bm:
+ case RSL_CMOD_CRT_OSMO_TCH_VAMOS_Lm:
+ /* Make sure that Osmocom specific TSC IE is present */
+ if (!TLVP_PRES_LEN(tp, RSL_IE_OSMO_TRAINING_SEQUENCE, 2)) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR,
+ "Training Sequence IE is not present\n");
+ *cause = RSL_ERR_MAND_IE_ERROR;
+ return -ENODEV;
+ }
+ break;
+ default:
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Channel Mode IE contains "
+ "unknown 'Channel rate and type' value 0x%02x\n",
+ cm->chan_rt);
+ *cause = RSL_ERR_IE_CONTENT;
+ return -ENOTSUP;
+ }
+
+#define RSL_CMODE(spd_ind, chan_rate) \
+ ((spd_ind << 8) | chan_rate)
+
+ /* Octet 6: Speech coding algorithm/data rate + transparency indicator.
+ * NOTE: coding of this octet depends on 'Speech or data indicator' */
+ switch (RSL_CMODE(cm->spd_ind, cm->chan_rate)) {
+ /* If octet 4 indicates signalling */
+ case RSL_CMODE(RSL_CMOD_SPD_SIGN, 0x00):
+ /* No resources required, all other values are reserved */
+ lchan->tch_mode = GSM48_CMODE_SIGN;
+ break;
+
+ /* If octet 4 indicates speech */
+ case RSL_CMODE(RSL_CMOD_SPD_SPEECH, RSL_CMOD_SP_GSM1):
lchan->tch_mode = GSM48_CMODE_SPEECH_V1;
break;
- case RSL_CMOD_SP_GSM2:
+ case RSL_CMODE(RSL_CMOD_SPD_SPEECH, RSL_CMOD_SP_GSM2):
lchan->tch_mode = GSM48_CMODE_SPEECH_EFR;
break;
- case RSL_CMOD_SP_GSM3:
+ case RSL_CMODE(RSL_CMOD_SPD_SPEECH, RSL_CMOD_SP_GSM3):
lchan->tch_mode = GSM48_CMODE_SPEECH_AMR;
break;
- case RSL_CMOD_SP_NT_14k5:
+ case RSL_CMODE(RSL_CMOD_SPD_SPEECH, RSL_CMOD_SP_GSM4):
+ lchan->tch_mode = GSM48_CMODE_SPEECH_V4;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_SPEECH, RSL_CMOD_SP_GSM5):
+ lchan->tch_mode = GSM48_CMODE_SPEECH_V5;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_SPEECH, RSL_CMOD_SP_GSM6):
+ lchan->tch_mode = GSM48_CMODE_SPEECH_V6;
+ break;
+
+ /* If octet 4 indicates non-transparent data */
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_14k5):
lchan->tch_mode = GSM48_CMODE_DATA_14k5;
break;
- case RSL_CMOD_SP_NT_12k0:
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_12k0):
lchan->tch_mode = GSM48_CMODE_DATA_12k0;
break;
- case RSL_CMOD_SP_NT_6k0:
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_6k0):
lchan->tch_mode = GSM48_CMODE_DATA_6k0;
break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_43k5):
+ lchan->tch_mode = GSM48_CMODE_DATA_43k5;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_28k8):
+ /* 28.8 kbit/s services, 29.0 kbit/s radio interface rate */
+ lchan->tch_mode = GSM48_CMODE_DATA_29k0;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_43k5_14k5):
+ lchan->tch_mode = GSM48_CMODE_DATA_43k5_14k5;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_29k0_14k5):
+ lchan->tch_mode = GSM48_CMODE_DATA_29k0_14k5;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_43k5_29k0):
+ lchan->tch_mode = GSM48_CMODE_DATA_43k5_29k0;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_14k5_43k5):
+ lchan->tch_mode = GSM48_CMODE_DATA_14k5_43k5;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_14k5_29k0):
+ lchan->tch_mode = GSM48_CMODE_DATA_14k5_29k0;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_29k0_43k5):
+ lchan->tch_mode = GSM48_CMODE_DATA_29k0_43k5;
+ break;
+
+ /* If octet 4 indicates transparent data */
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_32k0):
+ /* 32.0 kbit/s services, 32.0 kbit/s radio interface rate */
+ lchan->tch_mode = GSM48_CMODE_DATA_32k0;
+ lchan->csd_mode = LCHAN_CSD_M_T_32000;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_29k0):
+ /* 29.0 kbit/s services, 29.0 kbit/s radio interface rate */
+ lchan->tch_mode = GSM48_CMODE_DATA_29k0;
+ lchan->csd_mode = LCHAN_CSD_M_T_29000;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_14k4):
+ /* 14.4 kbit/s services, 14.5 kbit/s radio interface rate */
+ lchan->tch_mode = GSM48_CMODE_DATA_14k5;
+ lchan->csd_mode = LCHAN_CSD_M_T_14400;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_9k6):
+ /* 9.6 kbit/s services, 12.0 kbit/s radio interface rate */
+ lchan->tch_mode = GSM48_CMODE_DATA_12k0;
+ lchan->csd_mode = LCHAN_CSD_M_T_9600;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_4k8):
+ /* 4.8 kbit/s services, 6.0 kbit/s radio interface rate */
+ lchan->tch_mode = GSM48_CMODE_DATA_6k0;
+ lchan->csd_mode = LCHAN_CSD_M_T_4800;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_2k4):
+ /* 2.4 kbit/s *and less* services, 3.6 kbit/s radio interface rate */
+ lchan->tch_mode = GSM48_CMODE_DATA_3k6;
+ lchan->csd_mode = LCHAN_CSD_M_T_2400;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_1k2):
+ /* 2.4 kbit/s *and less* services, 3.6 kbit/s radio interface rate */
+ lchan->tch_mode = GSM48_CMODE_DATA_3k6;
+ lchan->csd_mode = LCHAN_CSD_M_T_1200;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_600):
+ /* 2.4 kbit/s *and less* services, 3.6 kbit/s radio interface rate */
+ lchan->tch_mode = GSM48_CMODE_DATA_3k6;
+ lchan->csd_mode = LCHAN_CSD_M_T_600;
+ break;
+ case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_1200_75):
+ /* 2.4 kbit/s *and less* services, 3.6 kbit/s radio interface rate */
+ lchan->tch_mode = GSM48_CMODE_DATA_3k6;
+ lchan->csd_mode = LCHAN_CSD_M_T_1200_75;
+ break;
+
+ default:
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Channel Mode IE contains "
+ "an unknown/unhandled combination of "
+ "'Speech or data indicator' 0x%02x and "
+ "'Speech coding algorithm/data rate' 0x%02x\n",
+ cm->spd_ind, cm->chan_rate);
+ *cause = RSL_ERR_IE_CONTENT;
+ return -ENOPROTOOPT;
+ }
+
+#undef RSL_CMODE
+
+ if (!bts_supports_cm(lchan->ts->trx->bts, cm)) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Channel type=0x%02x/mode=%s "
+ "is not supported by the PHY\n", cm->chan_rt,
+ gsm48_chan_mode_name(lchan->tch_mode));
+ *cause = RSL_ERR_SERV_OPT_UNAVAIL;
+ return -ENOTSUP;
+ }
+
+ return 0;
+}
+
+/* Handle RSL Channel Identification IE (see section 9.3.5) */
+static int rsl_handle_chan_ident_ie(struct gsm_lchan *lchan,
+ const struct tlv_parsed *tp,
+ uint8_t *cause)
+{
+ const struct gsm_bts_trx_ts *ts = lchan->ts;
+ const struct gsm_bts *bts = ts->trx->bts;
+ const struct gsm48_chan_desc *cd;
+
+ if (TLVP_PRES_LEN(tp, RSL_IE_CHAN_IDENT, sizeof(*cd) + 1)) {
+ /* Channel Description IE comes together with its IEI (see 9.3.5) */
+ cd = (const struct gsm48_chan_desc *) (TLVP_VAL(tp, RSL_IE_CHAN_IDENT) + 1);
+
+ /* The PHY may not support using different TSCs */
+ if (!osmo_bts_has_feature(bts->features, BTS_FEAT_MULTI_TSC)
+ && cd->h0.tsc != BTS_TSC(bts)) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "This PHY does not support "
+ "lchan TSC %u != BSIC-TSC %u, sending NACK\n",
+ cd->h0.tsc, BTS_TSC(bts));
+ *cause = RSL_ERR_SERV_OPT_UNIMPL;
+ return -ENOTSUP;
+ }
+ }
+
+ return 0;
+}
+
+/* Handle Osmocom specific TSC IE */
+static int rsl_handle_osmo_tsc_ie(struct gsm_lchan *lchan,
+ const struct tlv_parsed *tp,
+ uint8_t *cause)
+{
+ /* Osmocom specific IE indicating Training Sequence Code and Set */
+ if (TLVP_PRES_LEN(tp, RSL_IE_OSMO_TRAINING_SEQUENCE, 2)) {
+ const uint8_t *ie = TLVP_VAL(tp, RSL_IE_OSMO_TRAINING_SEQUENCE);
+ lchan->ts->tsc_set = ie[0] & 0x03; /* Range: 0..3 */
+ lchan->ts->tsc_rsl = ie[1] & 0x07; /* Range: 0..7 */
+ lchan->ts->tsc_rsl_configured = true;
+ } else {
+ lchan->ts->tsc_rsl_configured = false;
+ lchan->ts->tsc_rsl = 0xff;
+ lchan->ts->tsc_set = 0;
}
+ gsm_ts_apply_configured_tsc(lchan->ts);
+
+ return 0;
}
@@ -150,6 +371,16 @@ static bool chan_nr_is_dchan(uint8_t chan_nr)
return true;
}
+static struct gsm_bts_trx *trx_lookup_by_arfcn(struct llist_head *trx_list, uint16_t arfcn)
+{
+ struct gsm_bts_trx *trx;
+ llist_for_each_entry(trx, trx_list, list) {
+ if (trx->arfcn == arfcn)
+ return trx;
+ }
+ return NULL;
+}
+
static struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
const char *log_name)
{
@@ -269,6 +500,7 @@ static int rsl_tx_error_report(struct gsm_bts_trx *trx, uint8_t cause, const uin
/* 8.6.1 sending RF RESOURCE INDICATION */
int rsl_tx_rf_res(struct gsm_bts_trx *trx)
{
+ unsigned int tn, ln;
struct msgb *nmsg;
LOGP(DRSL, LOGL_INFO, "Tx RSL RF RESource INDication\n");
@@ -276,8 +508,48 @@ int rsl_tx_rf_res(struct gsm_bts_trx *trx)
nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr));
if (!nmsg)
return -ENOMEM;
- // FIXME: add interference levels of TRX
- msgb_tlv_put(nmsg, RSL_IE_RESOURCE_INFO, 0, NULL);
+
+ /* Add interference levels for each logical channel */
+ uint8_t *len = msgb_tl_put(nmsg, RSL_IE_RESOURCE_INFO);
+
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ const struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+
+ if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ continue;
+ if (ts->mo.nm_state.availability != NM_AVSTATE_OK)
+ continue;
+
+ for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
+ const struct gsm_lchan *lchan = &ts->lchan[ln];
+
+ /* No average interference value => no band */
+ if (lchan->meas.interf_meas_avg_dbm == 0)
+ continue;
+
+ /* Only for GSM_LCHAN_{SDCCH,TCH_F,TCH_H,PDTCH} */
+ switch (lchan->type) {
+ case GSM_LCHAN_SDCCH:
+ case GSM_LCHAN_TCH_F:
+ case GSM_LCHAN_TCH_H:
+ /* We're not interested in active CS lchans */
+ if (lchan->state == LCHAN_S_ACTIVE)
+ continue;
+ break;
+ case GSM_LCHAN_PDTCH:
+ break;
+ default:
+ continue;
+ }
+
+ msgb_v_put(nmsg, gsm_lchan2chan_nr_rsl(lchan));
+ msgb_v_put(nmsg, (lchan->meas.interf_band & 0x07) << 5);
+ }
+ }
+
+ /* Calculate length of the V part */
+ *len = msgb_l3len(nmsg) - 2;
+
rsl_trx_push_hdr(nmsg, RSL_MT_RF_RES_IND);
nmsg->trx = trx;
@@ -285,7 +557,7 @@ int rsl_tx_rf_res(struct gsm_bts_trx *trx)
}
/*
- * common channel releated messages
+ * common channel related messages
*/
/* 8.5.1 BCCH INFOrmation is received */
@@ -298,7 +570,13 @@ static int rsl_rx_bcch_info(struct gsm_bts_trx *trx, struct msgb *msg)
enum osmo_sysinfo_type osmo_si;
struct gsm48_system_information_type_2quater *si2q;
struct bitvec bv;
- rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+ const uint8_t *si_buf;
+ uint8_t prev_bs_ag_blks_res = 0xff; /* 0xff = unknown */
+
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_error_report(trx, RSL_ERR_PROTO, &cch->chan_nr, NULL, msg);
+ }
/* 9.3.30 System Info Type */
if (!TLVP_PRESENT(&tp, RSL_IE_SYSINFO_TYPE))
@@ -325,7 +603,8 @@ static int rsl_rx_bcch_info(struct gsm_bts_trx *trx, struct msgb *msg)
LOGP(DRSL, LOGL_INFO, " Rx RSL BCCH INFO (SI%s, %u bytes)\n",
get_value_string(osmo_sitype_strs, osmo_si), len);
- if (SYSINFO_TYPE_2quater == osmo_si) {
+ switch (osmo_si) {
+ case SYSINFO_TYPE_2quater:
si2q = (struct gsm48_system_information_type_2quater *) TLVP_VAL(&tp, RSL_IE_FULL_BCCH_INFO);
bv.data = si2q->rest_octets;
bv.data_len = GSM_MACBLOCK_LEN;
@@ -353,32 +632,68 @@ static int rsl_rx_bcch_info(struct gsm_bts_trx *trx, struct msgb *msg)
memset(GSM_BTS_SI2Q(bts, bts->si2q_index), GSM_MACBLOCK_PADDING, sizeof(sysinfo_buf_t));
memcpy(GSM_BTS_SI2Q(bts, bts->si2q_index), TLVP_VAL(&tp, RSL_IE_FULL_BCCH_INFO), len);
- } else {
+ break;
+ case SYSINFO_TYPE_3:
+ /* Keep previous BS_AG_BLKS_RES, used below */
+ if (GSM_BTS_HAS_SI(bts, SYSINFO_TYPE_3)) {
+ const struct gsm48_system_information_type_3 *si3 = GSM_BTS_SI(bts, SYSINFO_TYPE_3);
+ prev_bs_ag_blks_res = si3->control_channel_desc.bs_ag_blks_res;
+ }
+ /* fall-through */
+ default:
memset(bts->si_buf[osmo_si], GSM_MACBLOCK_PADDING, sizeof(sysinfo_buf_t));
memcpy(bts->si_buf[osmo_si], TLVP_VAL(&tp, RSL_IE_FULL_BCCH_INFO), len);
}
bts->si_valid |= (1 << osmo_si);
- if (SYSINFO_TYPE_3 == osmo_si) {
- if (trx->nr == 0 && num_agch(trx, "RSL") != 1) {
- lchan_deactivate(&trx->bts->c0->ts[0].lchan[CCCH_LCHAN]);
- /* will be reactivated by sapi_deactivate_cb() */
+ switch (osmo_si) {
+ case SYSINFO_TYPE_3:
+ /* If CCCH config on TS0 changed, reactivate the chan with the new config: */
+ if (trx->nr == 0 && trx->bts->c0->ts[0].lchan[CCCH_LCHAN].state != LCHAN_S_NONE &&
+ num_agch(trx, "RSL") != prev_bs_ag_blks_res) {
trx->bts->c0->ts[0].lchan[CCCH_LCHAN].rel_act_kind =
LCHAN_REL_ACT_REACT;
+ lchan_deactivate(&trx->bts->c0->ts[0].lchan[CCCH_LCHAN]);
+ /* will be reactivated by (see OS#1575):
+ * - bts-trx: lchan_deactivate()
+ * - sysmo,lc15,oc2g: lchan_deactivate()....[async]...sapi_deactivate_cb() */
}
/* decode original SI3 Rest Octets as sent by BSC */
- const uint8_t *si3_ro_buf = (uint8_t *) GSM_BTS_SI(bts, osmo_si);
- si3_ro_buf += offsetof(struct gsm48_system_information_type_3, rest_octets);
- osmo_gsm48_rest_octets_si3_decode(&bts->si3_ro_decoded, si3_ro_buf);
+ si_buf = (const uint8_t *) GSM_BTS_SI(bts, osmo_si);
+ si_buf += offsetof(struct gsm48_system_information_type_3, rest_octets);
+ osmo_gsm48_rest_octets_si3_decode(&bts->si3_ro_decoded, si_buf);
/* patch out GPRS indicator from binary if PCU is not connected; will be enabled
* after PCU connects */
regenerate_si3_restoctets(bts);
+ pcu_tx_si(trx->bts, SYSINFO_TYPE_3, true);
+ break;
+ case SYSINFO_TYPE_4:
+ /* decode original SI4 Rest Octets as sent by BSC */
+ si_buf = (const uint8_t *) GSM_BTS_SI(bts, osmo_si);
+ int si4_ro_offset = get_si4_ro_offset(si_buf);
+ if (si4_ro_offset > 0) {
+ osmo_gsm48_rest_octets_si4_decode(&bts->si4_ro_decoded,
+ si_buf + si4_ro_offset,
+ GSM_MACBLOCK_LEN - si4_ro_offset);
+ /* patch out GPRS indicator from binary if PCU is not connected; will be
+ * enabled after PCU connects */
+ regenerate_si4_restoctets(bts);
+ }
+ break;
+ case SYSINFO_TYPE_1:
+ /* Get the position of the NCH, if enabled. */
+ trx->bts->asci.pos_nch = pos_nch(trx, "BCCH INFO");
+ pcu_tx_si(trx->bts, SYSINFO_TYPE_1, true);
+ break;
+ case SYSINFO_TYPE_2:
+ case SYSINFO_TYPE_13:
+ pcu_tx_si(trx->bts, osmo_si, true);
+ break;
+ default:
+ break;
}
- if (SYSINFO_TYPE_13 == osmo_si)
- pcu_tx_si13(trx->bts, true);
-
} else if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) {
uint16_t len = TLVP_LEN(&tp, RSL_IE_L3_INFO);
if (len > sizeof(sysinfo_buf_t))
@@ -393,10 +708,20 @@ static int rsl_rx_bcch_info(struct gsm_bts_trx *trx, struct msgb *msg)
bts->si_valid &= ~(1 << osmo_si);
LOGP(DRSL, LOGL_INFO, " RX RSL Disabling BCCH INFO (SI%s)\n",
get_value_string(osmo_sitype_strs, osmo_si));
- if (SYSINFO_TYPE_13 == osmo_si)
- pcu_tx_si13(trx->bts, false);
- if (SYSINFO_TYPE_3 == osmo_si)
+ switch (osmo_si) {
+ case SYSINFO_TYPE_13:
+ pcu_tx_si(trx->bts, SYSINFO_TYPE_13, false);
+ break;
+ case SYSINFO_TYPE_3:
memset(&bts->si3_ro_decoded, 0, sizeof(bts->si3_ro_decoded));
+ pcu_tx_si(trx->bts, SYSINFO_TYPE_3, false);
+ break;
+ case SYSINFO_TYPE_1:
+ pcu_tx_si(trx->bts, SYSINFO_TYPE_1, false);
+ break;
+ default:
+ break;
+ }
}
osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts);
@@ -465,7 +790,10 @@ static int rsl_rx_paging_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
const uint8_t *identity_lv;
int rc;
- rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_error_report(trx, RSL_ERR_PROTO, &cch->chan_nr, NULL, msg);
+ }
if (!TLVP_PRESENT(&tp, RSL_IE_PAGING_GROUP) ||
!TLVP_PRESENT(&tp, RSL_IE_MS_IDENTITY))
@@ -500,7 +828,10 @@ static int rsl_rx_sms_bcast_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
bool extended_cbch = false;
int rc;
- rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_error_report(trx, RSL_ERR_PROTO, &cch->chan_nr, NULL, msg);
+ }
if (!TLVP_PRESENT(&tp, RSL_IE_CB_CMD_TYPE) ||
!TLVP_PRESENT(&tp, RSL_IE_SMSCB_MSG))
@@ -522,6 +853,100 @@ static int rsl_rx_sms_bcast_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
return 0;
}
+/* Broadcast notification about new VGCS/VBS call on every dedicated channel.
+ * This is required for MSs that are currently in dedicated mode that there is an ongoing call and on which channel
+ * the call is active. Most MSs in dedicated mode may not be able to receive the NCH otherwise.
+ * MSs that do not support ASCI will ignore it, as it is an unsupported message for them.
+ */
+static int asci_broadcast_facch(struct gsm_bts *bts, const uint8_t *group_call_ref, const uint8_t *chan_desc,
+ uint8_t chan_desc_len, unsigned int count)
+{
+ uint8_t notif[23];
+ struct msgb *msg, *cmsg;
+ struct gsm_bts_trx *trx;
+ struct gsm_lchan *lchan;
+ unsigned int tn, ln, n;
+ int rc;
+
+ rc = bts_asci_notify_facch_gen_msg(bts, notif, group_call_ref, chan_desc, chan_desc_len);
+ if (rc < 0)
+ return rc;
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ for (ln = 0; ln < ARRAY_SIZE(trx->ts[tn].lchan); ln++) {
+ lchan = &trx->ts[tn].lchan[ln];
+ if (!lchan_is_dcch(lchan))
+ continue;
+ if (lchan->state != LCHAN_S_ACTIVE)
+ continue;
+ msg = rsl_rll_simple(RSL_MT_UNIT_DATA_REQ, gsm_lchan2chan_nr(lchan), 0x00, 0);
+ msg->l3h = msg->tail; /* emulate rsl_rx_rll() behaviour */
+ msgb_tl16v_put(msg, RSL_IE_L3_INFO, sizeof(notif), (uint8_t *) &notif);
+ for (n = 1; n < count; n++) {
+ cmsg = msgb_copy(msg, "FACCH copy");
+ lapdm_rslms_recvmsg(cmsg, &lchan->lapdm_ch);
+ }
+ lapdm_rslms_recvmsg(msg, &lchan->lapdm_ch);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Number of times to broadcast ASCI call on every dedicated channel. */
+#define ASCI_BROADCAST_NUM 3
+
+/* 8.5.10 NOTIFICATION COMMAND */
+static int rsl_rx_notification_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
+{
+ struct abis_rsl_cchan_hdr *cch = msgb_l2(msg);
+ struct tlv_parsed tp;
+ uint8_t command_indicator;
+ int rc;
+
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_error_report(trx, RSL_ERR_PROTO, &cch->chan_nr, NULL, msg);
+ }
+
+ if (cch->chan_nr != RSL_CHAN_PCH_AGCH) {
+ LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): chan nr is not Downlink CCCH\n", __func__);
+ return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT, &cch->chan_nr, NULL, msg);
+ }
+
+ if (!TLVP_PRES_LEN(&tp, RSL_IE_CMD_INDICATOR, 1))
+ return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR, &cch->chan_nr, NULL, msg);
+ command_indicator = *TLVP_VAL(&tp, RSL_IE_CMD_INDICATOR);
+
+ switch (command_indicator) {
+ case RSL_CMD_INDICATOR_START:
+ /* we need at least a Group Call Reference to start notification */
+ if (!TLVP_PRES_LEN(&tp, RSL_IE_GROUP_CALL_REF, 5))
+ return rsl_tx_error_report(trx, RSL_ERR_OPT_IE_ERROR, &cch->chan_nr, NULL, msg);
+ rc = bts_asci_notification_add(trx->bts, TLVP_VAL(&tp, RSL_IE_GROUP_CALL_REF),
+ TLVP_VAL(&tp, RSL_IE_CHAN_DESC), TLVP_LEN(&tp, RSL_IE_CHAN_DESC),
+ (struct rsl_ie_nch_drx_info *) TLVP_VAL(&tp, RSL_IE_NCH_DRX_INFO));
+ /* Broadcast to FACCH */
+ asci_broadcast_facch(trx->bts, TLVP_VAL(&tp, RSL_IE_GROUP_CALL_REF), TLVP_VAL(&tp, RSL_IE_CHAN_DESC),
+ TLVP_LEN(&tp, RSL_IE_CHAN_DESC), ASCI_BROADCAST_NUM);
+ break;
+ case RSL_CMD_INDICATOR_STOP:
+ if (!TLVP_PRES_LEN(&tp, RSL_IE_GROUP_CALL_REF, 5)) {
+ /* interpret this as stopping of all notification */
+ rc = bts_asci_notification_reset(trx->bts);
+ } else {
+ rc = bts_asci_notification_del(trx->bts, TLVP_VAL(&tp, RSL_IE_GROUP_CALL_REF));
+ }
+ break;
+ default:
+ return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT, &cch->chan_nr, NULL, msg);
+ }
+
+ return rc;
+}
+
/* OSMO_ETWS_CMD - proprietary extension as TS 48.058 has no standardized way to do this :( */
static int rsl_rx_osmo_etws_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
{
@@ -529,7 +954,10 @@ static int rsl_rx_osmo_etws_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
struct gsm_bts *bts = trx->bts;
struct tlv_parsed tp;
- rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_error_report(trx, RSL_ERR_PROTO, &cch->chan_nr, NULL, msg);
+ }
if (!TLVP_PRESENT(&tp, RSL_IE_SMSCB_MSG))
return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR, &cch->chan_nr, NULL, msg);
@@ -571,10 +999,19 @@ static int rsl_rx_osmo_etws_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
* \param[out] buf Output buffer, must be caller-allocated and hold at least len + 2 or sizeof(sysinfo_buf_t) bytes
* \param[out] valid pointer to bit-mask of 'valid' System information types
* \param[in] current input data (L3 without L2/L1 header)
- * \param[in] osmo_si Sytstem Infrormation Type (SYSINFO_TYPE_*)
+ * \param[in] osmo_si Sytstem Information Type (SYSINFO_TYPE_*)
* \param[in] len length of \a current in octets */
static inline void lapdm_ui_prefix(uint8_t *buf, uint32_t *valid, const uint8_t *current, uint8_t osmo_si, uint16_t len)
{
+ /* Special case for short header SI. Do not pre-fix the two-byte UI header. */
+ switch (osmo_si) {
+ case SYSINFO_TYPE_10:
+ (*valid) |= (1 << osmo_si);
+ memset(buf, GSM_MACBLOCK_PADDING, sizeof(sysinfo_buf_t));
+ memcpy(buf, current, len);
+ return;
+ }
+
/* We have to pre-fix with the two-byte LAPDM UI header */
if (len > sizeof(sysinfo_buf_t) - 2) {
LOGP(DRSL, LOGL_ERROR, "Truncating received SI%s (%u -> %zu) to prepend LAPDM UI header (2 bytes)\n",
@@ -593,7 +1030,7 @@ static inline void lapdm_ui_prefix(uint8_t *buf, uint32_t *valid, const uint8_t
/*! Prefix a given SACCH frame with a L2/LAPDm UI header and store it in given BTS SACCH buffer
* \param[out] bts BTS in whose System Information State we shall store
* \param[in] current input data (L3 without L2/L1 header)
- * \param[in] osmo_si Sytstem Infrormation Type (SYSINFO_TYPE_*)
+ * \param[in] osmo_si Sytstem Information Type (SYSINFO_TYPE_*)
* \param[in] len length of \a current in octets */
static inline void lapdm_ui_prefix_bts(struct gsm_bts *bts, const uint8_t *current, uint8_t osmo_si, uint16_t len)
{
@@ -603,7 +1040,7 @@ static inline void lapdm_ui_prefix_bts(struct gsm_bts *bts, const uint8_t *curre
/*! Prefix a given SACCH frame with a L2/LAPDm UI header and store it in given lchan SACCH buffer
* \param[out] lchan Logical Channel in whose System Information State we shall store
* \param[in] current input data (L3 without L2/L1 header)
- * \param[in] osmo_si Sytstem Infrormation Type (SYSINFO_TYPE_*)
+ * \param[in] osmo_si Sytstem Information Type (SYSINFO_TYPE_*)
* \param[in] len length of \a current in octets */
static inline void lapdm_ui_prefix_lchan(struct gsm_lchan *lchan, const uint8_t *current, uint8_t osmo_si, uint16_t len)
{
@@ -618,7 +1055,10 @@ static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg)
uint8_t rsl_si;
enum osmo_sysinfo_type osmo_si;
- rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_error_report(trx, RSL_ERR_PROTO, NULL, NULL, msg);
+ }
/* 9.3.30 System Info Type */
if (!TLVP_PRESENT(&tp, RSL_IE_SYSINFO_TYPE))
@@ -682,13 +1122,256 @@ static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg)
}
+/* Parser for ip.access specific MS/BS Power parameters */
+static int parse_power_ctrl_params(struct gsm_power_ctrl_params *params,
+ const uint8_t *data, size_t data_len)
+{
+ const struct tlv_p_entry *ie;
+ struct tlv_parsed tp[3];
+ unsigned int i;
+ int rc;
+
+ /* There can be multiple RSL_IPAC_EIE_MEAS_AVG_CFG, so we use tlv_parse2() */
+ rc = tlv_parse2(&tp[0], ARRAY_SIZE(tp), &rsl_ipac_eie_tlvdef,
+ data, data_len, 0, 0);
+ if (rc < 0)
+ return rc;
+
+ /* Either of RSL_IPAC_EIE_{BS,MS}_PWR_CTL must be present */
+ if (TLVP_PRESENT(&tp[0], RSL_IPAC_EIE_BS_PWR_CTL) &&
+ TLVP_PRESENT(&tp[0], RSL_IPAC_EIE_MS_PWR_CTL))
+ return -EINVAL;
+
+ /* (TV) Thresholds: {L,U}_RXLEV_XX_P and {L,U}_RXQUAL_XX_P */
+ if ((ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_BS_PWR_CTL)) != NULL ||
+ (ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_MS_PWR_CTL)) != NULL) {
+ const struct ipac_preproc_pc_thresh *thresh;
+
+ thresh = (const struct ipac_preproc_pc_thresh *) ie->val;
+
+ params->rxlev_meas.lower_thresh = thresh->l_rxlev;
+ params->rxlev_meas.upper_thresh = thresh->u_rxlev;
+
+ params->rxqual_meas.lower_thresh = thresh->l_rxqual;
+ params->rxqual_meas.upper_thresh = thresh->u_rxqual;
+ }
+
+ /* Osmocom extension, C/I related thresholds: */
+ if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_MS_PWR_CTL, sizeof(struct osmo_preproc_pc_thresh))) {
+ const struct osmo_preproc_pc_thresh *osmo_thresh;
+ ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_MS_PWR_CTL);
+ osmo_thresh = (const struct osmo_preproc_pc_thresh *) ie->val;
+ params->ci_fr_meas.lower_thresh = osmo_thresh->l_ci_fr;
+ params->ci_fr_meas.upper_thresh = osmo_thresh->u_ci_fr;
+
+ params->ci_hr_meas.lower_thresh = osmo_thresh->l_ci_hr;
+ params->ci_hr_meas.upper_thresh = osmo_thresh->u_ci_hr;
+
+ params->ci_amr_fr_meas.lower_thresh = osmo_thresh->l_ci_amr_fr;
+ params->ci_amr_fr_meas.upper_thresh = osmo_thresh->u_ci_amr_fr;
+
+ params->ci_amr_hr_meas.lower_thresh = osmo_thresh->l_ci_amr_hr;
+ params->ci_amr_hr_meas.upper_thresh = osmo_thresh->u_ci_amr_hr;
+
+ params->ci_sdcch_meas.lower_thresh = osmo_thresh->l_ci_sdcch;
+ params->ci_sdcch_meas.upper_thresh = osmo_thresh->u_ci_sdcch;
+
+ params->ci_gprs_meas.lower_thresh = osmo_thresh->l_ci_gprs;
+ params->ci_gprs_meas.upper_thresh = osmo_thresh->u_ci_gprs;
+ }
+
+ /* (TV) PC Threshold Comparators */
+ if ((ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_PC_THRESH_COMP)) != NULL) {
+ const struct ipac_preproc_pc_comp *thresh_comp;
+
+ thresh_comp = (const struct ipac_preproc_pc_comp *) ie->val;
+
+ /* RxLev: P1, N1, P2, N2 (see 3GPP TS 45.008, A.3.2.1, a & b) */
+ params->rxlev_meas.lower_cmp_p = thresh_comp->p1;
+ params->rxlev_meas.lower_cmp_n = thresh_comp->n1;
+ params->rxlev_meas.upper_cmp_p = thresh_comp->p2;
+ params->rxlev_meas.upper_cmp_n = thresh_comp->n2;
+
+ /* RxQual: P3, N3, P4, N4 (see 3GPP TS 45.008, A.3.2.1, c & d) */
+ params->rxqual_meas.lower_cmp_p = thresh_comp->p3;
+ params->rxqual_meas.lower_cmp_n = thresh_comp->n3;
+ params->rxqual_meas.upper_cmp_p = thresh_comp->p4;
+ params->rxqual_meas.upper_cmp_n = thresh_comp->n4;
+
+ /* Minimum interval between power level changes (P_Con_INTERVAL) */
+ params->ctrl_interval = thresh_comp->pc_interval;
+
+ /* Power increase / reduce step size: POWER_{INC,RED}_STEP_SIZE */
+ params->inc_step_size_db = thresh_comp->inc_step_size;
+ params->red_step_size_db = thresh_comp->red_step_size;
+ }
+
+ /* Osmocom extension, C/I related thresholds: */
+ if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_PC_THRESH_COMP, sizeof(struct osmo_preproc_pc_thresh))) {
+ const struct osmo_preproc_pc_comp *osmo_thresh_comp;
+ ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_PC_THRESH_COMP);
+ osmo_thresh_comp = (const struct osmo_preproc_pc_comp *) ie->val;
+ #define SET_PREPROC_PC(PARAMS, FROM, TYPE) \
+ (PARAMS)->TYPE##_meas.lower_cmp_p = (FROM)->TYPE.lower_p; \
+ (PARAMS)->TYPE##_meas.lower_cmp_n = (FROM)->TYPE.lower_n; \
+ (PARAMS)->TYPE##_meas.upper_cmp_p = (FROM)->TYPE.upper_p; \
+ (PARAMS)->TYPE##_meas.upper_cmp_n = (FROM)->TYPE.upper_n
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_fr);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_hr);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_amr_fr);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_amr_hr);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_sdcch);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_gprs);
+ #undef SET_PREPROC_PC
+ }
+
+ /* (TLV) Measurement Averaging parameters for RxLev/RxQual */
+ for (i = 0; i < ARRAY_SIZE(tp); i++) {
+ const struct ipac_preproc_ave_cfg *ave_cfg;
+ struct gsm_power_ctrl_meas_params *mp;
+
+ ie = TLVP_GET(&tp[i], RSL_IPAC_EIE_MEAS_AVG_CFG);
+ if (ie == NULL)
+ break;
+
+ if (ie->len < sizeof(*ave_cfg))
+ return -EINVAL;
+
+ ave_cfg = (const struct ipac_preproc_ave_cfg *) ie->val;
+
+ switch (ave_cfg->param_id) {
+ case IPAC_RXQUAL_AVE:
+ mp = &params->rxqual_meas;
+ break;
+ case IPAC_RXLEV_AVE:
+ mp = &params->rxlev_meas;
+ break;
+ default:
+ /* Skip unknown parameters */
+ continue;
+ }
+
+ mp->h_reqave = ave_cfg->h_reqave;
+ mp->h_reqt = ave_cfg->h_reqt;
+
+ mp->algo = ave_cfg->ave_method + 1;
+ switch (ave_cfg->ave_method) {
+ case IPAC_OSMO_EWMA_AVE:
+ if (ie->len > sizeof(*ave_cfg))
+ mp->ewma.alpha = ave_cfg->params[0];
+ break;
+
+ /* FIXME: not implemented */
+ case IPAC_UNWEIGHTED_AVE:
+ case IPAC_WEIGHTED_AVE:
+ case IPAC_MEDIAN_AVE:
+ break;
+ }
+ }
+
+ /* (TLV) Measurement Averaging parameters for C/I (Osmocom extension)*/
+ if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG, sizeof(struct osmo_preproc_ave_cfg))) {
+ ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG);
+ const struct osmo_preproc_ave_cfg *cfg = (const struct osmo_preproc_ave_cfg *) ie->val;
+ unsigned params_offset = 0;
+ #define SET_AVE_CFG(PARAMS, FROM, TYPE, PARAM_OFFSET) do {\
+ if ((FROM)->TYPE.ave_enabled) { \
+ (PARAMS)->TYPE##_meas.h_reqave = (FROM)->TYPE.h_reqave; \
+ (PARAMS)->TYPE##_meas.h_reqt = (FROM)->TYPE.h_reqt; \
+ (PARAMS)->TYPE##_meas.algo = (FROM)->TYPE.ave_method + 1; \
+ switch ((FROM)->TYPE.ave_method) { \
+ case IPAC_OSMO_EWMA_AVE: \
+ if (ie->len > sizeof(*cfg) + (PARAM_OFFSET)) { \
+ (PARAMS)->TYPE##_meas.ewma.alpha = (FROM)->params[PARAM_OFFSET]; \
+ (PARAM_OFFSET)++; \
+ } \
+ break; \
+ /* FIXME: not implemented */ \
+ case IPAC_UNWEIGHTED_AVE: \
+ case IPAC_WEIGHTED_AVE: \
+ case IPAC_MEDIAN_AVE: \
+ break; \
+ } \
+ } else { \
+ (PARAMS)->TYPE##_meas.algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE; \
+ } \
+ } while(0)
+ SET_AVE_CFG(params, cfg, ci_fr, params_offset);
+ SET_AVE_CFG(params, cfg, ci_hr, params_offset);
+ SET_AVE_CFG(params, cfg, ci_amr_fr, params_offset);
+ SET_AVE_CFG(params, cfg, ci_amr_hr, params_offset);
+ SET_AVE_CFG(params, cfg, ci_sdcch, params_offset);
+ SET_AVE_CFG(params, cfg, ci_gprs, params_offset);
+ #undef SET_AVE_CFG
+ }
+
+ return 0;
+}
+
+/* ip.access specific Measurement Pre-processing Defaults for MS/BS Power control */
+static int rsl_rx_meas_preproc_dft(struct gsm_bts_trx *trx, struct msgb *msg)
+{
+ const struct gsm_bts *bts = trx->bts;
+ struct gsm_power_ctrl_params *params;
+ const struct tlv_p_entry *ie;
+ struct tlv_parsed tp;
+
+ LOGPTRX(trx, DRSL, LOGL_INFO, "Rx Measurement Pre-processing Defaults\n");
+
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_error_report(trx, RSL_ERR_PROTO, NULL, NULL, msg);
+ }
+
+ /* TLV (O) BS Power Parameters IE */
+ if ((ie = TLVP_GET(&tp, RSL_IE_BS_POWER_PARAM)) != NULL) {
+ /* Allocate a new chunk and initialize with default values */
+ params = talloc(trx, struct gsm_power_ctrl_params);
+ power_ctrl_params_def_reset(params, true);
+
+ if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) == 0) {
+ /* Initially it points to the global defaults */
+ if (trx->bs_dpc_params != &bts->bs_dpc_params)
+ talloc_free(trx->bs_dpc_params);
+ trx->bs_dpc_params = params;
+ } else {
+ LOGPTRX(trx, DRSL, LOGL_ERROR, "Failed to parse BS Power Parameters IE\n");
+ rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT, NULL, NULL, msg);
+ talloc_free(params);
+ }
+ }
+
+ /* TLV (O) MS Power Parameters IE */
+ if ((ie = TLVP_GET(&tp, RSL_IE_MS_POWER_PARAM)) != NULL) {
+ /* Allocate a new chunk and initialize with default values */
+ params = talloc(trx, struct gsm_power_ctrl_params);
+ power_ctrl_params_def_reset(params, false);
+
+ if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) == 0) {
+ /* Initially it points to the global defaults */
+ if (trx->ms_dpc_params != &bts->ms_dpc_params)
+ talloc_free(trx->ms_dpc_params);
+ trx->ms_dpc_params = params;
+ } else {
+ LOGPTRX(trx, DRSL, LOGL_ERROR, "Failed to parse MS Power Parameters IE\n");
+ rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT, NULL, NULL, msg);
+ talloc_free(params);
+ }
+ }
+
+ return 0;
+}
+
/* 8.5.6 IMMEDIATE ASSIGN COMMAND is received */
static int rsl_rx_imm_ass(struct gsm_bts_trx *trx, struct msgb *msg)
{
struct abis_rsl_cchan_hdr *cch = msgb_l2(msg);
struct tlv_parsed tp;
- rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_error_report(trx, RSL_ERR_PROTO, &cch->chan_nr, NULL, msg);
+ }
if (!TLVP_PRESENT(&tp, RSL_IE_FULL_IMM_ASS_INFO))
return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR, &cch->chan_nr, NULL, msg);
@@ -701,6 +1384,45 @@ static int rsl_rx_imm_ass(struct gsm_bts_trx *trx, struct msgb *msg)
msg->l2h = NULL;
msg->len = TLVP_LEN(&tp, RSL_IE_FULL_IMM_ASS_INFO);
+ /* Early Immediate Assignment: when there is a lot of latency on Abis, the Abis roundtrip of Chan Activ -> Chan
+ * Activ ACK -> Immediate Assignment may take so long that each MS sends a second RACH for Chan Rqd, reserving
+ * two SDCCH for each request but using only one. To help with that, the Early IA feature in osmo-bsc sends the
+ * Immediate Assignment without waiting for the Channel Activation ACK. This may then be too early, and the MS
+ * may not be able to establish a channel. So to help with Early IA, look up whether the target lchan is already
+ * active. If not, then hold back the RR Immediate Assignment message, and send it once L1 has confirmed that
+ * the channel is active. Hence we still wait for the activation, but don't need the Abis roundtrip of Activ ACK
+ * -> Immediate Assignment via the BSC.
+ * If anything is wrong with the sizes or the lchan lookup, behave normally, i.e. do not do the RR IA caching,
+ * but just send the RR message to the MS as-is.
+ * 'trx' here is the TRX of the BCCH channel. To find the correct TRX for the IMM ASS target, we need to look up
+ * the ARFCN that is contained in the IMM ASS message. When frequency hopping is enabled, there will not be an
+ * ARFCN, so we cannot support early-IA with frequency hopping enabled. */
+ if (msg->len >= sizeof(struct gsm48_imm_ass)) {
+ struct gsm48_imm_ass *rr_ia = (void*)msg->data;
+ if (rr_ia->chan_desc.h0.h == 0) {
+ /* hopping is disabled. */
+ struct gsm_bts_trx *ia_target_trx;
+ uint16_t arfcn;
+ arfcn = (rr_ia->chan_desc.h0.arfcn_high << 8) + rr_ia->chan_desc.h0.arfcn_low;
+
+ ia_target_trx = trx_lookup_by_arfcn(&trx->bts->trx_list, arfcn);
+ if (ia_target_trx) {
+ /* found the ARFCN's trx */
+ struct gsm_lchan *ia_target_lchan;
+ ia_target_lchan = lchan_lookup(ia_target_trx, rr_ia->chan_desc.chan_nr, "Early IA check: ");
+ if (ia_target_lchan && ia_target_lchan->state != LCHAN_S_ACTIVE) {
+ /* Target lchan is not yet active. Cache the IA.
+ * If a previous IA is still lingering, free it. */
+ msgb_free(ia_target_lchan->early_rr_ia);
+ ia_target_lchan->early_rr_ia = msg;
+
+ /* return 1 means: don't msgb_free() the msg */
+ return 1;
+ }
+ }
+ }
+ }
+
/* put into the AGCH queue of the BTS */
if (bts_agch_enqueue(trx->bts, msg) < 0) {
/* if there is no space in the queue: send DELETE IND */
@@ -738,7 +1460,7 @@ static int tx_rf_rel_ack(struct gsm_lchan *lchan, uint8_t chan_nr)
/* 8.4.19 sending RF CHANnel RELease ACKnowledge */
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
{
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
bool send_rel_ack;
switch (lchan->rel_act_kind) {
@@ -748,26 +1470,40 @@ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
case LCHAN_REL_ACT_PCU:
switch (lchan->ts->pchan) {
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
if (lchan->ts->dyn.pchan_is != GSM_PCHAN_PDCH) {
LOGP(DRSL, LOGL_ERROR, "%s (ss=%d) PDCH release: not in PDCH mode\n",
gsm_ts_and_pchan_name(lchan->ts), lchan->nr);
/* well, what to do about it ... carry on and hope it's fine. */
}
- /* remember the fact that the TS is now released */
- lchan->ts->dyn.pchan_is = GSM_PCHAN_NONE;
- /* Continue to ack the release below. (This is a non-standard rel ack invented
- * specifically for GSM_PCHAN_TCH_F_TCH_H_PDCH). */
- send_rel_ack = true;
+ if (lchan->ts->dyn.pchan_want != GSM_PCHAN_PDCH) {
+ /* Continue to ack the release below. (This is a non-standard rel ack invented
+ * specifically for GSM_PCHAN_OSMO_DYN). */
+ /* remember the fact that the TS is now released */
+ lchan->ts->dyn.pchan_is = GSM_PCHAN_NONE;
+ send_rel_ack = true;
+ } else {
+ /* Administrteively locked TRX, no need to
+ inform BSC. Keep pchan_is for when we are
+ unlocked again, since lower layers are stil
+ lconfigured for PDCH but we simply annonced
+ non-availability to PCU */
+ send_rel_ack = false;
+ }
break;
case GSM_PCHAN_TCH_F_PDCH:
/* GSM_PCHAN_TCH_F_PDCH, does not require a rel ack. The caller
* l1sap_info_rel_cnf() will continue with bts_model_ts_disconnect(). */
send_rel_ack = false;
break;
+ case GSM_PCHAN_PDCH:
+ /* Release was instructed by the BTS, for instance because the TRX is
+ * administrateively Locked */
+ send_rel_ack = false;
+ break;
default:
LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "PCU rel ack for unexpected lchan kind %s\n",
- gsm_pchan_name(lchan->rel_act_kind));
+ gsm_pchan_name(lchan->ts->pchan));
/* Release certainly was not requested by the BSC via RSL, so don't ack. */
send_rel_ack = false;
break;
@@ -782,20 +1518,14 @@ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
}
if (!send_rel_ack) {
- LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "%s not sending REL ACK\n", gsm_lchan_name(lchan));
+ LOGPLCHAN(lchan, DRSL, LOGL_INFO, "not sending REL ACK\n");
return 0;
}
- LOGP(DRSL, LOGL_NOTICE, "%s (ss=%d) %s Tx CHAN REL ACK\n",
+ LOGP(DRSL, LOGL_INFO, "%s (ss=%d) %s Tx CHAN REL ACK\n",
gsm_ts_and_pchan_name(lchan->ts), lchan->nr,
gsm_lchant_name(lchan->type));
- /*
- * Free the LAPDm resources now that the BTS
- * has released all the resources.
- */
- lapdm_channel_exit(&lchan->lapdm_ch);
-
return tx_rf_rel_ack(lchan, chan_nr);
}
@@ -804,10 +1534,10 @@ static 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 chan_nr = gsm_lchan2chan_nr_rsl(lchan);
uint8_t ie[2];
- LOGP(DRSL, LOGL_NOTICE, "%s (ss=%d) %s Tx CHAN ACT ACK\n",
+ LOGP(DRSL, LOGL_INFO, "%s (ss=%d) %s Tx CHAN ACT ACK\n",
gsm_ts_and_pchan_name(lchan->ts), lchan->nr,
gsm_lchant_name(lchan->type));
@@ -826,28 +1556,50 @@ static int rsl_tx_chan_act_ack(struct gsm_lchan *lchan)
return abis_bts_rsl_sendmsg(msg);
}
-/* 8.4.7 sending HANDOver DETection */
-int rsl_tx_hando_det(struct gsm_lchan *lchan, uint8_t *ho_delay)
+/* common helper function for *_DETECT */
+static int _rsl_tx_detect(struct gsm_lchan *lchan, uint8_t msg_type, uint8_t *acc_delay)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
-
- LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Sending HANDOver DETect\n");
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
/* 9.3.17 Access Delay */
- if (ho_delay)
- msgb_tv_put(msg, RSL_IE_ACCESS_DELAY, *ho_delay);
+ if (acc_delay)
+ msgb_tv_put(msg, RSL_IE_ACCESS_DELAY, *acc_delay);
- rsl_dch_push_hdr(msg, RSL_MT_HANDO_DET, chan_nr);
+ rsl_dch_push_hdr(msg, msg_type, chan_nr);
msg->trx = lchan->ts->trx;
return abis_bts_rsl_sendmsg(msg);
}
+/* 8.4.7 sending HANDOver DETection */
+int rsl_tx_hando_det(struct gsm_lchan *lchan, uint8_t *ho_delay)
+{
+ LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Sending HANDOver DETect\n");
+
+ return _rsl_tx_detect(lchan, RSL_MT_HANDO_DET, ho_delay);
+}
+
+/* 8.4.22 sending LISTENER DETection */
+int rsl_tx_listener_det(struct gsm_lchan *lchan, uint8_t *acc_delay)
+{
+ LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Sending LISTENER DETect\n");
+
+ return _rsl_tx_detect(lchan, RSL_MT_LISTENER_DET, acc_delay);
+}
+
+/* 8.4.21 sending TALKER DETection */
+int rsl_tx_talker_det(struct gsm_lchan *lchan, uint8_t *acc_delay)
+{
+ LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Sending TALKER DETect\n");
+
+ return _rsl_tx_detect(lchan, RSL_MT_TALKER_DET, acc_delay);
+}
+
/* 8.4.3 sending CHANnel ACTIVation Negative ACK */
static int _rsl_tx_chan_act_nack(struct gsm_bts_trx *trx, uint8_t chan_nr, uint8_t cause,
struct gsm_lchan *lchan)
@@ -855,7 +1607,7 @@ static int _rsl_tx_chan_act_nack(struct gsm_bts_trx *trx, uint8_t chan_nr, uint8
struct msgb *msg;
if (lchan)
- LOGP(DRSL, LOGL_NOTICE, "%s: ", gsm_lchan_name(lchan));
+ LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "");
else
LOGP(DRSL, LOGL_NOTICE, "0x%02x: ", chan_nr);
LOGPC(DRSL, LOGL_NOTICE, "Sending Channel Activated NACK: cause = 0x%02x\n", cause);
@@ -872,14 +1624,14 @@ static int _rsl_tx_chan_act_nack(struct gsm_bts_trx *trx, uint8_t chan_nr, uint8
return abis_bts_rsl_sendmsg(msg);
}
static int rsl_tx_chan_act_nack(struct gsm_lchan *lchan, uint8_t cause) {
- return _rsl_tx_chan_act_nack(lchan->ts->trx, gsm_lchan2chan_nr(lchan), cause, lchan);
+ return _rsl_tx_chan_act_nack(lchan->ts->trx, gsm_lchan2chan_nr_rsl(lchan), cause, lchan);
}
/* Send an RSL Channel Activation Ack if cause is zero, a Nack otherwise. */
int rsl_tx_chan_act_acknack(struct gsm_lchan *lchan, uint8_t cause)
{
if (lchan->rel_act_kind != LCHAN_REL_ACT_RSL) {
- LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "not sending CHAN ACT %s\n",
+ LOGPLCHAN(lchan, DRSL, LOGL_INFO, "not sending CHAN ACT %s\n",
cause ? "NACK" : "ACK");
return 0;
}
@@ -890,10 +1642,10 @@ int rsl_tx_chan_act_acknack(struct gsm_lchan *lchan, uint8_t cause)
}
/* 8.4.4 sending CONNection FAILure */
-int rsl_tx_conn_fail(struct gsm_lchan *lchan, uint8_t cause)
+int rsl_tx_conn_fail(const struct gsm_lchan *lchan, uint8_t cause)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Sending Connection Failure: cause = 0x%02x\n", cause);
@@ -996,17 +1748,13 @@ static void clear_lchan_for_pdch_activ(struct gsm_lchan *lchan)
{
/* These values don't apply to PDCH, just clear them. Particularly the encryption must be
* cleared, or we would enable encryption on PDCH with parameters remaining from the TCH. */
- lchan->ms_power = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, 0);
- lchan->ms_power_ctrl.current = lchan->ms_power;
- lchan->ms_power_ctrl.fixed = 0;
lchan->rsl_cmode = 0;
lchan->tch_mode = 0;
memset(&lchan->encr, 0, sizeof(lchan->encr));
memset(&lchan->ho, 0, sizeof(lchan->ho));
- lchan->bs_power = 0;
- lchan->ms_power = 0;
memset(&lchan->ms_power_ctrl, 0, sizeof(lchan->ms_power_ctrl));
- lchan->rqd_ta = 0;
+ memset(&lchan->bs_power_ctrl, 0, sizeof(lchan->bs_power_ctrl));
+ lchan->ta_ctrl.current = 0;
copy_sacch_si_to_lchan(lchan);
memset(&lchan->tch, 0, sizeof(lchan->tch));
}
@@ -1015,13 +1763,14 @@ static void clear_lchan_for_pdch_activ(struct gsm_lchan *lchan)
* Store the CHAN_ACTIV msg, connect the L1 timeslot in the proper type and
* then invoke rsl_rx_chan_activ() with msg.
*/
-static int dyn_ts_l1_reconnect(struct gsm_bts_trx_ts *ts, struct msgb *msg)
+static int dyn_ts_l1_reconnect(struct gsm_bts_trx_ts *ts)
{
DEBUGP(DRSL, "%s dyn_ts_l1_reconnect\n", gsm_ts_and_pchan_name(ts));
switch (ts->dyn.pchan_want) {
case GSM_PCHAN_TCH_F:
case GSM_PCHAN_TCH_H:
+ case GSM_PCHAN_SDCCH8_SACCH8C:
break;
case GSM_PCHAN_PDCH:
/* Only the first lchan matters for PDCH */
@@ -1035,9 +1784,6 @@ static int dyn_ts_l1_reconnect(struct gsm_bts_trx_ts *ts, struct msgb *msg)
return -EINVAL;
}
- /* We will feed this back to rsl_rx_chan_activ() later */
- ts->dyn.pending_chan_activ = msg;
-
/* Disconnect, continue connecting from cb_ts_disconnected(). */
DEBUGP(DRSL, "%s Disconnect\n", gsm_ts_and_pchan_name(ts));
return bts_model_ts_disconnect(ts);
@@ -1045,14 +1791,23 @@ static int dyn_ts_l1_reconnect(struct gsm_bts_trx_ts *ts, struct msgb *msg)
static enum gsm_phys_chan_config dyn_pchan_from_chan_nr(uint8_t chan_nr)
{
- uint8_t cbits = chan_nr & RSL_CHAN_NR_MASK;
+ uint8_t cbits = chan_nr >> 3;
switch (cbits) {
- case RSL_CHAN_Bm_ACCHs:
+ case ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs:
return GSM_PCHAN_TCH_F;
- case RSL_CHAN_Lm_ACCHs:
- case (RSL_CHAN_Lm_ACCHs + RSL_CHAN_NR_1):
+ case ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(0):
+ case ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(1):
return GSM_PCHAN_TCH_H;
- case RSL_CHAN_OSMO_PDCH:
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(0):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(1):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(2):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(3):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(4):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(5):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(6):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(7):
+ return GSM_PCHAN_SDCCH8_SACCH8C;
+ case ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH:
return GSM_PCHAN_PDCH;
default:
LOGP(DRSL, LOGL_ERROR,
@@ -1062,24 +1817,145 @@ static enum gsm_phys_chan_config dyn_pchan_from_chan_nr(uint8_t chan_nr)
}
}
+/* Parse RSL_IE_OSMO_REP_ACCH_CAP */
+static int parse_repeated_acch_capability(struct gsm_lchan *lchan, struct tlv_parsed *tp)
+{
+ /* 3GPP TS 24.008, section 10.5.1.7 defines a Repeated ACCH Capability
+ * bit that indicates if REPEATED FACCH/SACCH is supported or not.
+ * Unfortunately there is not 3gpp spec that describes how this bit
+ * should be communicated in the RSL CHANNEL ACTIVATION. For osmo-bts
+ * we will use a propritary IE. */
+
+ memset(&lchan->rep_acch_cap, 0, sizeof(lchan->rep_acch_cap));
+
+ if (!TLVP_PRES_LEN(tp, RSL_IE_OSMO_REP_ACCH_CAP, sizeof(lchan->rep_acch_cap)))
+ return 0;
+
+ if (!osmo_bts_has_feature(lchan->ts->trx->bts->features, BTS_FEAT_ACCH_REP))
+ return -RSL_ERR_OPT_IE_ERROR;
+
+ memcpy(&lchan->rep_acch_cap, TLVP_VAL(tp, RSL_IE_OSMO_REP_ACCH_CAP),
+ sizeof(lchan->rep_acch_cap));
+
+ return 0;
+}
+
+/* Parse RSL_IE_OSMO_TOP_ACCH_CAP */
+static int parse_temporary_overpower_acch_capability(struct gsm_lchan *lchan,
+ const struct tlv_parsed *tp)
+{
+ memset(&lchan->top_acch_cap, 0, sizeof(lchan->top_acch_cap));
+
+ if (!TLVP_PRES_LEN(tp, RSL_IE_OSMO_TEMP_OVP_ACCH_CAP, sizeof(lchan->top_acch_cap)))
+ return 0;
+
+ if (!osmo_bts_has_feature(lchan->ts->trx->bts->features, BTS_FEAT_ACCH_TEMP_OVP))
+ return -RSL_ERR_OPT_IE_ERROR;
+
+ memcpy(&lchan->top_acch_cap,
+ TLVP_VAL(tp, RSL_IE_OSMO_TEMP_OVP_ACCH_CAP),
+ sizeof(lchan->top_acch_cap));
+
+ /* Simplify checking whether the overpower is enabled at all: allow
+ * testing just one parameter (overpower_db > 0) instead of all three. */
+ if (!lchan->top_acch_cap.sacch_enable && !lchan->top_acch_cap.facch_enable)
+ lchan->top_acch_cap.overpower_db = 0;
+
+ return 0;
+}
+
+/* Parse (O) MultiRate configuration IE (see 9.3.52) */
+static int parse_multirate_config(struct gsm_lchan *lchan,
+ const struct tlv_parsed *tp)
+{
+ int rc;
+
+ if (!TLVP_PRESENT(tp, RSL_IE_MR_CONFIG)) {
+ /* Included if the Channel Mode indicates that a multi-rate codec is used */
+ if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
+ LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Missing MultiRate conf IE "
+ "(TCH mode is %s)\n", gsm48_chan_mode_name(lchan->tch_mode));
+ /* Init lchan->tch.amr_mr with hard-coded default values */
+ amr_init_mr_conf_def(lchan);
+ goto parsed;
+ }
+ return 0;
+ }
+
+ /* Included if the Channel Mode indicates that a multi-rate codec is used */
+ if (lchan->tch_mode != GSM48_CMODE_SPEECH_AMR) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Unexpected MultiRate conf IE "
+ "(TCH mode is %s)\n", gsm48_chan_mode_name(lchan->tch_mode));
+ return -RSL_ERR_OPT_IE_ERROR;
+ }
+
+ rc = amr_parse_mr_conf(&lchan->tch.amr_mr,
+ TLVP_VAL(tp, RSL_IE_MR_CONFIG),
+ TLVP_LEN(tp, RSL_IE_MR_CONFIG));
+ if (rc < 0) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Error parsing MultiRate conf IE\n");
+ return -RSL_ERR_IE_CONTENT;
+ }
+
+parsed:
+ amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan), &lchan->tch.amr_mr);
+ lchan->tch.last_cmr = AMR_CMR_NONE;
+ return 0;
+}
+
/* 8.4.1 CHANnel ACTIVation is received */
static int rsl_rx_chan_activ(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
struct gsm_lchan *lchan = msg->lchan;
struct gsm_bts_trx_ts *ts = lchan->ts;
- struct rsl_ie_chan_mode *cm;
+ struct gsm_bts_trx_ts *primary_ts;
struct tlv_parsed tp;
- uint8_t type;
+ const struct tlv_p_entry *ie;
+ uint8_t type, cause;
+ bool reactivation = false;
int rc;
- if (lchan->state != LCHAN_S_NONE) {
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_chan_act_nack(lchan, RSL_ERR_PROTO);
+ }
+
+ /* 9.3.3 Activation Type */
+ if (!TLVP_PRESENT(&tp, RSL_IE_ACT_TYPE)) {
+ LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "missing Activation Type\n");
+ return rsl_tx_chan_act_nack(lchan, RSL_ERR_MAND_IE_ERROR);
+ }
+ type = *TLVP_VAL(&tp, RSL_IE_ACT_TYPE);
+ if ((type & RSL_ACT_TYPE_REACT)) {
+ type -= RSL_ACT_TYPE_REACT;
+ reactivation = true;
+ }
+
+ /* If Activation Type is IMMEDIATE ASSIGNMENT, we expect L3 info with establishment. */
+ lchan->l3_info_estab = (type == RSL_ACT_INTRA_IMM_ASS);
+
+ if (!reactivation && lchan->state != LCHAN_S_NONE) {
LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "error: lchan is not available, but in state: %s.\n",
gsm_lchans_name(lchan->state));
return rsl_tx_chan_act_nack(lchan, RSL_ERR_EQUIPMENT_FAIL);
}
- if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) {
+ if (reactivation && lchan->state == LCHAN_S_NONE) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "error: reactivation on inactive lchan.\n");
+ return rsl_tx_chan_act_nack(lchan, RSL_ERR_EQUIPMENT_FAIL);
+ }
+
+ /* We need to pick the real TS here to check NM state: */
+ primary_ts = ts->vamos.is_shadow ? ts->vamos.peer : ts;
+ if (primary_ts->mo.nm_state.operational != NM_OPSTATE_ENABLED ||
+ primary_ts->mo.nm_state.availability != NM_AVSTATE_OK) {
+ LOGP(DRSL, LOGL_ERROR, "%s rx chan activ but TS not in nm_state oper=ENABLED avail=OK, nack!\n",
+ gsm_ts_and_pchan_name(ts));
+ return rsl_tx_chan_act_nack(lchan, RSL_ERR_RR_UNAVAIL);
+ }
+
+ if (ts->pchan == GSM_PCHAN_OSMO_DYN) {
ts->dyn.pchan_want = dyn_pchan_from_chan_nr(dch->chan_nr);
DEBUGP(DRSL, "%s rx chan activ\n", gsm_ts_and_pchan_name(ts));
@@ -1089,39 +1965,37 @@ static int rsl_rx_chan_activ(struct msgb *msg)
* mode than this activation needs it to be.
* Re-connect, then come back to rsl_rx_chan_activ().
*/
- rc = dyn_ts_l1_reconnect(ts, msg);
+ rc = dyn_ts_l1_reconnect(ts);
if (rc)
return rsl_tx_chan_act_nack(lchan, RSL_ERR_NORMAL_UNSPEC);
+ /* will be fed back to rsl_rx_chan_activ() later */
+ OSMO_ASSERT(lchan->pending_chan_activ == NULL);
+ lchan->pending_chan_activ = msg;
/* indicate that the msgb should not be freed. */
return 1;
}
}
- LOGPLCHAN(lchan, DRSL, LOGL_DEBUG, "rx Channel Activation in state: %s.\n",
- gsm_lchans_name(lchan->state));
-
- /* Initialize channel defaults */
- lchan->ms_power = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, 0);
- lchan->ms_power_ctrl.current = lchan->ms_power;
- lchan->ms_power_ctrl.fixed = 0;
-
- rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+ /* Initialize MS Power Control defaults */
+ lchan->ms_power_ctrl = (struct lchan_power_ctrl_state) {
+ .max = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, 0),
+ .current = lchan->ms_power_ctrl.max,
+ };
- /* 9.3.3 Activation Type */
- if (!TLVP_PRESENT(&tp, RSL_IE_ACT_TYPE)) {
- LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "missing Activation Type\n");
- return rsl_tx_chan_act_nack(lchan, RSL_ERR_MAND_IE_ERROR);
- }
- type = *TLVP_VAL(&tp, RSL_IE_ACT_TYPE);
+ /* Initialize BS Power Control defaults */
+ lchan->bs_power_ctrl = (struct lchan_power_ctrl_state) {
+ .max = 2 * 15, /* maximum defined in 9.3.4 */
+ .current = 0,
+ };
/* 9.3.6 Channel Mode */
if (type != RSL_ACT_OSMO_PDCH) {
- if (!TLVP_PRESENT(&tp, RSL_IE_CHAN_MODE)) {
- LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "missing Channel Mode\n");
- return rsl_tx_chan_act_nack(lchan, RSL_ERR_MAND_IE_ERROR);
- }
- cm = (struct rsl_ie_chan_mode *) TLVP_VAL(&tp, RSL_IE_CHAN_MODE);
- lchan_tchmode_from_cmode(lchan, cm);
+ if (rsl_handle_chan_mod_ie(lchan, &tp, &cause) != 0)
+ return rsl_tx_chan_act_nack(lchan, cause);
+ if (rsl_handle_chan_ident_ie(lchan, &tp, &cause) != 0)
+ return rsl_tx_chan_act_nack(lchan, cause);
+ if (rsl_handle_osmo_tsc_ie(lchan, &tp, &cause) != 0)
+ return rsl_tx_chan_act_nack(lchan, cause);
}
/* 9.3.7 Encryption Information */
@@ -1149,28 +2023,73 @@ static int rsl_rx_chan_activ(struct msgb *msg)
}
/* 9.3.4 BS Power */
- if (TLVP_PRES_LEN(&tp, RSL_IE_BS_POWER, 1))
- lchan->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER);
+ if (TLVP_PRES_LEN(&tp, RSL_IE_BS_POWER, 1)) {
+ if (*TLVP_VAL(&tp, RSL_IE_BS_POWER) & (1 << 4)) {
+ LOGPLCHAN(lchan, DRSL, LOGL_NOTICE,
+ "Fast Power Control is not supported\n");
+ return rsl_tx_chan_act_nack(lchan, RSL_ERR_SERV_OPT_UNIMPL);
+ }
+
+ uint8_t red = BS_POWER2DB(*TLVP_VAL(&tp, RSL_IE_BS_POWER));
+
+ /* BS power reduction is generally not allowed on BCCH/CCCH carrier.
+ * However, we allow it in the BCCH carrier power reduction operation.
+ * Constrain BS power value by the maximum reduction for this timeslot. */
+ if (ts->trx->bts->c0 == ts->trx)
+ red = OSMO_MIN(red, ts->c0_power_red_db);
+
+ lchan->bs_power_ctrl.max = red;
+ lchan->bs_power_ctrl.current = red;
+
+ LOGPLCHAN(lchan, DRSL, LOGL_DEBUG, "BS Power attenuation %u dB\n",
+ lchan->bs_power_ctrl.current);
+ }
+
/* 9.3.13 MS Power */
if (TLVP_PRES_LEN(&tp, RSL_IE_MS_POWER, 1)) {
- lchan->ms_power = *TLVP_VAL(&tp, RSL_IE_MS_POWER);
- lchan->ms_power_ctrl.current = lchan->ms_power;
- lchan->ms_power_ctrl.fixed = 0;
+ lchan->ms_power_ctrl.max = *TLVP_VAL(&tp, RSL_IE_MS_POWER) & 0x1F;
+ lchan->ms_power_ctrl.current = lchan->ms_power_ctrl.max;
}
/* 9.3.24 Timing Advance */
if (TLVP_PRES_LEN(&tp, RSL_IE_TIMING_ADVANCE, 1))
- lchan->rqd_ta = *TLVP_VAL(&tp, RSL_IE_TIMING_ADVANCE);
+ lchan->ta_ctrl.current = *TLVP_VAL(&tp, RSL_IE_TIMING_ADVANCE);
+
+ /* 9.3.31 (TLV) MS Power Parameters IE (vendor specific) */
+ if ((ie = TLVP_GET(&tp, RSL_IE_MS_POWER_PARAM)) != NULL) {
+ struct gsm_power_ctrl_params *params = &lchan->ms_dpc_params;
+
+ /* Parsed parameters will override per-TRX defaults */
+ memcpy(params, ts->trx->ms_dpc_params, sizeof(*params));
+
+ if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) != 0) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Failed to parse MS Power Parameters IE\n");
+ return rsl_tx_chan_act_nack(lchan, RSL_ERR_IE_CONTENT);
+ }
- /* 9.3.32 BS Power Parameters */
- /* 9.3.31 MS Power Parameters */
- if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER_PARAM))
- lchan->ms_power_ctrl.fixed = 0;
- else {
/* Spec explicitly states BTS should only perform
* autonomous MS power control loop in BTS if 'MS Power
* Parameters' IE is present! */
- lchan->ms_power_ctrl.fixed = 1;
+ lchan->ms_power_ctrl.dpc_params = params;
+ }
+
+ /* 9.3.32 (TLV) BS Power Parameters IE (vendor specific) */
+ if ((ie = TLVP_GET(&tp, RSL_IE_BS_POWER_PARAM)) != NULL) {
+ struct gsm_power_ctrl_params *params = &lchan->bs_dpc_params;
+
+ /* Parsed parameters will override per-TRX defaults */
+ memcpy(params, ts->trx->bs_dpc_params, sizeof(*params));
+
+ /* Parsed parameters will override per-TRX defaults */
+ if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) != 0) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Failed to parse BS Power Parameters IE\n");
+ return rsl_tx_chan_act_nack(lchan, RSL_ERR_IE_CONTENT);
+ }
+
+ /* NOTE: it's safer to start from 0 */
+ lchan->bs_power_ctrl.current = 0;
+ lchan->bs_power_ctrl.dpc_params = params;
}
+
/* 9.3.16 Physical Context */
/* 9.3.29 SACCH Information */
@@ -1213,29 +2132,22 @@ static int rsl_rx_chan_activ(struct msgb *msg)
/* use standard SACCH filling of the BTS */
copy_sacch_si_to_lchan(lchan);
}
+
/* 9.3.52 MultiRate Configuration */
- if (TLVP_PRESENT(&tp, RSL_IE_MR_CONFIG)) {
- if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_bts_lv) - 1) {
- LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Error parsing MultiRate conf IE\n");
- rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT, &dch->chan_nr, NULL, msg);
- return rsl_tx_chan_act_acknack(lchan, RSL_ERR_IE_CONTENT);
- }
- memcpy(lchan->mr_bts_lv, TLVP_VAL(&tp, RSL_IE_MR_CONFIG) - 1,
- TLVP_LEN(&tp, RSL_IE_MR_CONFIG) + 1);
- amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
- TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
- amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
- &lchan->tch.amr_mr);
- lchan->tch.last_cmr = AMR_CMR_NONE;
- }
+ rc = parse_multirate_config(lchan, &tp);
+ if (rc < 0)
+ return rsl_tx_chan_act_acknack(lchan, -rc);
+
/* 9.3.53 MultiRate Control */
/* 9.3.54 Supported Codec Types */
- LOGPLCHAN(lchan, DRSL, LOGL_INFO, "chan_nr=%s type=0x%02x mode=%s\n",
- rsl_chan_nr_str(dch->chan_nr), type, gsm48_chan_mode_name(lchan->tch_mode));
+ LOGPLCHAN(lchan, DRSL, LOGL_INFO, "chan_nr=%s type=0x%02x=%s mode=%s\n",
+ rsl_chan_nr_str(dch->chan_nr),
+ type, get_value_string(rsl_act_type_names, type),
+ gsm48_chan_mode_name(lchan->tch_mode));
/* Connecting PDCH on dyn TS goes via PCU instead. */
- if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
+ if (ts->pchan == GSM_PCHAN_OSMO_DYN
&& ts->dyn.pchan_want == GSM_PCHAN_PDCH) {
/*
* We ack the activation to the BSC right away, regardless of
@@ -1281,48 +2193,75 @@ static int rsl_rx_chan_activ(struct msgb *msg)
return 0;
}
+ /* Indicate which SAPIs should be enabled before the first RACH is received, for handover. See 3GPP TS 48.058
+ * 4.1.3 and 4.1.4.
+ *
+ * | | Timing || transmit | activate | This implementation
+ * | MS Power | Advance || on main channel | dl SACCH | activates DL SACCH
+ * -----------------------------------------------------------------------------------------
+ * async ho no * --> yes no no
+ * async ho yes * --> yes may be started no
+ * async ho yes yes --> yes may be started yes
+ * sync ho no no --> yes no no
+ * sync ho yes no --> yes may be started no
+ * sync ho yes yes --> yes shall be started yes
+ */
+ switch (type) {
+ case RSL_ACT_INTER_ASYNC:
+ case RSL_ACT_INTER_SYNC:
+ lchan->want_dl_sacch_active = (TLVP_PRES_LEN(&tp, RSL_IE_MS_POWER, 1)
+ && TLVP_PRES_LEN(&tp, RSL_IE_TIMING_ADVANCE, 1));
+ break;
+ default:
+ lchan->want_dl_sacch_active = true;
+ break;
+ }
+
/* Remember to send an RSL ACK once the lchan is active */
lchan->rel_act_kind = LCHAN_REL_ACT_RSL;
- /* actually activate the channel in the BTS */
- rc = l1sap_chan_act(lchan->ts->trx, dch->chan_nr, &tp);
+ rc = parse_repeated_acch_capability(lchan, &tp);
+ if (rc < 0)
+ return rsl_tx_chan_act_acknack(lchan, -rc);
+ rc = parse_temporary_overpower_acch_capability(lchan, &tp);
if (rc < 0)
return rsl_tx_chan_act_acknack(lchan, -rc);
- return 0;
-}
-
-static int dyn_ts_pdch_release(struct gsm_lchan *lchan)
-{
- struct gsm_bts_trx_ts *ts = lchan->ts;
+ /* Take the first ACCH overpower decision (if allowed): it can be
+ * enabled immediately if the RxQual threshold is disabled (0). */
+ if (lchan->top_acch_cap.overpower_db > 0)
+ lchan->top_acch_active = !lchan->top_acch_cap.rxqual;
+ else
+ lchan->top_acch_active = false;
- if (ts->dyn.pchan_is != ts->dyn.pchan_want) {
- LOGP(DRSL, LOGL_ERROR, "%s: PDCH release requested but already"
- " in switchover\n", gsm_ts_and_pchan_name(ts));
- return -EINVAL;
+ /* set ASCI channel into right state */
+ if (rsl_chan_rt_is_asci(lchan->rsl_chan_rt)) {
+ if (reactivation)
+ vgcs_lchan_react(lchan);
+ else
+ vgcs_lchan_activate(lchan);
}
- /*
- * Indicate PDCH Disconnect in dyn_pdch.want, let pcu_tx_info_ind()
- * pick it up and wait for PCU to disable the channel.
- */
- ts->dyn.pchan_want = GSM_PCHAN_NONE;
-
- if (!pcu_connected()) {
- /* PCU not connected yet. Just record the new type and done,
- * the PCU will pick it up once connected. */
- ts->dyn.pchan_is = GSM_PCHAN_NONE;
- return 1;
+ /* on reactivation, the channel is already activated */
+ if (reactivation) {
+ rc = rsl_tx_chan_act_ack(lchan);
+ if (rc < 0)
+ LOGP(DRSL, LOGL_ERROR, "%s Cannot send act ack: %d\n",
+ gsm_ts_and_pchan_name(ts), rc);
+ return 0;
}
- return pcu_tx_info_ind();
+ /* actually activate the channel in the BTS */
+ rc = l1sap_chan_act(lchan->ts->trx, dch->chan_nr);
+ if (rc < 0)
+ return rsl_tx_chan_act_acknack(lchan, -rc);
+
+ return 0;
}
/* 8.4.14 RF CHANnel RELease is received */
static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan, uint8_t chan_nr)
{
- int rc;
-
if (lchan->state == LCHAN_S_NONE) {
LOGP(DRSL, LOGL_ERROR,
"%s ss=%d state=%s Rx RSL RF Channel Release, but is already inactive;"
@@ -1333,38 +2272,7 @@ static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan, uint8_t chan_nr)
* not necessarily reflecting the current lchan state. */
return tx_rf_rel_ack(lchan, chan_nr);
}
-
- if (lchan->abis_ip.rtp_socket) {
- rsl_tx_ipac_dlcx_ind(lchan, RSL_ERR_NORMAL_UNSPEC);
- osmo_rtp_socket_log_stats(lchan->abis_ip.rtp_socket, DRTP, LOGL_INFO,
- "Closing RTP socket on Channel Release ");
- osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
- lchan->abis_ip.rtp_socket = NULL;
- msgb_queue_flush(&lchan->dl_tch_queue);
- }
-
- /* release handover state */
- handover_reset(lchan);
-
- lchan->rel_act_kind = LCHAN_REL_ACT_RSL;
-
- /* Dynamic channel in PDCH mode is released via PCU */
- if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
- && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH) {
- rc = dyn_ts_pdch_release(lchan);
- if (rc == 1) {
- /* If the PCU is not connected, continue to rel ack right away. */
- lchan->rel_act_kind = LCHAN_REL_ACT_PCU;
- return rsl_tx_rf_rel_ack(lchan);
- }
- /* Waiting for PDCH release */
- return rc;
- }
-
- l1sap_chan_rel(lchan->ts->trx, chan_nr);
-
- lapdm_channel_exit(&lchan->lapdm_ch);
-
+ gsm_lchan_release(lchan, LCHAN_REL_ACT_RSL);
return 0;
}
@@ -1398,7 +2306,7 @@ static int tx_ciph_mod_compl_hack(struct gsm_lchan *lchan, uint8_t link_id,
}
}
- rsl_rll_push_l3(fake_msg, RSL_MT_DATA_IND, gsm_lchan2chan_nr(lchan),
+ rsl_rll_push_l3(fake_msg, RSL_MT_DATA_IND, gsm_lchan2chan_nr_rsl(lchan),
link_id, 1);
fake_msg->lchan = lchan;
@@ -1443,7 +2351,8 @@ static int rsl_rx_encr_cmd(struct msgb *msg)
uint8_t link_id;
if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
- return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT, &dch->chan_nr, NULL, msg);
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_error_report(msg->trx, RSL_ERR_PROTO, &dch->chan_nr, NULL, msg);
}
if (!TLVP_PRESENT(&tp, RSL_IE_ENCR_INFO) ||
@@ -1512,7 +2421,7 @@ static int _rsl_tx_mode_modif_nack(struct gsm_bts_trx *trx, uint8_t chan_nr, uin
struct msgb *msg;
if (lchan)
- LOGP(DRSL, LOGL_NOTICE, "%s: ", gsm_lchan_name(lchan));
+ LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "");
else
LOGP(DRSL, LOGL_NOTICE, "0x%02x: ", chan_nr);
LOGPC(DRSL, LOGL_NOTICE, "Tx MODE MODIFY NACK (cause = 0x%02x)\n", cause);
@@ -1533,7 +2442,7 @@ static int _rsl_tx_mode_modif_nack(struct gsm_bts_trx *trx, uint8_t chan_nr, uin
}
static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause)
{
- return _rsl_tx_mode_modif_nack(lchan->ts->trx, gsm_lchan2chan_nr(lchan), cause, lchan);
+ return _rsl_tx_mode_modif_nack(lchan->ts->trx, gsm_lchan2chan_nr_rsl(lchan), cause, lchan);
}
@@ -1541,7 +2450,7 @@ static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause)
static int rsl_tx_mode_modif_ack(struct gsm_lchan *lchan)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Tx MODE MODIF ACK\n");
@@ -1560,24 +2469,24 @@ 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;
+ uint8_t cause;
+ int rc;
- rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
-
- /* 9.3.6 Channel Mode */
- if (!TLVP_PRESENT(&tp, RSL_IE_CHAN_MODE)) {
- LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "missing Channel Mode\n");
- return rsl_tx_mode_modif_nack(lchan, RSL_ERR_MAND_IE_ERROR);
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_mode_modif_nack(lchan, RSL_ERR_PROTO);
}
- cm = (struct rsl_ie_chan_mode *) TLVP_VAL(&tp, RSL_IE_CHAN_MODE);
- lchan_tchmode_from_cmode(lchan, cm);
- if (bts_supports_cm(lchan->ts->trx->bts, ts_pchan(lchan->ts), lchan->tch_mode) != 1) {
- LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s: invalid mode: %s (wrong BSC configuration?)\n",
- gsm_ts_and_pchan_name(lchan->ts), gsm48_chan_mode_name(lchan->tch_mode));
- return rsl_tx_mode_modif_nack(lchan, RSL_ERR_SERV_OPT_UNAVAIL);
- }
+ /* 9.3.6 Channel Mode */
+ if (rsl_handle_chan_mod_ie(lchan, &tp, &cause) != 0)
+ return rsl_tx_mode_modif_nack(lchan, cause);
+ /* 9.3.5 Channel Identification */
+ if (rsl_handle_chan_ident_ie(lchan, &tp, &cause) != 0)
+ return rsl_tx_mode_modif_nack(lchan, cause);
+ /* Osmocom specific TSC IE for VAMOS */
+ if (rsl_handle_osmo_tsc_ie(lchan, &tp, &cause) != 0)
+ return rsl_tx_mode_modif_nack(lchan, cause);
/* 9.3.7 Encryption Information */
if (TLVP_PRESENT(&tp, RSL_IE_ENCR_INFO)) {
@@ -1593,23 +2502,27 @@ static int rsl_rx_mode_modif(struct msgb *msg)
/* 9.3.45 Main channel reference */
/* 9.3.52 MultiRate Configuration */
- if (TLVP_PRESENT(&tp, RSL_IE_MR_CONFIG)) {
- if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_bts_lv) - 1) {
- LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Error parsing MultiRate conf IE\n");
- rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT, &dch->chan_nr, NULL, msg);
- return rsl_tx_mode_modif_nack(lchan, RSL_ERR_IE_CONTENT);;
- }
- memcpy(lchan->mr_bts_lv, TLVP_VAL(&tp, RSL_IE_MR_CONFIG) - 1,
- TLVP_LEN(&tp, RSL_IE_MR_CONFIG) + 1);
- amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
- TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
- amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
- &lchan->tch.amr_mr);
- lchan->tch.last_cmr = AMR_CMR_NONE;
- }
+ rc = parse_multirate_config(lchan, &tp);
+ if (rc < 0)
+ return rsl_tx_mode_modif_nack(lchan, -rc);
+
/* 9.3.53 MultiRate Control */
/* 9.3.54 Supported Codec Types */
+ rc = parse_repeated_acch_capability(lchan, &tp);
+ if (rc < 0)
+ return rsl_tx_mode_modif_nack(lchan, -rc);
+ rc = parse_temporary_overpower_acch_capability(lchan, &tp);
+ if (rc < 0)
+ return rsl_tx_mode_modif_nack(lchan, -rc);
+
+ /* Immediately disable ACCH overpower if the value is 0 dB,
+ * or enable if the RxQual threshold becomes disabled (0). */
+ if (lchan->top_acch_cap.overpower_db == 0)
+ lchan->top_acch_active = false;
+ else if (lchan->top_acch_cap.rxqual == 0)
+ lchan->top_acch_active = true;
+
l1sap_chan_modify(lchan->ts->trx, dch->chan_nr);
/* FIXME: delay this until L1 says OK? */
@@ -1623,28 +2536,61 @@ static int rsl_rx_ms_pwr_ctrl(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
struct gsm_lchan *lchan = msg->lchan;
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+ const struct tlv_p_entry *ie;
struct tlv_parsed tp;
uint8_t pwr;
+ int max_pwr, curr_pwr;
- rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_error_report(msg->trx, RSL_ERR_PROTO, &dch->chan_nr, NULL, msg);
+ }
/* 9.3.13 MS Power (M) */
if (!TLVP_PRES_LEN(&tp, RSL_IE_MS_POWER, 1))
return rsl_tx_error_report(msg->trx, RSL_ERR_MAND_IE_ERROR, &dch->chan_nr, NULL, msg);
pwr = *TLVP_VAL(&tp, RSL_IE_MS_POWER) & 0x1F;
- lchan->ms_power_ctrl.current = pwr;
+ lchan->ms_power_ctrl.max = pwr;
- LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Rx MS POWER CONTROL %d\n", lchan->ms_power_ctrl.current);
+ LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Rx MS POWER CONTROL %" PRIu8 "\n", pwr);
- /* 9.3.31 MS Power Parameters (O) */
- if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER_PARAM))
- lchan->ms_power_ctrl.fixed = 0;
- else {
- /* Spec explicitly states BTS should only perform
- * autonomous MS power control loop in BTS if 'MS Power
- * Parameters' IE is present! */
- lchan->ms_power_ctrl.fixed = 1;
+ /* Spec explicitly states BTS should only perform autonomous MS Power
+ * control loop in BTS if 'MS Power Parameters' IE is present! */
+ lchan->ms_power_ctrl.dpc_params = NULL;
+
+ /* 9.3.31 (TLV) MS Power Parameters IE (vendor specific) */
+ if ((ie = TLVP_GET(&tp, RSL_IE_MS_POWER_PARAM)) != NULL) {
+ struct gsm_power_ctrl_params *params = &lchan->ms_dpc_params;
+
+ /* Parsed parameters will override per-TRX defaults */
+ memcpy(params, msg->trx->ms_dpc_params, sizeof(*params));
+
+ /* Parsed parameters will override per-TRX defaults */
+ if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) != 0) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Failed to parse MS Power Parameters IE\n");
+ return rsl_tx_chan_act_nack(lchan, RSL_ERR_IE_CONTENT);
+ }
+
+ lchan->ms_power_ctrl.dpc_params = params;
+ }
+
+ /* Only set current to max if actual value of current
+ in dBm > value in dBm from max, or if fixed. */
+ if (lchan->ms_power_ctrl.dpc_params == NULL) {
+ lchan->ms_power_ctrl.current = lchan->ms_power_ctrl.max;
+ } else {
+ max_pwr = ms_pwr_dbm(bts->band, lchan->ms_power_ctrl.max);
+ curr_pwr = ms_pwr_dbm(bts->band, lchan->ms_power_ctrl.current);
+ if (max_pwr < 0 || curr_pwr < 0) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR,
+ "Unable to calculate power levels to dBm: %" PRIu8 " -> %d, %" PRIu8 " -> %d\n",
+ lchan->ms_power_ctrl.max, max_pwr,
+ lchan->ms_power_ctrl.current, curr_pwr);
+ } else if (curr_pwr > max_pwr) {
+ lchan->ms_power_ctrl.current = lchan->ms_power_ctrl.max;
+ }
}
bts_model_adjst_ms_pwr(lchan);
@@ -1652,41 +2598,77 @@ static int rsl_rx_ms_pwr_ctrl(struct msgb *msg)
return 0;
}
-/* See TS 48.058 Section 9.3.4 */
-static int bs_power_attenuation_dB(uint8_t bs_power)
-{
- /* the lower nibble contains the number of 2dB steps that the BS power is reduced compared
- * to its nominal transmit power */
- return - ((bs_power & 0xF) *2);
-}
-
/* 8.4.16 BS POWER CONTROL */
static int rsl_rx_bs_pwr_ctrl(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
struct gsm_lchan *lchan = msg->lchan;
+ struct gsm_bts_trx *trx = msg->trx;
+ const struct tlv_p_entry *ie;
struct tlv_parsed tp;
- uint8_t new_bs_power;
+ uint8_t old, new;
- rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_error_report(trx, RSL_ERR_PROTO, &dch->chan_nr, NULL, msg);
+ }
/* 9.3.4 BS Power (M) */
if (!TLVP_PRES_LEN(&tp, RSL_IE_BS_POWER, 1))
- return rsl_tx_error_report(msg->trx, RSL_ERR_MAND_IE_ERROR, &dch->chan_nr, NULL, msg);
+ return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR, &dch->chan_nr, NULL, msg);
+
+ if (*TLVP_VAL(&tp, RSL_IE_BS_POWER) & (1 << 4)) {
+ LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Fast Power Control is not supported\n");
+ return rsl_tx_error_report(trx, RSL_ERR_SERV_OPT_UNIMPL, &dch->chan_nr, NULL, msg);
+ }
+
+ new = BS_POWER2DB(*TLVP_VAL(&tp, RSL_IE_BS_POWER));
+ old = lchan->bs_power_ctrl.current;
+
+ /* Osmocom specific extension for BCCH carrier power reduction */
+ if (dch->chan_nr == RSL_CHAN_BCCH) {
+ int rc = bts_set_c0_pwr_red(trx->bts, new);
+ if (rc != 0) {
+ const uint8_t cause = (rc == -ENOTSUP) ?
+ RSL_ERR_SERV_OPT_UNIMPL : RSL_ERR_IE_CONTENT;
+ return rsl_tx_error_report(trx, cause, &dch->chan_nr, NULL, msg);
+ }
- new_bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER);
+ return 0;
+ }
+
+ /* BS power reduction is generally not allowed on BCCH/CCCH carrier.
+ * However, we allow it in the BCCH carrier power reduction operation.
+ * Constrain BS power value by the maximum reduction for this timeslot. */
+ if (trx->bts->c0 == trx)
+ new = OSMO_MIN(new, lchan->ts->c0_power_red_db);
- LOGPLCHAN(lchan, DRSL, LOGL_INFO, "BS POWER CONTROL Attenuation %d -> %d dB\n",
- bs_power_attenuation_dB(lchan->bs_power), bs_power_attenuation_dB(new_bs_power));
+ /* 9.3.32 (TLV) BS Power Parameters IE (vendor specific) */
+ if ((ie = TLVP_GET(&tp, RSL_IE_BS_POWER_PARAM)) != NULL) {
+ struct gsm_power_ctrl_params *params = &lchan->bs_dpc_params;
- lchan->bs_power = new_bs_power;
+ /* Parsed parameters will override per-TRX defaults */
+ memcpy(params, trx->bs_dpc_params, sizeof(*params));
+
+ /* Parsed parameters will override per-TRX defaults */
+ if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) != 0) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Failed to parse BS Power Parameters IE\n");
+ return rsl_tx_chan_act_nack(lchan, RSL_ERR_IE_CONTENT);
+ }
- /* 9.3.31 MS Power Parameters (O) */
- if (TLVP_PRESENT(&tp, RSL_IE_BS_POWER_PARAM)) {
- /* Spec explicitly states BTS should perform autonomous
- * BS power control loop in BTS if 'BS Power Parameters'
- * IE is present! WE don't support that. */
- return rsl_tx_error_report(msg->trx, RSL_ERR_OPT_IE_ERROR, &dch->chan_nr, NULL, msg);
+ /* NOTE: it's safer to start from 0 */
+ lchan->bs_power_ctrl.current = 0;
+ lchan->bs_power_ctrl.max = new;
+ lchan->bs_power_ctrl.dpc_params = params;
+ } else {
+ lchan->bs_power_ctrl.dpc_params = NULL;
+ lchan->bs_power_ctrl.current = new;
+ }
+
+ if (lchan->bs_power_ctrl.current != old) {
+ LOGPLCHAN(lchan, DRSL, LOGL_INFO, "BS POWER CONTROL: "
+ "attenuation change %u -> %u dB\n",
+ old, lchan->bs_power_ctrl.current);
}
return 0;
@@ -1702,7 +2684,10 @@ static int rsl_rx_sacch_inf_mod(struct msgb *msg)
struct tlv_parsed tp;
uint8_t rsl_si, osmo_si;
- rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_error_report(msg->trx, RSL_ERR_PROTO, &dch->chan_nr, NULL, msg);
+ }
if (TLVP_PRESENT(&tp, RSL_IE_STARTNG_TIME)) {
LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Starting time not supported\n");
@@ -1758,7 +2743,7 @@ int rsl_tx_cbch_load_indication(struct gsm_bts *bts, bool ext_cbch, bool overflo
return -ENOMEM;
/* 9.3.1 Channel Number */
- rsl_cch_push_hdr(msg, RSL_MT_CBCH_LOAD_IND, gsm_lchan2chan_nr(lchan));
+ rsl_cch_push_hdr(msg, RSL_MT_CBCH_LOAD_IND, gsm_lchan2chan_nr_rsl(lchan));
/* 9.3.43 CBCH Load Information */
load_info = ((overflow & 1) << 7) | (amount & 0x0F);
@@ -1810,7 +2795,9 @@ int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause)
{
struct msgb *nmsg;
- LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Sending RTP delete indication: cause = %s\n",
+ LOGPLCHAN(lchan, DRSL,
+ (cause == RSL_ERR_NORMAL_UNSPEC) ? LOGL_INFO : LOGL_NOTICE,
+ "Sending RTP delete indication: cause = %s\n",
rsl_err_name(cause));
nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
@@ -1820,7 +2807,7 @@ int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause)
msgb_tv16_put(nmsg, RSL_IE_IPAC_CONN_ID, htons(lchan->abis_ip.conn_id));
rsl_add_rtp_stats(lchan, nmsg);
msgb_tlv_put(nmsg, RSL_IE_CAUSE, 1, &cause);
- rsl_ipa_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND, gsm_lchan2chan_nr(lchan));
+ rsl_ipa_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND, gsm_lchan2chan_nr_rsl(lchan));
nmsg->trx = lchan->ts->trx;
@@ -1832,7 +2819,7 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
uint8_t orig_msgt)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
const char *name;
struct in_addr ia;
@@ -1870,6 +2857,11 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
lchan->abis_ip.rtp_payload2);
}
+ /* Osmocom Extension: Osmux CID */
+ if (lchan->abis_ip.osmux.use)
+ msgb_tlv_put(msg, RSL_IE_OSMO_OSMUX_CID, 1,
+ &lchan->abis_ip.osmux.local_cid);
+
/* push the header in front */
rsl_ipa_push_hdr(msg, orig_msgt + 1, chan_nr);
msg->trx = lchan->ts->trx;
@@ -1880,7 +2872,7 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
static int rsl_tx_ipac_dlcx_ack(struct gsm_lchan *lchan, int inc_conn_id)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_INFO, "RSL Tx IPAC_DLCX_ACK\n");
@@ -1903,7 +2895,7 @@ static int rsl_tx_ipac_dlcx_nack(struct gsm_lchan *lchan, int inc_conn_id,
uint8_t cause)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_INFO, "RSL Tx IPAC_DLCX_NACK\n");
@@ -1924,15 +2916,15 @@ static int rsl_tx_ipac_dlcx_nack(struct gsm_lchan *lchan, int inc_conn_id,
}
-/* transmit an CRCX NACK for the lchan */
+/* Send an xxCX NACK for the given xxCX message type and lchan */
static int tx_ipac_XXcx_nack(struct gsm_lchan *lchan, uint8_t cause,
int inc_ipport, uint8_t orig_msgtype)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
+ uint8_t msg_type = orig_msgtype + 2;
- /* FIXME: allocate new msgb and copy old over */
- LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "RSL Tx IPAC_BIND_NACK\n");
+ LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "RSL Tx %s\n", rsl_ipac_msg_name(msg_type));
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
@@ -1952,7 +2944,7 @@ static int tx_ipac_XXcx_nack(struct gsm_lchan *lchan, uint8_t cause,
msgb_tlv_put(msg, RSL_IE_CAUSE, 1, &cause);
/* push the header in front */
- rsl_ipa_push_hdr(msg, orig_msgtype + 2, chan_nr);
+ rsl_ipa_push_hdr(msg, msg_type, chan_nr);
msg->trx = lchan->ts->trx;
return abis_bts_rsl_sendmsg(msg);
@@ -1960,7 +2952,7 @@ static int tx_ipac_XXcx_nack(struct gsm_lchan *lchan, uint8_t cause,
static char *get_rsl_local_ip(struct gsm_bts_trx *trx)
{
- struct e1inp_ts *ts = trx->rsl_link->ts;
+ struct e1inp_ts *ts = trx->bb_transc.rsl.link->ts;
struct sockaddr_storage ss;
socklen_t sa_len = sizeof(ss);
static char hostbuf[256];
@@ -1980,42 +2972,21 @@ static char *get_rsl_local_ip(struct gsm_bts_trx *trx)
return hostbuf;
}
-static int bind_rtp(struct gsm_bts *bts, struct osmo_rtp_socket *rs, const char *ip)
-{
- int rc;
- unsigned int i;
- unsigned int tries;
-
- tries = (bts->rtp_port_range_end - bts->rtp_port_range_start) / 2;
- for (i = 0; i < tries; i++) {
-
- if (bts->rtp_port_range_next >= bts->rtp_port_range_end)
- bts->rtp_port_range_next = bts->rtp_port_range_start;
-
- rc = osmo_rtp_socket_bind(rs, ip, bts->rtp_port_range_next);
-
- bts->rtp_port_range_next += 2;
-
- if (rc == 0)
- return 0;
- }
-
- return -1;
-}
-
static int rsl_rx_ipac_XXcx(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
struct tlv_parsed tp;
struct gsm_lchan *lchan = msg->lchan;
struct gsm_bts *bts = lchan->ts->trx->bts;
- const uint8_t *payload_type, *speech_mode, *payload_type2;
+ const uint8_t *payload_type, *speech_mode, *payload_type2, *csd_fmt;
+ const uint8_t *osmux_cid = NULL;
uint32_t connect_ip = 0;
uint16_t connect_port = 0;
- int rc, inc_ip_port = 0, port;
+ int rc, inc_ip_port = 0;
char *name;
struct in_addr ia;
- struct in_addr addr;
+ enum rsl_ipac_rtp_csd_format_d csd_fmt_d;
+ enum rsl_ipac_rtp_csd_format_ir csd_fmt_ir;
if (dch->c.msg_type == RSL_MT_IPAC_CRCX)
name = "CRCX";
@@ -2027,22 +2998,22 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
return tx_ipac_XXcx_nack(lchan, 0x52,
0, dch->c.msg_type);
- rc = rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
- if (rc < 0)
- return tx_ipac_XXcx_nack(lchan, RSL_ERR_MAND_IE_ERROR,
- 0, dch->c.msg_type);
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return tx_ipac_XXcx_nack(lchan, RSL_ERR_PROTO, 0, dch->c.msg_type);
+ }
LOGPLCHAN(lchan, DRSL, LOGL_DEBUG, "IPAC_%s: ", name);
if (TLVP_PRES_LEN(&tp, RSL_IE_IPAC_REMOTE_IP, 4)) {
+ struct in_addr addr;
connect_ip = tlvp_val32_unal(&tp, RSL_IE_IPAC_REMOTE_IP);
addr.s_addr = connect_ip;
LOGPC(DRSL, LOGL_DEBUG, "connect_ip=%s ", inet_ntoa(addr));
}
if (TLVP_PRES_LEN(&tp, RSL_IE_IPAC_REMOTE_PORT, 2)) {
- connect_port = tlvp_val16_unal(&tp, RSL_IE_IPAC_REMOTE_PORT);
- LOGPC(DRSL, LOGL_DEBUG, "connect_port=%u ",
- ntohs(connect_port));
+ connect_port = tlvp_val16be(&tp, RSL_IE_IPAC_REMOTE_PORT);
+ LOGPC(DRSL, LOGL_DEBUG, "connect_port=%u ", connect_port);
}
speech_mode = TLVP_VAL(&tp, RSL_IE_IPAC_SPEECH_MODE);
@@ -2056,6 +3027,14 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
LOGPC(DRSL, LOGL_DEBUG, "\n");
payload_type2 = TLVP_VAL(&tp, RSL_IE_IPAC_RTP_PAYLOAD2);
+ if (payload_type2)
+ LOGPC(DRSL, LOGL_DEBUG, "payload_type2=%u ", *payload_type2);
+
+ /* this IE has TLV format when TV would have been good enough */
+ if (TLVP_PRES_LEN(&tp, RSL_IE_OSMO_OSMUX_CID, 1))
+ osmux_cid = TLVP_VAL(&tp, RSL_IE_OSMO_OSMUX_CID);
+ if (osmux_cid)
+ LOGPC(DRSL, LOGL_DEBUG, "osmux_cid=%u ", *osmux_cid);
if (dch->c.msg_type == RSL_MT_IPAC_CRCX && connect_ip && connect_port)
inc_ip_port = 1;
@@ -2067,116 +3046,107 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
inc_ip_port, dch->c.msg_type);
}
- if (dch->c.msg_type == RSL_MT_IPAC_CRCX) {
- char cname[32];
- char *ipstr = NULL;
- if (lchan->abis_ip.rtp_socket) {
- LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Rx RSL IPAC CRCX, "
- "but we already have socket!\n");
- return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
- inc_ip_port, dch->c.msg_type);
+ if ((csd_fmt = TLVP_VAL(&tp, RSL_IE_IPAC_RTP_CSD_FMT))) {
+ csd_fmt_d = *csd_fmt & 0xf;
+ csd_fmt_ir = *csd_fmt >> 4;
+ LOGPC(DRSL, LOGL_DEBUG, "csd_fmt_d=%d csd_fmt_ir=%d ", csd_fmt_d, csd_fmt_ir);
+ if (csd_fmt_d != RSL_IPAC_RTP_CSD_TRAU_BTS) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Rx RSL IPAC %s, csd_fmt_d=%d is not supported\n",
+ name, csd_fmt_d);
+ return tx_ipac_XXcx_nack(lchan, RSL_ERR_SERV_OPT_UNIMPL, inc_ip_port, dch->c.msg_type);
}
- /* FIXME: select default value depending on speech_mode */
- //if (!payload_type)
- lchan->tch.last_fn = LCHAN_FN_DUMMY;
- lchan->abis_ip.rtp_socket = osmo_rtp_socket_create(lchan->ts->trx,
- OSMO_RTP_F_POLL);
- if (!lchan->abis_ip.rtp_socket) {
- LOGPLCHAN(lchan, DRTP, LOGL_ERROR, "IPAC Failed to create RTP/RTCP sockets\n");
- oml_tx_failure_event_rep(&lchan->ts->trx->mo,
- NM_SEVER_MINOR, OSMO_EVT_CRIT_RTP_TOUT,
- "%s IPAC Failed to create RTP/RTCP sockets",
- gsm_lchan_name(lchan));
+ }
+
+ if (!osmux_cid) { /* Regular RTP */
+ if (bts->osmux.use == OSMUX_USAGE_ONLY) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Rx RSL IPAC XXcx without Osmux CID"
+ "goes against configured Osmux policy 'only'\n");
return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
inc_ip_port, dch->c.msg_type);
}
- rc = osmo_rtp_socket_set_param(lchan->abis_ip.rtp_socket,
- bts->rtp_jitter_adaptive ?
- OSMO_RTP_P_JIT_ADAP :
- OSMO_RTP_P_JITBUF,
- bts->rtp_jitter_buf_ms);
- if (rc < 0)
- LOGPLCHAN(lchan, DRTP, LOGL_ERROR,
- "IPAC Failed to set RTP socket parameters: %s\n", strerror(-rc));
- else
- LOGPLCHAN(lchan, DRTP, LOGL_INFO, "IPAC set RTP socket parameters: %d\n", rc);
- lchan->abis_ip.rtp_socket->priv = lchan;
- 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()
- * here to 0.0.0.0 and wait for the connect()
- * below, after which the kernel will have
- * selected the local IP address. */
- ipstr = "0.0.0.0";
- } else {
- /* if CRCX does not specify a remote IP, we will
- * not do any connect() below, and thus the
- * local socket will remain bound to 0.0.0.0 -
- * which however we cannot legitimately report
- * back to the BSC in the CRCX_ACK */
- ipstr = get_rsl_local_ip(lchan->ts->trx);
+
+ if (dch->c.msg_type == RSL_MT_IPAC_CRCX) { /* CRCX */
+ char *ipstr = NULL;
+ if (connect_ip && connect_port) {
+ /* if CRCX specifies a remote IP, we can bind()
+ * here to 0.0.0.0 and wait for the connect()
+ * below, after which the kernel will have
+ * selected the local IP address. */
+ ipstr = "0.0.0.0";
+ } else {
+ /* if CRCX does not specify a remote IP, we will
+ * not do any connect() below, and thus the
+ * local socket will remain bound to 0.0.0.0 -
+ * which however we cannot legitimately report
+ * back to the BSC in the CRCX_ACK */
+ ipstr = get_rsl_local_ip(lchan->ts->trx);
+ }
+ rc = lchan_rtp_socket_create(lchan, ipstr);
+ if (rc < 0)
+ return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
+ inc_ip_port, dch->c.msg_type);
+ } else { /* MDCX */
+ if (!lchan->abis_ip.rtp_socket) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Rx RSL IPAC MDCX, "
+ "but we have no RTP socket!\n");
+ return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
+ inc_ip_port, dch->c.msg_type);
+ }
}
- rc = bind_rtp(bts, lchan->abis_ip.rtp_socket, ipstr);
+
+ /* Special rule: If connect_ip == 0.0.0.0, use RSL IP
+ * address */
+ if (connect_ip == 0) {
+ struct e1inp_sign_link *sign_link =
+ lchan->ts->trx->bb_transc.rsl.link;
+
+ ia.s_addr = htonl(get_signlink_remote_ip(sign_link));
+ } else
+ ia.s_addr = connect_ip;
+ rc = lchan_rtp_socket_connect(lchan, &ia, connect_port);
if (rc < 0) {
- LOGPLCHAN(lchan, DRTP, LOGL_ERROR, "IPAC Failed to bind RTP/RTCP sockets\n");
- oml_tx_failure_event_rep(&lchan->ts->trx->mo,
- NM_SEVER_MINOR, OSMO_EVT_CRIT_RTP_TOUT,
- "%s IPAC Failed to bind RTP/RTCP sockets",
- gsm_lchan_name(lchan));
- osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
- lchan->abis_ip.rtp_socket = NULL;
- msgb_queue_flush(&lchan->dl_tch_queue);
+ lchan_rtp_socket_free(lchan);
return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
inc_ip_port, dch->c.msg_type);
}
- /* Ensure RTCP SDES contains some useful information */
- snprintf(cname, sizeof(cname), "bts@%s", ipstr);
- osmo_rtp_set_source_desc(lchan->abis_ip.rtp_socket, cname,
- gsm_lchan_name(lchan), NULL, NULL,
- gsm_trx_unit_id(lchan->ts->trx),
- "OsmoBTS-" PACKAGE_VERSION, NULL);
- /* FIXME: multiplex connection, BSC proxy */
- } else {
- /* MDCX */
- if (!lchan->abis_ip.rtp_socket) {
- LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Rx RSL IPAC MDCX, "
- "but we have no RTP socket!\n");
+
+ } else { /* Osmux */
+ if (bts->osmux.use == OSMUX_USAGE_OFF) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Rx RSL IPAC XXcx with Osmux CID"
+ "goes against configured Osmux policy 'off'\n");
return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
inc_ip_port, dch->c.msg_type);
}
- }
-
- /* Special rule: If connect_ip == 0.0.0.0, use RSL IP
- * address */
- if (connect_ip == 0) {
- struct e1inp_sign_link *sign_link =
- lchan->ts->trx->rsl_link;
+ if (dch->c.msg_type == RSL_MT_IPAC_CRCX) { /* CRCX */
+ rc = lchan_osmux_init(lchan, payload_type ? *payload_type : 0);
+ if (rc < 0)
+ return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
+ inc_ip_port, dch->c.msg_type);
+ } else { /* MDCX */
+ if (!lchan->abis_ip.osmux.use) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Rx RSL IPAC MDCX with Osmux CID, "
+ "CRCX was configured as RTP!\n");
+ return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
+ inc_ip_port, dch->c.msg_type);
+ }
+ }
- ia.s_addr = htonl(get_signlink_remote_ip(sign_link));
- } else
- ia.s_addr = connect_ip;
- rc = osmo_rtp_socket_connect(lchan->abis_ip.rtp_socket,
- inet_ntoa(ia), ntohs(connect_port));
- if (rc < 0) {
- LOGPLCHAN(lchan, DRTP, LOGL_ERROR, "Failed to connect RTP/RTCP sockets\n");
- osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
- lchan->abis_ip.rtp_socket = NULL;
- msgb_queue_flush(&lchan->dl_tch_queue);
- return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
- inc_ip_port, dch->c.msg_type);
+ if (connect_ip != 0)
+ lchan->abis_ip.connect_ip = connect_ip;
+ if (connect_port != 0)
+ lchan->abis_ip.connect_port = connect_port;
+ lchan->abis_ip.osmux.remote_cid = *osmux_cid;
+ if (lchan->abis_ip.connect_ip && lchan->abis_ip.connect_port &&
+ !lchan_osmux_connected(lchan)) {
+ rc = lchan_osmux_connect(lchan);
+ if (rc < 0) {
+ lchan_osmux_release(lchan);
+ return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
+ inc_ip_port, dch->c.msg_type);
+ }
+ }
}
- /* save IP address and port number */
- lchan->abis_ip.connect_ip = ntohl(ia.s_addr);
- lchan->abis_ip.connect_port = ntohs(connect_port);
-
- rc = osmo_rtp_get_bound_ip_port(lchan->abis_ip.rtp_socket,
- &lchan->abis_ip.bound_ip,
- &port);
- if (rc < 0)
- LOGPLCHAN(lchan, DRTP, LOGL_ERROR, "IPAC cannot obtain locally bound IP/port: %d\n", rc);
- lchan->abis_ip.bound_port = port;
/* Everything has succeeded, we can store new values in lchan */
if (payload_type) {
@@ -2206,9 +3176,10 @@ static int rsl_rx_ipac_dlcx(struct msgb *msg)
struct gsm_lchan *lchan = msg->lchan;
int rc, inc_conn_id = 0;
- rc = rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
- if (rc < 0)
- return rsl_tx_ipac_dlcx_nack(lchan, 0, RSL_ERR_MAND_IE_ERROR);
+ if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return rsl_tx_ipac_dlcx_nack(lchan, 0, RSL_ERR_PROTO);
+ }
if (TLVP_PRESENT(&tp, RSL_IE_IPAC_CONN_ID))
inc_conn_id = 1;
@@ -2217,9 +3188,7 @@ static int rsl_rx_ipac_dlcx(struct msgb *msg)
if (lchan->abis_ip.rtp_socket) {
osmo_rtp_socket_log_stats(lchan->abis_ip.rtp_socket, DRTP, LOGL_INFO,
"Closing RTP socket on DLCX ");
- osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
- lchan->abis_ip.rtp_socket = NULL;
- msgb_queue_flush(&lchan->dl_tch_queue);
+ lchan_rtp_socket_free(lchan);
}
return rc;
}
@@ -2233,7 +3202,7 @@ static int rsl_rx_ipac_dlcx(struct msgb *msg)
static int rsl_tx_dyn_pdch_ack(struct gsm_lchan *lchan, bool pdch_act)
{
struct gsm_time *gtime = get_time(lchan->ts->trx->bts);
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
struct msgb *msg;
uint8_t ie[2];
@@ -2262,7 +3231,7 @@ static int rsl_tx_dyn_pdch_nack(struct gsm_lchan *lchan, bool pdch_act,
uint8_t cause)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Tx PDCH %s NACK (cause = 0x%02x)\n",
pdch_act ? "ACT" : "DEACT", cause);
@@ -2371,7 +3340,6 @@ static void rsl_rx_dyn_pdch(struct msgb *msg, bool pdch_act)
if (pdch_act) {
/* Clear TCH state. Only first lchan matters for PDCH */
clear_lchan_for_pdch_activ(ts->lchan);
-
/* First, disconnect the TCH channel, to connect PDTCH later */
rc = bts_model_ts_disconnect(ts);
} else {
@@ -2400,14 +3368,12 @@ static void ipacc_dyn_pdch_ts_disconnected(struct gsm_bts_trx_ts *ts)
enum gsm_phys_chan_config as_pchan;
if (ts->flags & TS_F_PDCH_DEACT_PENDING) {
- LOGP(DRSL, LOGL_DEBUG,
- "%s PDCH DEACT operation: channel disconnected, will reconnect as TCH\n",
- gsm_lchan_name(ts->lchan));
+ LOGPLCHAN(ts->lchan, DRSL, LOGL_DEBUG,
+ "PDCH DEACT operation: channel disconnected, will reconnect as TCH\n");
as_pchan = GSM_PCHAN_TCH_F;
} else if (ts->flags & TS_F_PDCH_ACT_PENDING) {
- LOGP(DRSL, LOGL_DEBUG,
- "%s PDCH ACT operation: channel disconnected, will reconnect as PDTCH\n",
- gsm_lchan_name(ts->lchan));
+ LOGPLCHAN(ts->lchan, DRSL, LOGL_DEBUG,
+ "PDCH ACT operation: channel disconnected, will reconnect as PDTCH\n");
as_pchan = GSM_PCHAN_PDCH;
} else
/* No reconnect pending. */
@@ -2434,6 +3400,7 @@ static void osmo_dyn_ts_disconnected(struct gsm_bts_trx_ts *ts)
switch (ts->dyn.pchan_want) {
case GSM_PCHAN_TCH_F:
case GSM_PCHAN_TCH_H:
+ case GSM_PCHAN_SDCCH8_SACCH8C:
case GSM_PCHAN_PDCH:
break;
default:
@@ -2457,7 +3424,7 @@ void cb_ts_disconnected(struct gsm_bts_trx_ts *ts)
switch (ts->pchan) {
case GSM_PCHAN_TCH_F_PDCH:
return ipacc_dyn_pdch_ts_disconnected(ts);
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
return osmo_dyn_ts_disconnected(ts);
default:
return;
@@ -2467,39 +3434,31 @@ void cb_ts_disconnected(struct gsm_bts_trx_ts *ts)
static void ipacc_dyn_pdch_ts_connected(struct gsm_bts_trx_ts *ts, int rc)
{
if (rc) {
- LOGP(DRSL, LOGL_NOTICE, "%s PDCH ACT IPA operation failed (%d) in bts model\n",
- gsm_lchan_name(ts->lchan), rc);
+ LOGPLCHAN(ts->lchan, DRSL, LOGL_NOTICE, "PDCH ACT IPA operation failed (%d) in bts model\n", rc);
ipacc_dyn_pdch_complete(ts, rc);
return;
}
if (ts->flags & TS_F_PDCH_DEACT_PENDING) {
- if (ts->lchan[0].type != GSM_LCHAN_TCH_F)
- LOGP(DRSL, LOGL_ERROR, "%s PDCH DEACT error:"
- " timeslot connected, so expecting"
- " lchan type TCH/F, but is %s\n",
- gsm_lchan_name(ts->lchan),
- gsm_lchant_name(ts->lchan[0].type));
+ if (ts->lchan[0].type != GSM_LCHAN_TCH_F) {
+ LOGPLCHAN(ts->lchan, DRSL, LOGL_ERROR, "PDCH DEACT error: timeslot connected, so "
+ "expecting lchan type TCH/F, but is %s\n", gsm_lchant_name(ts->lchan[0].type));
+ }
- LOGP(DRSL, LOGL_DEBUG, "%s PDCH DEACT operation:"
- " timeslot connected as TCH/F\n",
- gsm_lchan_name(ts->lchan));
+ LOGPLCHAN(ts->lchan, DRSL, LOGL_DEBUG, "PDCH DEACT operation: timeslot connected as TCH/F\n");
/* During PDCH DEACT, we're done right after the TCH/F came
* back up. */
ipacc_dyn_pdch_complete(ts, 0);
} else if (ts->flags & TS_F_PDCH_ACT_PENDING) {
- if (ts->lchan[0].type != GSM_LCHAN_PDTCH)
- LOGP(DRSL, LOGL_ERROR, "%s PDCH ACT error:"
- " timeslot connected, so expecting"
- " lchan type PDTCH, but is %s\n",
- gsm_lchan_name(ts->lchan),
+ if (ts->lchan[0].type != GSM_LCHAN_PDTCH) {
+ LOGPLCHAN(ts->lchan, DRSL, LOGL_ERROR, "PDCH ACT error: timeslot connected, "
+ "so expecting lchan type PDTCH, but is %s\n",
gsm_lchant_name(ts->lchan[0].type));
+ }
- LOGP(DRSL, LOGL_DEBUG, "%s PDCH ACT operation:"
- " timeslot connected as PDTCH\n",
- gsm_lchan_name(ts->lchan));
+ LOGPLCHAN(ts->lchan, DRSL, LOGL_DEBUG, "PDCH ACT operation: timeslot connected as PDTCH\n");
/* The PDTCH is connected, now tell the PCU about it. Except
* when the PCU is not connected (yet), then there's nothing
@@ -2522,30 +3481,31 @@ static void ipacc_dyn_pdch_ts_connected(struct gsm_bts_trx_ts *ts, int rc)
static void osmo_dyn_ts_connected(struct gsm_bts_trx_ts *ts, int rc)
{
- struct msgb *msg = ts->dyn.pending_chan_activ;
- ts->dyn.pending_chan_activ = NULL;
+ unsigned int ln;
if (rc) {
- LOGP(DRSL, LOGL_NOTICE, "%s PDCH ACT OSMO operation failed (%d) in bts model\n",
- gsm_lchan_name(ts->lchan), rc);
+ LOGPLCHAN(ts->lchan, DRSL, LOGL_NOTICE, "PDCH ACT OSMO operation failed (%d) in bts model\n", rc);
ipacc_dyn_pdch_complete(ts, rc);
return;
}
- if (!msg) {
- LOGP(DRSL, LOGL_ERROR,
- "%s TS re-connected, but no chan activ msg pending\n",
- gsm_ts_and_pchan_name(ts));
- return;
- }
-
ts->dyn.pchan_is = ts->dyn.pchan_want;
DEBUGP(DRSL, "%s Connected\n", gsm_ts_and_pchan_name(ts));
- /* continue where we left off before re-connecting the TS. */
- rc = rsl_rx_chan_activ(msg);
- if (rc != 1)
- msgb_free(msg);
+ /* Handle postponed RSL CHANnel ACTIVation messages (if any) */
+ for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
+ struct gsm_lchan *lchan = &ts->lchan[ln];
+
+ if (lchan->pending_chan_activ == NULL)
+ continue;
+
+ struct msgb *msg = lchan->pending_chan_activ;
+ lchan->pending_chan_activ = NULL;
+
+ /* Continue where we left off before re-connecting the TS */
+ if (rsl_rx_chan_activ(msg) != 1)
+ msgb_free(msg);
+ }
}
void cb_ts_connected(struct gsm_bts_trx_ts *ts, int rc)
@@ -2555,7 +3515,7 @@ void cb_ts_connected(struct gsm_bts_trx_ts *ts, int rc)
switch (ts->pchan) {
case GSM_PCHAN_TCH_F_PDCH:
return ipacc_dyn_pdch_ts_connected(ts, rc);
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
return osmo_dyn_ts_connected(ts, rc);
default:
return;
@@ -2569,10 +3529,10 @@ void ipacc_dyn_pdch_complete(struct gsm_bts_trx_ts *ts, int rc)
pdch_act = ts->flags & TS_F_PDCH_ACT_PENDING;
- if ((ts->flags & TS_F_PDCH_PENDING_MASK) == TS_F_PDCH_PENDING_MASK)
- LOGP(DRSL, LOGL_ERROR,
- "%s Internal Error: both PDCH ACT and PDCH DEACT pending\n",
- gsm_lchan_name(ts->lchan));
+ if ((ts->flags & TS_F_PDCH_PENDING_MASK) == TS_F_PDCH_PENDING_MASK) {
+ LOGPLCHAN(ts->lchan, DRSL, LOGL_ERROR,
+ "Internal Error: both PDCH ACT and PDCH DEACT pending\n");
+ }
ts->flags &= ~TS_F_PDCH_PENDING_MASK;
@@ -2588,9 +3548,8 @@ void ipacc_dyn_pdch_complete(struct gsm_bts_trx_ts *ts, int rc)
ts->flags |= TS_F_PDCH_ACTIVE;
else
ts->flags &= ~TS_F_PDCH_ACTIVE;
- DEBUGP(DRSL, "%s %s switched to %s mode (ts->flags == %x)\n",
- gsm_lchan_name(ts->lchan), gsm_pchan_name(ts->pchan),
- pdch_act? "PDCH" : "TCH/F", ts->flags);
+ LOGPLCHAN(ts->lchan, DRSL, LOGL_DEBUG, "%s switched to %s mode (ts->flags == %x)\n",
+ gsm_pchan_name(ts->pchan), pdch_act ? "PDCH" : "TCH/F", ts->flags);
rc = rsl_tx_dyn_pdch_ack(ts->lchan, pdch_act);
if (rc)
@@ -2692,8 +3651,11 @@ static int rsl_rx_rll(struct gsm_bts_trx *trx, struct msgb *msg)
return -1;
}
- DEBUGP(DRLL, "%s Rx RLL %s Abis -> LAPDm\n", gsm_lchan_name(lchan),
- rsl_msg_name(rh->c.msg_type));
+ /* VGCS Uplink is released by MSC using REL-REQ. */
+ if (rh->c.msg_type == RSL_MT_REL_REQ)
+ vgcs_talker_reset(lchan, true);
+
+ LOGPLCHAN(lchan, DRLL, LOGL_DEBUG, "Rx RLL %s Abis -> LAPDm\n", rsl_msg_name(rh->c.msg_type));
/* make copy of RLL header, as the message will be free'd in case of erroneous return */
rh2 = *rh;
@@ -2788,15 +3750,15 @@ static int handle_gprs_susp_req(struct msgb *msg)
int rc;
if (!gh || msgb_l3len(msg) < sizeof(*gh)+sizeof(*gsr)) {
- LOGP(DRSL, LOGL_NOTICE, "%s Short GPRS SUSPEND REQ received, ignoring\n", gsm_lchan_name(msg->lchan));
+ LOGPLCHAN(msg->lchan, DRSL, LOGL_NOTICE, "Short GPRS SUSPEND REQ received, ignoring\n");
+ msgb_free(msg);
return -EINVAL;
}
gsr = (struct gsm48_gprs_susp_req *) gh->data;
tlli = osmo_ntohl(gsr->tlli);
- LOGP(DRSL, LOGL_INFO, "%s Fwd GPRS SUSPEND REQ for TLLI=0x%08x to PCU\n",
- gsm_lchan_name(msg->lchan), tlli);
+ LOGPLCHAN(msg->lchan, DRSL, LOGL_INFO, "Fwd GPRS SUSPEND REQ for TLLI=0x%08x to PCU\n", tlli);
rc = pcu_tx_susp_req(msg->lchan, tlli, gsr->ra_id, gsr->cause);
msgb_free(msg);
@@ -2804,16 +3766,6 @@ static int handle_gprs_susp_req(struct msgb *msg)
return rc;
}
-static inline uint8_t ms_to2rsl(const struct gsm_lchan *lchan, const struct lapdm_entity *le)
-{
- return (lchan->ms_t_offs >= 0) ? lchan->ms_t_offs : (lchan->p_offs - le->ta);
-}
-
-static inline bool ms_to_valid(const struct gsm_lchan *lchan)
-{
- return (lchan->ms_t_offs >= 0) || (lchan->p_offs >= 0);
-}
-
struct osmo_bts_supp_meas_info {
int16_t toa256_mean;
int16_t toa256_min;
@@ -2821,12 +3773,12 @@ struct osmo_bts_supp_meas_info {
uint16_t toa256_std_dev;
} __attribute__((packed));
-/* 8.4.8 MEASUREMENT RESult */
-static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, const struct lapdm_entity *le)
+/* Compose and send 8.4.8 MEASUREMENT RESult via RSL. (timing_offset=-1 -> not present) */
+int rsl_tx_meas_res(struct gsm_lchan *lchan, const uint8_t *l3, unsigned int l3_len, int timing_offset)
{
struct msgb *msg;
uint8_t meas_res[16];
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
int res_valid = lchan->meas.flags & LC_UL_M_F_RES_VALID;
struct gsm_bts *bts = lchan->ts->trx->bts;
@@ -2841,20 +3793,19 @@ static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, con
return -ENOMEM;
LOGPLCHAN(lchan, DRSL, LOGL_DEBUG,
- "Send Meas RES: NUM:%u, RXLEV_FULL:%u, RXLEV_SUB:%u, RXQUAL_FULL:%u, RXQUAL_SUB:%u, MS_PWR:%u, UL_TA:%u, L3_LEN:%d, TimingOff:%u\n",
+ "Send Meas RES: NUM:%u, RXLEV_FULL:%u, RXLEV_SUB:%u, RXQUAL_FULL:%u, RXQUAL_SUB:%u, MS_PWR:%u, UL_TA:%u, L3_LEN:%u, TimingOff:%u\n",
lchan->meas.res_nr,
lchan->meas.ul_res.full.rx_lev,
lchan->meas.ul_res.sub.rx_lev,
lchan->meas.ul_res.full.rx_qual,
lchan->meas.ul_res.sub.rx_qual,
- lchan->meas.l1_info[0],
- lchan->meas.l1_info[1], l3_len, ms_to2rsl(lchan, le) - MEAS_MAX_TIMING_ADVANCE);
+ lchan->meas.l1_info.ms_pwr,
+ lchan->meas.l1_info.ta, l3_len, timing_offset - MEAS_MAX_TIMING_ADVANCE);
- msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr++);
+ msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr);
size_t ie_len = gsm0858_rsl_ul_meas_enc(&lchan->meas.ul_res,
lchan->tch.dtx.dl_active,
meas_res);
- lchan->tch.dtx.dl_active = false;
if (ie_len >= 3) {
if (bts->supp_meas_toa256 && lchan->meas.flags & LC_UL_M_F_OSMO_EXT_VALID) {
struct osmo_bts_supp_meas_info *smi;
@@ -2867,26 +3818,23 @@ static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, con
* to know the total propagation time between MS and BTS, we need to add
* the actual TA value applied by the MS plus the respective toa256 value in
* 1/256 symbol periods. */
- int16_t ta256 = lchan_get_ta(lchan) * 256;
+ int16_t ta256 = lchan->meas.l1_info.ta * 256;
smi->toa256_mean = htons(ta256 + lchan->meas.ms_toa256);
smi->toa256_min = htons(ta256 + lchan->meas.ext.toa256_min);
smi->toa256_max = htons(ta256 + lchan->meas.ext.toa256_max);
smi->toa256_std_dev = htons(lchan->meas.ext.toa256_std_dev);
- lchan->meas.flags &= ~LC_UL_M_F_OSMO_EXT_VALID;
}
msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, ie_len, meas_res);
- lchan->meas.flags &= ~LC_UL_M_F_RES_VALID;
}
- msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->meas.bts_tx_pwr);
+ msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power_ctrl.current / 2);
if (lchan->meas.flags & LC_UL_M_F_L1_VALID) {
- msgb_tv_fixed_put(msg, RSL_IE_L1_INFO, 2, lchan->meas.l1_info);
- lchan->meas.flags &= ~LC_UL_M_F_L1_VALID;
+ msgb_tv_fixed_put(msg, RSL_IE_L1_INFO, sizeof(lchan->meas.l1_info), (uint8_t*)&lchan->meas.l1_info);
}
- msgb_tl16v_put(msg, RSL_IE_L3_INFO, l3_len, l3);
- if (ms_to_valid(lchan)) {
- msgb_tv_put(msg, RSL_IE_MS_TIMING_OFFSET, ms_to2rsl(lchan, le));
- lchan->ms_t_offs = -1;
- lchan->p_offs = -1;
+
+ if (l3 && l3_len > 0) {
+ msgb_tl16v_put(msg, RSL_IE_L3_INFO, l3_len, l3);
+ if (timing_offset != -1)
+ msgb_tv_put(msg, RSL_IE_MS_TIMING_OFFSET, timing_offset);
}
rsl_dch_push_hdr(msg, RSL_MT_MEAS_RES, chan_nr);
@@ -2914,22 +3862,42 @@ int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx)
msg->trx = lchan->ts->trx;
msg->lchan = lchan;
- /* check if this is a measurement report from SACCH which needs special
- * processing before forwarding */
- if (rslms_is_meas_rep(msg)) {
- int rc;
+ /* If DL estabishment on main signaling link and SAPI 0 with L3 info is expected. */
+ if (lchan->l3_info_estab && rh->msg_type == RSL_MT_EST_IND) {
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ if ((rllh->link_id & 0xc7) == 0) {
+ /* Reject initial establishment without L3 info. */
+ if (msgb_l2len(msg) == sizeof(struct abis_rsl_rll_hdr)) {
+ LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "RLL EST IND without contention resolution.\n");
+ /* Release normally, re-use the msgb. */
+ rh->msg_type = RSL_MT_REL_REQ;
+ msgb_tv_put(msg, RSL_IE_RELEASE_MODE, RSL_REL_NORMAL);
+ return rsl_rx_rll(lchan->ts->trx, msg);
+ }
+ /* Re-estabishment without contention resoltuion is allowed. */
+ lchan->l3_info_estab = false;
+ }
+ }
- LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Handing RLL msg %s from LAPDm to MEAS REP\n",
+ /* If this is a Measurement Report, then we simply ignore it,
+ * because it has already been processed in l1sap_ph_data_ind(). */
+ if (rslms_is_meas_rep(msg)) {
+ msgb_free(msg);
+ return 0;
+ } else if (rslms_is_gprs_susp_req(msg)) {
+ return handle_gprs_susp_req(msg);
+ } else {
+ LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Fwd RLL msg %s from LAPDm to A-bis\n",
rsl_msg_name(rh->msg_type));
/* REL_IND handling */
- if (rh->msg_type == RSL_MT_REL_IND &&
- (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H)) {
+ if (rh->msg_type == RSL_MT_REL_IND && lchan_is_tch(lchan)) {
+ vgcs_talker_reset(lchan, true);
LOGPLCHAN(lchan, DRSL, LOGL_INFO,
"Scheduling %s to L3 in next associated TCH-RTS.ind\n",
rsl_msg_name(rh->msg_type));
- if(lchan->pending_rel_ind_msg) {
+ if (lchan->pending_rel_ind_msg) {
LOGPLCHAN(lchan, DRSL, LOGL_INFO,
"Dropping pending release indication message\n");
msgb_free(lchan->pending_rel_ind_msg);
@@ -2939,15 +3907,6 @@ int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx)
return 0;
}
- rc = rsl_tx_meas_res(lchan, msgb_l3(msg), msgb_l3len(msg), le);
- msgb_free(msg);
- return rc;
- } else if (rslms_is_gprs_susp_req(msg)) {
- return handle_gprs_susp_req(msg);
- } else {
- LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Fwd RLL msg %s from LAPDm to A-bis\n",
- rsl_msg_name(rh->msg_type));
-
return abis_bts_rsl_sendmsg(msg);
}
}
@@ -2994,8 +3953,10 @@ static int rsl_rx_cchan(struct gsm_bts_trx *trx, struct msgb *msg)
case RSL_MT_SMS_BC_CMD:
ret = rsl_rx_sms_bcast_cmd(trx, msg);
break;
- case RSL_MT_SMS_BC_REQ:
case RSL_MT_NOT_CMD:
+ ret = rsl_rx_notification_cmd(trx, msg);
+ break;
+ case RSL_MT_SMS_BC_REQ:
LOGPLCHAN(msg->lchan, DRSL, LOGL_NOTICE, "unimplemented RSL cchan msg_type %s\n",
rsl_msg_name(cch->c.msg_type));
rsl_tx_error_report(trx, RSL_ERR_MSG_TYPE, &cch->chan_nr, NULL, msg);
@@ -3003,6 +3964,10 @@ static int rsl_rx_cchan(struct gsm_bts_trx *trx, struct msgb *msg)
case RSL_MT_OSMO_ETWS_CMD:
ret = rsl_rx_osmo_etws_cmd(trx, msg);
break;
+ /* Osmocom specific extension for BCCH carrier power reduction */
+ case RSL_MT_BS_POWER_CONTROL:
+ ret = rsl_rx_bs_pwr_ctrl(msg);
+ break;
default:
LOGPLCHAN(msg->lchan, DRSL, LOGL_NOTICE, "undefined RSL cchan msg_type 0x%02x\n",
cch->c.msg_type);
@@ -3113,6 +4078,9 @@ static int rsl_rx_trx(struct gsm_bts_trx *trx, struct msgb *msg)
case RSL_MT_SACCH_FILL:
ret = rsl_rx_sacch_fill(trx, msg);
break;
+ case RSL_MT_IPAC_MEAS_PREPROC_DFT:
+ ret = rsl_rx_meas_preproc_dft(trx, msg);
+ break;
default:
LOGP(DRSL, LOGL_NOTICE, "undefined RSL TRX msg_type 0x%02x\n",
th->msg_type);
@@ -3171,14 +4139,6 @@ static int rsl_rx_ipaccess(struct gsm_bts_trx *trx, struct msgb *msg)
return ret;
}
-int lchan_deactivate(struct gsm_lchan *lchan)
-{
- OSMO_ASSERT(lchan);
-
- lchan->ciph_state = 0;
- return bts_model_lchan_deactivate(lchan);
-}
-
int down_rsl(struct gsm_bts_trx *trx, struct msgb *msg)
{
struct abis_rsl_common_hdr *rslh;