aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeels Hofmeyr <neels@hofmeyr.de>2016-07-23 19:45:15 +0200
committerNeels Hofmeyr <neels@hofmeyr.de>2016-07-28 11:56:51 +0200
commitb91e6002a6c97cf1c19b14015f4f17a7ad7ace62 (patch)
tree158cf049a8ede9bcd6c03556c407ae84d5cd9a49
parent7af652c0b27d79322043a23f4aa081c919546a73 (diff)
dyn TS: implement pchan switchover logic
In struct gsm_lchan, add dyn.rqd_ref and dyn.rqd_ta. These save the Channel Requested details across the PDCH deactivation dance. abis_rsl.c: add static functions: * dyn_ts_switchover*() for the various stages of switchover between pchans. * pchan_for_lchant() to derive the desired pchan from the lchan type that was set during lchan_alloc(). * rsl_chan_activate_lchan_as_pdch() to compose the simpler RSL CHAN ACT message without introducing numerous special cases to the normal RSL CHAN ACT code. In rsl_chan_activate_lchan(), detect and initiate required pchan switchovers if requested pchan on a dyn TS differs. In rsl_rx_rf_chan_rel_ack(), initiate or continue pchan switchovers after a channel was released. In rsl_rx_chan_act_ack(), notice that a switchover is complete. In chan_alloc.c, add ts_subslots(): abis_rsl.c will need to know the number of subslots per pchan, to verify that all lchans are free before dyn TS switchover. The subslots_per_pchan[] array is static to lchan_alloc.c, and since we need a non-trivial check for dyn TS anyway, add public ts_subslots() to lchan_alloc.c, which also checks the current dyn pchan type. Change-Id: I5c6bce13092a10204113d84678c587c65e35e4fd
-rw-r--r--openbsc/include/openbsc/chan_alloc.h2
-rw-r--r--openbsc/include/openbsc/gsm_data_shared.h2
-rw-r--r--openbsc/src/libbsc/abis_rsl.c339
-rw-r--r--openbsc/src/libbsc/chan_alloc.c9
4 files changed, 351 insertions, 1 deletions
diff --git a/openbsc/include/openbsc/chan_alloc.h b/openbsc/include/openbsc/chan_alloc.h
index 78242e5b7..3e03b97d0 100644
--- a/openbsc/include/openbsc/chan_alloc.h
+++ b/openbsc/include/openbsc/chan_alloc.h
@@ -51,4 +51,6 @@ void network_chan_load(struct pchan_load *pl, struct gsm_network *net);
int trx_is_usable(struct gsm_bts_trx *trx);
+uint8_t ts_subslots(struct gsm_bts_trx_ts *ts);
+
#endif /* _CHAN_ALLOC_H */
diff --git a/openbsc/include/openbsc/gsm_data_shared.h b/openbsc/include/openbsc/gsm_data_shared.h
index b4ff0a43b..953bac62e 100644
--- a/openbsc/include/openbsc/gsm_data_shared.h
+++ b/openbsc/include/openbsc/gsm_data_shared.h
@@ -261,6 +261,8 @@ struct gsm_lchan {
/* channel activation type and handover ref */
uint8_t act_type;
uint8_t ho_ref;
+ struct gsm48_req_ref *rqd_ref;
+ uint8_t rqd_ta;
} dyn;
#else
/* Number of different GsmL1_Sapi_t used in osmo_bts_sysmo is 23.
diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c
index e260ce94a..8af3b9bd4 100644
--- a/openbsc/src/libbsc/abis_rsl.c
+++ b/openbsc/src/libbsc/abis_rsl.c
@@ -55,6 +55,11 @@ enum sacch_deact {
static int rsl_send_imm_assignment(struct gsm_lchan *lchan);
static void error_timeout_cb(void *data);
+static int dyn_ts_switchover_start(struct gsm_lchan *lchan,
+ enum gsm_phys_chan_config to_pchan);
+static int dyn_ts_switchover_continue(struct gsm_lchan *lchan);
+static int dyn_ts_switchover_failed(struct gsm_lchan *lchan, int rc);
+static void dyn_ts_switchover_complete(struct gsm_lchan *lchan);
static void send_lchan_signal(int sig_no, struct gsm_lchan *lchan,
struct gsm_meas_rep *resp)
@@ -432,6 +437,49 @@ static void mr_config_for_bts(struct gsm_lchan *lchan, struct msgb *msg)
lchan->mr_bts_lv + 1);
}
+static enum gsm_phys_chan_config pchan_for_lchant(enum gsm_chan_t type)
+{
+ switch (type) {
+ case GSM_LCHAN_TCH_F:
+ return GSM_PCHAN_TCH_F;
+ case GSM_LCHAN_TCH_H:
+ return GSM_PCHAN_TCH_H;
+ case GSM_LCHAN_NONE:
+ case GSM_LCHAN_PDTCH:
+ /* TODO: so far lchan->type is NONE in PDCH mode. PDTCH is only
+ * used in osmo-bts. Maybe set PDTCH and drop the NONE case
+ * here. */
+ return GSM_PCHAN_PDCH;
+ default:
+ return GSM_PCHAN_UNKNOWN;
+ }
+}
+
+/*! Tx simplified channel activation message for non-standard PDCH type. */
+static int rsl_chan_activate_lchan_as_pdch(struct gsm_lchan *lchan)
+{
+ struct msgb *msg;
+ struct abis_rsl_dchan_hdr *dh;
+
+ /* This might be called after release of the second lchan of a TCH/H,
+ * but PDCH activation should always happen on the first lchan. So
+ * switch to lchan->nr == 0. */
+ lchan = lchan->ts->lchan;
+
+ rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ);
+
+ msg = rsl_msgb_alloc();
+ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+ init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV);
+ dh->chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, GSM_PCHAN_PDCH);
+
+ msgb_tv_put(msg, RSL_IE_ACT_TYPE, RSL_ACT_OSMO_PDCH);
+
+ msg->dst = lchan->ts->trx->rsl_link;
+
+ return abis_rsl_sendmsg(msg);
+}
+
/* Chapter 8.4.1 */
int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type,
uint8_t ho_ref)
@@ -454,6 +502,63 @@ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type,
return rsl_ipacc_pdch_activate(lchan->ts, 0);
}
+ /*
+ * If necessary, release PDCH on dynamic TS. Note that sending a
+ * release here is only necessary when in PDCH mode; for TCH types, an
+ * RSL RF Chan Release is initiated by the BTS when a voice call ends,
+ * so when we reach this, it will already be released. If a dyn TS is
+ * in PDCH mode, it is still active and we need to initiate a release
+ * from the BSC side here.
+ *
+ * If pchan_is != pchan_want, the PDCH has already been taken down and
+ * the switchover now needs to enable the TCH lchan.
+ *
+ * To switch a dyn TS between TCH/H and TCH/F, it is sufficient to send
+ * a chan activ with the new lchan type, because it will already be
+ * released.
+ */
+ if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
+ && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH
+ && lchan->ts->dyn.pchan_is == lchan->ts->dyn.pchan_want) {
+ enum gsm_phys_chan_config pchan_want;
+ pchan_want = pchan_for_lchant(lchan->type);
+ if (lchan->ts->dyn.pchan_is != pchan_want) {
+ lchan->dyn.act_type = act_type,
+ lchan->dyn.ho_ref = ho_ref;
+ lchan->dyn.rqd_ref = lchan->rqd_ref;
+ lchan->dyn.rqd_ta = lchan->rqd_ta;
+ lchan->rqd_ref = NULL;
+ lchan->rqd_ta = 0;
+ DEBUGP(DRSL, "%s saved rqd_ref=%p ta=%u\n",
+ gsm_lchan_name(lchan), lchan->rqd_ref,
+ lchan->rqd_ta);
+ return dyn_ts_switchover_start(lchan, pchan_want);
+ }
+ }
+
+ DEBUGP(DRSL, "%s Tx RSL Channel Activate with act_type=%s\n",
+ gsm_ts_and_pchan_name(lchan->ts),
+ rsl_act_type_name(act_type));
+
+ if (act_type == RSL_ACT_OSMO_PDCH) {
+ if (lchan->ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH) {
+ LOGP(DRSL, LOGL_ERROR,
+ "%s PDCH channel activation only allowed on %s\n",
+ gsm_ts_and_pchan_name(lchan->ts),
+ gsm_pchan_name(GSM_PCHAN_TCH_F_TCH_H_PDCH));
+ return -EINVAL;
+ }
+ return rsl_chan_activate_lchan_as_pdch(lchan);
+ }
+
+ if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
+ && lchan->ts->dyn.pchan_want == GSM_PCHAN_PDCH) {
+ LOGP(DRSL, LOGL_ERROR,
+ "%s Expected PDCH activation kind\n",
+ gsm_ts_and_pchan_name(lchan->ts));
+ return -EINVAL;
+ }
+
rc = channel_mode_from_lchan(&cm, lchan);
if (rc < 0) {
LOGP(DRSL, LOGL_ERROR,
@@ -721,6 +826,7 @@ static int rsl_rf_chan_release_err(struct gsm_lchan *lchan)
static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan)
{
+ int ss;
struct gsm_bts_trx_ts *ts = lchan->ts;
DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", gsm_lchan_name(lchan));
@@ -755,6 +861,46 @@ static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan)
do_lchan_free(lchan);
/*
+ * Check Osmocom RSL CHAN ACT style dynamic TCH/F_TCH/H_PDCH TS for pending
+ * transitions in these cases:
+ *
+ * a) after PDCH was released due to switchover request, activate TCH.
+ * BSC initiated this switchover, so dyn.pchan_is != pchan_want and
+ * lchan->type has been set to the desired GSM_LCHAN_*.
+ *
+ * b) Voice call ended and a TCH is released. If the TS is now unused,
+ * switch to PDCH. Here still dyn.pchan_is == dyn.pchan_want because
+ * we're only just notified and may decide to switch to PDCH now.
+ */
+ if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) {
+ DEBUGP(DRSL, "%s Rx RSL Channel Release ack for lchan %u\n",
+ gsm_ts_and_pchan_name(ts), lchan->nr);
+
+ /* (a) */
+ if (ts->dyn.pchan_is != ts->dyn.pchan_want)
+ return dyn_ts_switchover_continue(lchan);
+
+ /* (b) */
+ if (ts->dyn.pchan_is != GSM_PCHAN_PDCH
+ && ts->trx->bts->gprs.mode != BTS_GPRS_NONE) {
+ for (ss = 0; ss < ts_subslots(ts); ss++) {
+ struct gsm_lchan *lc = &ts->lchan[ss];
+ if (lc->state != LCHAN_S_NONE) {
+ DEBUGP(DRSL, "%s lchan %u still in use\n",
+ gsm_ts_and_pchan_name(ts),
+ lc->nr);
+ /* An lchan is still used. */
+ return 0;
+ }
+ }
+ /* All channels are released, go to PDCH mode. */
+ DEBUGP(DRSL, "%s back to PDCH\n",
+ gsm_ts_and_pchan_name(ts));
+ return dyn_ts_switchover_start(lchan, GSM_PCHAN_PDCH);
+ }
+ }
+
+ /*
* Put a dynamic TCH/F_PDCH channel back to PDCH mode iff it was
* released successfully. If in error, the PDCH ACT will follow after
* T3111 in error_timeout_cb().
@@ -988,6 +1134,9 @@ static int rsl_rx_chan_act_ack(struct msgb *msg)
gsm_lchans_name(lchan->state));
rsl_lchan_set_state(lchan, LCHAN_S_ACTIVE);
+ if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH)
+ dyn_ts_switchover_complete(lchan);
+
if (lchan->rqd_ref) {
rsl_send_imm_assignment(lchan);
talloc_free(lchan->rqd_ref);
@@ -1514,7 +1663,15 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
return 0;
}
- if (lchan->state != LCHAN_S_NONE)
+ /*
+ * Expecting lchan state to be NONE, except for dyn TS in PDCH mode.
+ * Those are expected to be ACTIVE: the PDCH release will be sent from
+ * rsl_chan_activate_lchan() below.
+ */
+ if (lchan->state != LCHAN_S_NONE
+ && !(lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
+ && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH
+ && lchan->state == LCHAN_S_ACTIVE))
LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel "
"in state %s\n", gsm_lchan_name(lchan),
gsm_lchans_name(lchan->state));
@@ -2164,6 +2321,186 @@ static int abis_rsl_rx_ipacc(struct msgb *msg)
return rc;
}
+static int dyn_ts_switchover_start(struct gsm_lchan *lchan,
+ enum gsm_phys_chan_config to_pchan)
+{
+ int ss;
+ struct gsm_bts_trx_ts *ts = lchan->ts;
+ int rc = -EIO;
+
+ OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH);
+ DEBUGP(DRSL, "%s starting switchover to %s\n",
+ gsm_ts_and_pchan_name(ts), gsm_pchan_name(to_pchan));
+
+ if (ts->dyn.pchan_is != ts->dyn.pchan_want) {
+ LOGP(DRSL, LOGL_ERROR,
+ "%s: Attempt to switch dynamic channel to %s,"
+ " but is already in switchover.\n",
+ gsm_ts_and_pchan_name(ts),
+ gsm_pchan_name(to_pchan));
+ return ts->dyn.pchan_want == to_pchan? 0 : -EAGAIN;
+ }
+
+ if (ts->dyn.pchan_is == to_pchan) {
+ LOGP(DRSL, LOGL_INFO,
+ "%s %s Already is in %s mode, skipping switchover.\n",
+ gsm_ts_name(ts), gsm_pchan_name(ts->pchan),
+ gsm_pchan_name(to_pchan));
+ dyn_ts_switchover_complete(lchan);
+ return 0;
+ }
+
+ /* Paranoia: let's make sure all is indeed released. */
+ for (ss = 0; ss < ts_subslots(lchan->ts); ss++) {
+ struct gsm_lchan *lc = &ts->lchan[ss];
+ if (lc->state != LCHAN_S_NONE) {
+ LOGP(DRSL, LOGL_ERROR,
+ "%s Attempt to switch dynamic channel to %s,"
+ " but is not fully released.\n",
+ gsm_ts_and_pchan_name(ts),
+ gsm_pchan_name(to_pchan));
+ return -EAGAIN;
+ }
+ }
+
+ /* Record that we're busy switching. */
+ ts->dyn.pchan_want = to_pchan;
+
+ /*
+ * To switch from PDCH, we need to initiate the release from the BSC
+ * side. dyn_ts_switchover_continue() will be called from
+ * rsl_rx_rf_chan_rel_ack().
+ */
+ if (ts->dyn.pchan_is == GSM_PCHAN_PDCH) {
+ rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ);
+ rc = rsl_rf_chan_release(lchan, 0, SACCH_NONE);
+ if (rc) {
+ LOGP(DRSL, LOGL_ERROR,
+ "%s RSL RF Chan Release failed\n",
+ gsm_ts_and_pchan_name(ts));
+ return dyn_ts_switchover_failed(lchan, rc);
+ }
+ return 0;
+ }
+
+ /*
+ * To switch from TCH/F and TCH/H pchans, this has been called from
+ * rsl_rx_rf_chan_rel_ack(), i.e. release is complete. Go ahead and
+ * activate as new type. This will always be PDCH.
+ */
+ return dyn_ts_switchover_continue(lchan);
+}
+
+static int dyn_ts_switchover_continue(struct gsm_lchan *lchan)
+{
+ int rc;
+ uint8_t act_type;
+ uint8_t ho_ref;
+ struct gsm_bts_trx_ts *ts = lchan->ts;
+
+ OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH);
+ DEBUGP(DRSL, "%s switchover: release complete,"
+ " activating new pchan type\n",
+ gsm_ts_and_pchan_name(ts));
+
+ if (ts->dyn.pchan_is == ts->dyn.pchan_want) {
+ LOGP(DRSL, LOGL_ERROR,
+ "%s Requested to switchover dynamic channel to the"
+ " same type it is already in.\n",
+ gsm_ts_and_pchan_name(ts));
+ return 0;
+ }
+
+ /*
+ * For TCH/x, the lchan->type has been set in lchan_alloc(), but it may
+ * have been lost during channel release due to dynamic switchover.
+ *
+ * For PDCH, the lchan->type will actually remain NONE.
+ * TODO: set GSM_LCHAN_PDTCH?
+ */
+ switch (ts->dyn.pchan_want) {
+ case GSM_PCHAN_TCH_F:
+ lchan->type = GSM_LCHAN_TCH_F;
+ break;
+ case GSM_PCHAN_TCH_H:
+ lchan->type = GSM_LCHAN_TCH_H;
+ break;
+ case GSM_PCHAN_PDCH:
+ lchan->type = GSM_LCHAN_NONE;
+ break;
+ default:
+ LOGP(DRSL, LOGL_ERROR,
+ "%s Invalid target pchan for dynamic TS\n",
+ gsm_ts_and_pchan_name(ts));
+ }
+
+ act_type = (ts->dyn.pchan_want == GSM_PCHAN_PDCH)
+ ? RSL_ACT_OSMO_PDCH
+ : lchan->dyn.act_type;
+ ho_ref = (ts->dyn.pchan_want == GSM_PCHAN_PDCH)
+ ? 0
+ : lchan->dyn.ho_ref;
+
+ /* Fetch the rqd_ref back from before switchover started. */
+ if (lchan->rqd_ref) {
+ LOGP(DRSL, LOGL_ERROR,
+ "%s During dyn TS switchover, expecting no"
+ " Request Reference to be pending. Discarding!\n",
+ gsm_lchan_name(lchan));
+ talloc_free(lchan->rqd_ref);
+ lchan->rqd_ref = NULL;
+ }
+ lchan->rqd_ref = lchan->dyn.rqd_ref;
+ lchan->rqd_ta = lchan->dyn.rqd_ta;
+ lchan->dyn.rqd_ref = NULL;
+ lchan->dyn.rqd_ta = 0;
+
+ rc = rsl_chan_activate_lchan(lchan, act_type, ho_ref);
+ if (rc) {
+ LOGP(DRSL, LOGL_ERROR,
+ "%s RSL Chan Activate failed\n",
+ gsm_ts_and_pchan_name(ts));
+ return dyn_ts_switchover_failed(lchan, rc);
+ }
+ return 0;
+}
+
+static int dyn_ts_switchover_failed(struct gsm_lchan *lchan, int rc)
+{
+ struct gsm_bts_trx_ts *ts = lchan->ts;
+ ts->dyn.pchan_want = ts->dyn.pchan_is;
+ LOGP(DRSL, LOGL_ERROR, "%s Error %d during dynamic channel switchover."
+ " Going back to previous pchan.\n", gsm_ts_and_pchan_name(ts),
+ rc);
+ return rc;
+}
+
+static void dyn_ts_switchover_complete(struct gsm_lchan *lchan)
+{
+ enum gsm_phys_chan_config pchan_act;
+ enum gsm_phys_chan_config pchan_was;
+ struct gsm_bts_trx_ts *ts = lchan->ts;
+
+ OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH);
+
+ pchan_act = pchan_for_lchant(lchan->type);
+ /*
+ * Paranoia: do the types match?
+ * In case of errors: we've received an act ack already, so what to do
+ * about it? Logging the error should suffice for now.
+ */
+ if (pchan_act != ts->dyn.pchan_want)
+ LOGP(DRSL, LOGL_ERROR,
+ "%s Requested transition does not match lchan type %s\n",
+ gsm_ts_and_pchan_name(ts),
+ gsm_lchant_name(ts->lchan[0].type));
+
+ pchan_was = ts->dyn.pchan_is;
+ ts->dyn.pchan_is = ts->dyn.pchan_want = pchan_act;
+
+ LOGP(DRSL, LOGL_INFO, "%s switchover from %s complete.\n",
+ gsm_ts_and_pchan_name(ts), gsm_pchan_name(pchan_was));
+}
/* Entry-point where L2 RSL from BTS enters */
int abis_rsl_rcvmsg(struct msgb *msg)
diff --git a/openbsc/src/libbsc/chan_alloc.c b/openbsc/src/libbsc/chan_alloc.c
index f502740a4..1e5dd74cd 100644
--- a/openbsc/src/libbsc/chan_alloc.c
+++ b/openbsc/src/libbsc/chan_alloc.c
@@ -88,6 +88,15 @@ static const uint8_t subslots_per_pchan[] = {
*/
};
+/*! According to ts->pchan and possibly ts->dyn_pchan, return the number of
+ * logical channels available in the timeslot. */
+uint8_t ts_subslots(struct gsm_bts_trx_ts *ts)
+{
+ if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH)
+ return subslots_per_pchan[ts->dyn.pchan_is];
+ return subslots_per_pchan[ts->pchan];
+}
+
static struct gsm_lchan *
_lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan,
enum gsm_phys_chan_config dyn_as_pchan)