dyn PDCH: Automatically deactivate/activate PDCH on TCH/F+PDCH channel

Handle shared TCH/F+PDCH channels as regular TCH/F channels. Prior to
activation, deactivate PDCH mode.

After deactivation, restore PDCH mode.

Change-Id: I59712b8769cc3959ef114a6e12e77801816fe8b6
diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c
index abe2f9b..59176df 100644
--- a/openbsc/src/libbsc/abis_rsl.c
+++ b/openbsc/src/libbsc/abis_rsl.c
@@ -470,6 +470,15 @@
 	if (rc < 0)
 		return rc;
 
+	/* if channel is in PDCH mode, deactivate PDCH first */
+	if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH
+	    && (lchan->ts->flags & TS_F_PDCH_MODE)) {
+		/* store activation type and handover reference */
+		lchan->dyn_pdch.act_type = act_type;
+		lchan->dyn_pdch.ho_ref = ho_ref;
+		return rsl_ipacc_pdch_activate(lchan->ts, 0);
+	}
+
 	ta = lchan->rqd_ta;
 
 	/* BS11 requires TA shifted by 2 bits */
@@ -746,6 +755,11 @@
 		LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n",
 			gsm_lchan_name(lchan),
 			gsm_lchans_name(lchan->state));
+
+	/* Put PDCH channel back into PDCH mode first */
+	if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH)
+		return rsl_ipacc_pdch_activate(lchan->ts, 1);
+
 	do_lchan_free(lchan);
 
 	return 0;
@@ -1187,6 +1201,26 @@
 	return 0;
 }
 
+static int rsl_rx_pdch_act_ack(struct msgb *msg)
+{
+	msg->lchan->ts->flags |= TS_F_PDCH_MODE;
+
+	/* We have activated PDCH, so now the channel is available again. */
+	do_lchan_free(msg->lchan);
+
+	return 0;
+}
+
+static int rsl_rx_pdch_deact_ack(struct msgb *msg)
+{
+	msg->lchan->ts->flags &= ~TS_F_PDCH_MODE;
+
+	rsl_chan_activate_lchan(msg->lchan, msg->lchan->dyn_pdch.act_type,
+				msg->lchan->dyn_pdch.ho_ref);
+
+	return 0;
+}
+
 static int abis_rsl_rx_dchan(struct msgb *msg)
 {
 	struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
@@ -1225,14 +1259,14 @@
 		break;
 	case RSL_MT_IPAC_PDCH_ACT_ACK:
 		DEBUGP(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name);
-		msg->lchan->ts->flags |= TS_F_PDCH_MODE;
+		rc = rsl_rx_pdch_act_ack(msg);
 		break;
 	case RSL_MT_IPAC_PDCH_ACT_NACK:
 		LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name);
 		break;
 	case RSL_MT_IPAC_PDCH_DEACT_ACK:
 		DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name);
-		msg->lchan->ts->flags &= ~TS_F_PDCH_MODE;
+		rc = rsl_rx_pdch_deact_ack(msg);
 		break;
 	case RSL_MT_IPAC_PDCH_DEACT_NACK:
 		LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name);