aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmocom/bsc/gsm_data.h3
-rw-r--r--src/osmo-bsc/abis_rsl.c17
-rw-r--r--src/osmo-bsc/assignment_fsm.c1
-rw-r--r--src/osmo-bsc/gsm_data.c69
-rw-r--r--src/osmo-bsc/timeslot_fsm.c27
5 files changed, 105 insertions, 12 deletions
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index fc6ce3077..88adb9ff5 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -1041,8 +1041,11 @@ uint8_t gsm_ts_tsc(const struct gsm_bts_trx_ts *ts);
enum gsm_phys_chan_config ts_pchan(struct gsm_bts_trx_ts *ts);
uint8_t pchan_subslots(enum gsm_phys_chan_config pchan);
+uint8_t pchan_subslots_vamos(enum gsm_phys_chan_config pchan);
bool ts_is_tch(struct gsm_bts_trx_ts *ts);
+struct gsm_lchan *gsm_lchan_vamos_to_primary(const struct gsm_lchan *lchan_vamos);
+struct gsm_lchan *gsm_lchan_primary_to_vamos(const struct gsm_lchan *lchan_primary);
struct gsm_bts *conn_get_bts(struct gsm_subscriber_connection *conn);
diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c
index c20693785..1f7b639fb 100644
--- a/src/osmo-bsc/abis_rsl.c
+++ b/src/osmo-bsc/abis_rsl.c
@@ -1708,10 +1708,11 @@ static bool force_free_lchan_for_emergency(struct chan_rqd *rqd)
* This will take a short amount of time. We need to come back and check regulary to see if we managed to
* free up another lchan. */
if (!rqd->release_lchan) {
+ struct gsm_lchan *release_lchan;
/* Pick any busy TCH/F or TCH/H lchan and inititate a channel
* release to make room for the incoming emergency call */
- rqd->release_lchan = get_any_lchan(rqd->bts);
- if (!rqd->release_lchan) {
+ rqd->release_lchan = release_lchan = get_any_lchan(rqd->bts);
+ if (!release_lchan) {
/* It can not happen that we first find out that there
* is no TCH/H or TCH/F available and at the same time
* we ware unable to find any busy TCH/H or TCH/F. In
@@ -1724,10 +1725,16 @@ static bool force_free_lchan_for_emergency(struct chan_rqd *rqd)
LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE,
"CHAN RQD/EMERGENCY-PRIORITY: inducing termination of lchan %s (state:%s) in favor of incoming EMERGENCY CALL!\n",
- gsm_lchan_name(rqd->release_lchan), osmo_fsm_inst_state_name(rqd->release_lchan->fi));
+ gsm_lchan_name(release_lchan), osmo_fsm_inst_state_name(release_lchan->fi));
+
+ lchan_release(release_lchan, !!(release_lchan->conn), true, 0,
+ gscon_last_eutran_plmn(release_lchan->conn));
- lchan_release(rqd->release_lchan, !!(rqd->release_lchan->conn), true, 0,
- gscon_last_eutran_plmn(rqd->release_lchan->conn));
+ /* Also release any overlapping VAMOS multiplexes on this lchan */
+ release_lchan = gsm_lchan_primary_to_vamos(release_lchan);
+ if (release_lchan)
+ lchan_release(release_lchan, !!(release_lchan->conn), true, 0,
+ gscon_last_eutran_plmn(release_lchan->conn));
} else {
/* BTS is shutting down, give up... */
if (rqd->release_lchan->ts->fi->state == TS_ST_NOT_INITIALIZED)
diff --git a/src/osmo-bsc/assignment_fsm.c b/src/osmo-bsc/assignment_fsm.c
index 65cd8a825..b6de29310 100644
--- a/src/osmo-bsc/assignment_fsm.c
+++ b/src/osmo-bsc/assignment_fsm.c
@@ -640,6 +640,7 @@ static void assignment_fsm_wait_lchan_active_onenter(struct osmo_fsm_inst *fi, u
.ta_known = true,
.tsc_set = req->tsc_set,
.tsc = req->tsc,
+ .vamos = conn->assignment.new_lchan->vamos.is_secondary,
};
lchan_activate(conn->assignment.new_lchan, &activ_info);
}
diff --git a/src/osmo-bsc/gsm_data.c b/src/osmo-bsc/gsm_data.c
index b33b1c39d..4d2d81181 100644
--- a/src/osmo-bsc/gsm_data.c
+++ b/src/osmo-bsc/gsm_data.c
@@ -337,8 +337,9 @@ char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts)
char *gsm_lchan_name_compute(void *ctx, const struct gsm_lchan *lchan)
{
struct gsm_bts_trx_ts *ts = lchan->ts;
- return talloc_asprintf(ctx, "(bts=%d,trx=%d,ts=%d,ss=%d)",
- ts->trx->bts->nr, ts->trx->nr, ts->nr, lchan->nr);
+ return talloc_asprintf(ctx, "(bts=%d,trx=%d,ts=%d,ss=%d%s)",
+ ts->trx->bts->nr, ts->trx->nr, ts->nr, lchan->nr,
+ lchan->vamos.is_secondary ? "-VAMOS" : "");
}
/* obtain the MO structure for a given object instance */
@@ -545,9 +546,13 @@ uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan)
{
- /* Note: non-standard Osmocom style dyn TS PDCH mode chan_nr is only used within
- * rsl_tx_dyn_ts_pdch_act_deact(). */
- return gsm_pchan2chan_nr(lchan->ts->pchan_is, lchan->ts->nr, lchan->nr);
+ uint8_t lchan_nr = lchan->nr;
+ /* The VAMOS lchans are behind the primary ones in the ts->lchan[] array. They keep their lchan->nr as in the
+ * array, but on the wire they are the "shadow" lchans for the primary lchans. For example, for TCH/F, there is
+ * a primary ts->lchan[0] and a VAMOS ts->lchan[1]. Still, the VAMOS lchan should send chan_nr = 0. */
+ if (lchan->vamos.is_secondary)
+ lchan_nr -= lchan->ts->max_primary_lchans;
+ return gsm_pchan2chan_nr(lchan->ts->pchan_is, lchan->ts->nr, lchan_nr);
}
static const uint8_t subslots_per_pchan[] = {
@@ -574,6 +579,30 @@ uint8_t pchan_subslots(enum gsm_phys_chan_config pchan)
return subslots_per_pchan[pchan];
}
+static const uint8_t subslots_per_pchan_vamos[] = {
+ [GSM_PCHAN_NONE] = 0,
+ [GSM_PCHAN_CCCH] = 0,
+ [GSM_PCHAN_PDCH] = 0,
+ [GSM_PCHAN_CCCH_SDCCH4] = 0,
+ /* VAMOS: on a TCH/F, there may be a TCH/H shadow */
+ [GSM_PCHAN_TCH_F] = 2,
+ [GSM_PCHAN_TCH_H] = 2,
+ [GSM_PCHAN_SDCCH8_SACCH8C] = 0,
+ [GSM_PCHAN_CCCH_SDCCH4_CBCH] = 0,
+ [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 0,
+ [GSM_PCHAN_TCH_F_TCH_H_PDCH] = 2,
+ [GSM_PCHAN_TCH_F_PDCH] = 2,
+};
+
+/* Return the maximum number of VAMOS secondary lchans that may be used in a timeslot of the given physical channel
+ * configuration. */
+uint8_t pchan_subslots_vamos(enum gsm_phys_chan_config pchan)
+{
+ if (pchan < 0 || pchan >= ARRAY_SIZE(subslots_per_pchan_vamos))
+ return 0;
+ return subslots_per_pchan_vamos[pchan];
+}
+
static bool pchan_is_tch(enum gsm_phys_chan_config pchan)
{
switch (pchan) {
@@ -590,6 +619,36 @@ bool ts_is_tch(struct gsm_bts_trx_ts *ts)
return pchan_is_tch(ts->pchan_is);
}
+/* For a VAMOS secondary shadow lchan, return its primary lchan. If the lchan is not a secondary lchan, return NULL. */
+struct gsm_lchan *gsm_lchan_vamos_to_primary(const struct gsm_lchan *lchan_vamos)
+{
+ struct gsm_lchan *lchan_primary;
+ if (!lchan_vamos || !lchan_vamos->vamos.is_secondary)
+ return NULL;
+ /* OsmoBSC currently does not support mixed TCH/F + TCH/H VAMOS multiplexes. Hence the primary <-> secondary
+ * relation is a simple index shift in the lchan array. If mixed multiplexes were allowed, a TCH/F primary might
+ * have two TCH/H VAMOS secondary lchans, etc. Fortunately, we don't need to care about that. */
+ lchan_primary = (struct gsm_lchan*)lchan_vamos - lchan_vamos->ts->max_primary_lchans;
+ if (!lchan_primary->fi)
+ return NULL;
+ return lchan_primary;
+}
+
+/* For a primary lchan, return its VAMOS secondary shadow lchan. If the lchan is not a primary lchan, return NULL. */
+struct gsm_lchan *gsm_lchan_primary_to_vamos(const struct gsm_lchan *lchan_primary)
+{
+ struct gsm_lchan *lchan_vamos;
+ if (!lchan_primary || lchan_primary->vamos.is_secondary)
+ return NULL;
+ /* OsmoBSC currently does not support mixed TCH/F + TCH/H VAMOS multiplexes. Hence the primary <-> secondary
+ * relation is a simple index shift in the lchan array. If mixed multiplexes were allowed, a TCH/F primary might
+ * have two TCH/H VAMOS secondary lchans, etc. Fortunately, we don't need to care about that. */
+ lchan_vamos = (struct gsm_lchan*)lchan_primary + lchan_primary->ts->max_primary_lchans;
+ if (!lchan_vamos->fi)
+ return NULL;
+ return lchan_vamos;
+}
+
struct gsm_bts *conn_get_bts(struct gsm_subscriber_connection *conn) {
if (!conn || !conn->lchan)
return NULL;
diff --git a/src/osmo-bsc/timeslot_fsm.c b/src/osmo-bsc/timeslot_fsm.c
index 047097280..64b98a67d 100644
--- a/src/osmo-bsc/timeslot_fsm.c
+++ b/src/osmo-bsc/timeslot_fsm.c
@@ -189,22 +189,45 @@ static void ts_fsm_err_ready_to_go_in_pdch(struct osmo_fsm_inst *fi, struct gsm_
void ts_set_pchan_is(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config pchan_is)
{
+ int i;
+ struct gsm_lchan *lchan;
ts->pchan_is = pchan_is;
ts->max_primary_lchans = pchan_subslots(ts->pchan_is);
LOG_TS(ts, LOGL_DEBUG, "pchan_is=%s max_primary_lchans=%d max_lchans_possible=%d\n",
gsm_pchan_name(ts->pchan_is), ts->max_primary_lchans, ts->max_lchans_possible);
+ switch (ts->pchan_is) {
+ case GSM_PCHAN_TCH_F:
+ case GSM_PCHAN_TCH_H:
+ for (i = 0; i < ts->max_lchans_possible; i++) {
+ lchan = &ts->lchan[i];
+ if (i < ts->max_primary_lchans)
+ lchan->vamos.is_secondary = false;
+ else
+ lchan->vamos.is_secondary = true;
+ }
+ break;
+ default:
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible)
+ lchan->vamos.is_secondary = false;
+ break;
+ }
}
static void ts_setup_lchans(struct gsm_bts_trx_ts *ts)
{
int i, max_lchans;
+ int max_lchans_vamos;
ts->pchan_on_init = ts->pchan_from_config;
ts_fsm_update_id(ts);
max_lchans = pchan_subslots(ts->pchan_on_init);
- LOG_TS(ts, LOGL_DEBUG, "max lchans: %d\n", max_lchans);
- ts->max_lchans_possible = max_lchans;
+ if (osmo_bts_has_feature(&ts->trx->bts->features, BTS_FEAT_VAMOS))
+ max_lchans_vamos = pchan_subslots_vamos(ts->pchan_on_init);
+ else
+ max_lchans_vamos = 0;
+ LOG_TS(ts, LOGL_DEBUG, "max lchans: %d + %d VAMOS secondaries\n", max_lchans, max_lchans_vamos);
+ ts->max_lchans_possible = max_lchans + max_lchans_vamos;
ts->max_primary_lchans = 0;
for (i = 0; i < ts->max_lchans_possible; i++) {