[MERGED] osmo-bts[master]: dyn TS: common TCH/F_TCH/H_PDCH implementation

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Neels Hofmeyr gerrit-no-reply at lists.osmocom.org
Tue Jul 26 08:05:15 UTC 2016


Neels Hofmeyr has submitted this change and it was merged.

Change subject: dyn TS: common TCH/F_TCH/H_PDCH implementation
......................................................................


dyn TS: common TCH/F_TCH/H_PDCH implementation

common/l1sap: For dyn TS, the BSC will issue RSL Chan Activ requests with a
non-standard chan_nr. While the rest of the code now understands that, the L1
phy will not. Translate to standard PDCH (== TCH/F).

common/oml: use dyn TS' current pchan mode for lchans config.

common/pcu_sock: detect desired PDCH mode of dyn TS.

common/rsl: implement reconnection chain of a TS for changing its pchan:

* rsl_rx_chan_activ():
** Add dyn_pchan_from_chan_nr() to derive the requested pchan from the RSL
   chan_nr IE.
** Notice the need for a pchan change and invoke dyn_ts_l1_reconnect() (s.b.)
** Make Chan Mode IE presence optional, because the non-standard PDCH
   activation message is simpler and does not require it.
** Do PDCH activation via PCU.

* Add dyn_ts_l1_reconnect(): store state and disconnect the L1 channel;
  then wait for cb_ts_disconnected().

* Add osmo_dyn_ts_disconnected() to cb_ts_disconnected():
  verify state and connect with the new pchan type; then wait for
  cb_ts_connected().

* Add osmo_dyn_ts_connected() to cb_ts_connected(), which re-issues
  the cached chan activation message from before disconnecting the L1 channel.

* Also send an rf chan rel/act ack for dyn TS upon PDCH de/act via PCU.

* Add dyn_ts_pdch_release(): on channel release of a dyn TS in PDCH mode,
  release via the PCU. Call from rsl_rx_rd_chan_rel().

Change-Id: I463bb6b4e57674f091c3badba9257374961c52c7
---
M src/common/l1sap.c
M src/common/oml.c
M src/common/pcu_sock.c
M src/common/rsl.c
4 files changed, 235 insertions(+), 12 deletions(-)

Approvals:
  Harald Welte: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index 23bc66e..0bbdb9e 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -1078,6 +1078,11 @@
 {
 	struct osmo_phsap_prim l1sap;
 
+	/* The caller may pass a non-standard RSL_CHAN_OSMO_PDCH, which the L1
+	 * doesn't understand. Use the normal TCH/F cbits instead. */
+	if ((chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_OSMO_PDCH)
+		chan_nr = RSL_CHAN_Bm_ACCHs | (chan_nr & ~RSL_CHAN_NR_MASK);
+
 	memset(&l1sap, 0, sizeof(l1sap));
 	osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_REQUEST,
 		NULL);
diff --git a/src/common/oml.c b/src/common/oml.c
index 8970858..690a81d 100644
--- a/src/common/oml.c
+++ b/src/common/oml.c
@@ -694,6 +694,15 @@
 		pchan = ts->flags & TS_F_PDCH_ACTIVE? GSM_PCHAN_PDCH
 						    : GSM_PCHAN_TCH_F;
 
+	/* Osmocom RSL CHAN ACT style dyn TS */
+	if (pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) {
+		pchan = ts->dyn.pchan_is;
+
+		/* If the dyn TS doesn't have a pchan yet, do nothing. */
+		if (pchan == GSM_PCHAN_NONE)
+			return 0;
+	}
+
 	return conf_lchans_as_pchan(ts, pchan);
 }
 
diff --git a/src/common/pcu_sock.c b/src/common/pcu_sock.c
index 845e5bb..d40b19b 100644
--- a/src/common/pcu_sock.c
+++ b/src/common/pcu_sock.c
@@ -110,6 +110,14 @@
 		else
 			return (ts->flags & TS_F_PDCH_ACT_PENDING);
 	}
+	if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) {
+		/*
+		 * When we're busy de-/activating the PDCH, we first set
+		 * ts->dyn.pchan_want, tell the PCU about it and wait for a
+		 * response. So only care about dyn.pchan_want here.
+		 */
+		return ts->dyn.pchan_want == GSM_PCHAN_PDCH;
+	}
 	return false;
 }
 
diff --git a/src/common/rsl.c b/src/common/rsl.c
index 69ed108..490ae28 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -502,7 +502,15 @@
 	struct msgb *msg;
 	uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
 
-	if (lchan->rel_act_kind != LCHAN_REL_ACT_RSL) {
+	/*
+	 * Normally, PDCH deactivation via PCU does not ack back to the BSC.
+	 * But for GSM_PCHAN_TCH_F_TCH_H_PDCH, send a non-standard rel ack for
+	 * LCHAN_REL_ACT_PCU, since the rel req came from RSL initially.
+	 */
+	if (lchan->rel_act_kind != LCHAN_REL_ACT_RSL
+	    && !(lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
+		 && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH
+		 && lchan->rel_act_kind == LCHAN_REL_ACT_PCU)) {
 		LOGP(DRSL, LOGL_NOTICE, "%s not sending REL ACK\n",
 			gsm_lchan_name(lchan));
 		return 0;
@@ -534,7 +542,15 @@
 	uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
 	uint8_t ie[2];
 
-	if (lchan->rel_act_kind != LCHAN_REL_ACT_RSL) {
+	/*
+	 * Normally, PDCH activation via PCU does not ack back to the BSC.
+	 * But for GSM_PCHAN_TCH_F_TCH_H_PDCH, send a non-standard act ack for
+	 * LCHAN_REL_ACT_PCU, since the act req came from RSL initially.
+	 */
+	if (lchan->rel_act_kind != LCHAN_REL_ACT_RSL
+	    && !(lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
+		 && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH
+		 && lchan->rel_act_kind == LCHAN_REL_ACT_PCU)) {
 		LOGP(DRSL, LOGL_NOTICE, "%s not sending CHAN ACT ACK\n",
 			gsm_lchan_name(lchan));
 		return 0;
@@ -703,11 +719,60 @@
 	return 0;
 }
 
+/*!
+ * 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)
+{
+	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_PDCH:
+		break;
+	default:
+		LOGP(DRSL, LOGL_ERROR,
+		     "%s Cannot reconnect as pchan %s\n",
+		     gsm_ts_and_pchan_name(ts),
+		     gsm_pchan_name(ts->dyn.pchan_want));
+		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);
+}
+
+static enum gsm_phys_chan_config dyn_pchan_from_chan_nr(uint8_t chan_nr)
+{
+	uint8_t cbits = chan_nr & RSL_CHAN_NR_MASK;
+	switch (cbits) {
+	case RSL_CHAN_Bm_ACCHs:
+		return GSM_PCHAN_TCH_F;
+	case RSL_CHAN_Lm_ACCHs:
+	case (RSL_CHAN_Lm_ACCHs + RSL_CHAN_NR_1):
+		return GSM_PCHAN_TCH_H;
+	case RSL_CHAN_OSMO_PDCH:
+		return GSM_PCHAN_PDCH;
+	default:
+		LOGP(DRSL, LOGL_ERROR,
+		     "chan nr 0x%x not covered by dyn_pchan_from_chan_nr()\n",
+		     chan_nr);
+		return GSM_PCHAN_UNKNOWN;
+	}
+}
+
 /* 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 tlv_parsed tp;
 	uint8_t type;
@@ -718,6 +783,25 @@
 		     "%s: error: lchan is not available, but in state: %s.\n",
 		     gsm_lchan_name(lchan), 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) {
+		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));
+
+		if (ts->dyn.pchan_is != ts->dyn.pchan_want) {
+			/*
+			 * The phy has the timeslot connected in a different
+			 * 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);
+			if (rc)
+				return rsl_tx_chan_act_nack(lchan,
+							    RSL_ERR_NORMAL_UNSPEC);
+			/* indicate that the msgb should not be freed. */
+			return 1;
+		}
 	}
 
 	/* Initialize channel defaults */
@@ -735,12 +819,14 @@
 	type = *TLVP_VAL(&tp, RSL_IE_ACT_TYPE);
 
 	/* 9.3.6 Channel Mode */
-	if (!TLVP_PRESENT(&tp, RSL_IE_CHAN_MODE)) {
-		LOGP(DRSL, LOGL_NOTICE, "missing Channel Mode\n");
-		return rsl_tx_chan_act_nack(lchan, RSL_ERR_MAND_IE_ERROR);
+	if (type != RSL_ACT_OSMO_PDCH) {
+		if (!TLVP_PRESENT(&tp, RSL_IE_CHAN_MODE)) {
+			LOGP(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);
 	}
-	cm = (struct rsl_ie_chan_mode *) TLVP_VAL(&tp, RSL_IE_CHAN_MODE);
-	lchan_tchmode_from_cmode(lchan, cm);
 
 	/* 9.3.7 Encryption Information */
 	if (TLVP_PRESENT(&tp, RSL_IE_ENCR_INFO)) {
@@ -839,13 +925,64 @@
 	LOGP(DRSL, LOGL_INFO, " chan_nr=0x%02x type=0x%02x mode=0x%02x\n",
 		dch->chan_nr, type, lchan->tch_mode);
 
-	/* actually activate the channel in the BTS */
+	/* Connecting PDCH on dyn TS goes via PCU instead. */
+	if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
+	    && ts->dyn.pchan_want == GSM_PCHAN_PDCH) {
+		/*
+		 * pcu_tx_info_ind() will pick up the ts->dyn.pchan_want. If
+		 * the PCU is not connected yet, ignore for now; the PCU will
+		 * catch up (and send the RSL ack) once it connects.
+		 */
+		if (pcu_connected()) {
+			DEBUGP(DRSL, "%s Activate via PCU\n", gsm_ts_and_pchan_name(ts));
+			rc = pcu_tx_info_ind();
+		}
+		else {
+			DEBUGP(DRSL, "%s Activate via PCU when PCU connects\n",
+			       gsm_ts_and_pchan_name(ts));
+			rc = 0;
+		}
+		if (rc)
+			return rsl_tx_error_report(msg->trx,
+						   RSL_ERR_NORMAL_UNSPEC);
+		return 0;
+	}
+
+	/* 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);
 	if (rc < 0)
 		return rsl_tx_chan_act_nack(lchan, -rc);
 
 	return 0;
+}
+
+static int dyn_ts_pdch_release(struct gsm_lchan *lchan)
+{
+	struct gsm_bts_trx_ts *ts = lchan->ts;
+
+	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;
+	}
+
+	/*
+	 * 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 0;
+	}
+
+	return pcu_tx_info_ind();
 }
 
 /* 8.4.14 RF CHANnel RELease is received */
@@ -862,6 +999,12 @@
 	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)
+		return dyn_ts_pdch_release(lchan);
+
 	l1sap_chan_rel(lchan->ts->trx, chan_nr);
 
 	lapdm_channel_exit(&lchan->lapdm_ch);
@@ -1766,10 +1909,40 @@
 		ipacc_dyn_pdch_complete(ts, rc);
 }
 
+static void osmo_dyn_ts_disconnected(struct gsm_bts_trx_ts *ts)
+{
+	DEBUGP(DRSL, "%s Disconnected\n", gsm_ts_and_pchan_name(ts));
+	ts->dyn.pchan_is = GSM_PCHAN_NONE;
+
+	switch (ts->dyn.pchan_want) {
+	case GSM_PCHAN_TCH_F:
+	case GSM_PCHAN_TCH_H:
+	case GSM_PCHAN_PDCH:
+		break;
+	default:
+		LOGP(DRSL, LOGL_ERROR,
+		     "%s Dyn TS disconnected, but invalid desired pchan",
+		     gsm_ts_and_pchan_name(ts));
+		ts->dyn.pchan_want = GSM_PCHAN_NONE;
+		/* TODO: how would this recover? */
+		return;
+	}
+
+	conf_lchans_as_pchan(ts, ts->dyn.pchan_want);
+	DEBUGP(DRSL, "%s Connect\n", gsm_ts_and_pchan_name(ts));
+	bts_model_ts_connect(ts, ts->dyn.pchan_want);
+}
+
 void cb_ts_disconnected(struct gsm_bts_trx_ts *ts)
 {
-	if (ts->pchan == GSM_PCHAN_TCH_F_PDCH)
-		ipacc_dyn_pdch_ts_disconnected(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:
+		return osmo_dyn_ts_disconnected(ts);
+	default:
+		return;
+	}
 }
 
 static void ipacc_dyn_pdch_ts_connected(struct gsm_bts_trx_ts *ts)
@@ -1823,10 +1996,38 @@
 	}
 }
 
+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;
+
+	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);
+}
+
 void cb_ts_connected(struct gsm_bts_trx_ts *ts)
 {
-	if (ts->pchan == GSM_PCHAN_TCH_F_PDCH)
-		ipacc_dyn_pdch_ts_connected(ts);
+	switch (ts->pchan) {
+	case GSM_PCHAN_TCH_F_PDCH:
+		return ipacc_dyn_pdch_ts_connected(ts);
+	case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+		return osmo_dyn_ts_connected(ts);
+	default:
+		return;
+	}
 }
 
 void ipacc_dyn_pdch_complete(struct gsm_bts_trx_ts *ts, int rc)

-- 
To view, visit https://gerrit.osmocom.org/605
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I463bb6b4e57674f091c3badba9257374961c52c7
Gerrit-PatchSet: 1
Gerrit-Project: osmo-bts
Gerrit-Branch: master
Gerrit-Owner: Neels Hofmeyr <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr at sysmocom.de>



More information about the gerrit-log mailing list