Add new BSC RLL layer of code

A caller can call rll_establish(lchan, link_id) and a callback to the GSM RLL
code.  He will get called back if the RLL link is established or receives some
error message, or the establishment times out.

We need this for proper SMS implementation, where we need to restablish a SAPI3
RLL link before transmitting the actual CP-DATA messages.
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index adef573..c1f3f13 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -2,4 +2,5 @@
 		 gsm_subscriber.h linuxlist.h msgb.h select.h tlv.h gsm_04_11.h \
 		 timer.h misdn.h chan_alloc.h telnet_interface.h paging.h \
 		 subchan_demux.h trau_frame.h e1_input.h trau_mux.h signal.h \
-		 gsm_utils.h ipaccess.h rs232.h openbscdefines.h rtp_proxy.h
+		 gsm_utils.h ipaccess.h rs232.h openbscdefines.h rtp_proxy.h \
+		 bsc_rll.h
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index 02bc521..a8734d1 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -476,6 +476,7 @@
 int rsl_imm_assign_cmd(struct gsm_bts *bts, u_int8_t len, u_int8_t *val);
 
 int rsl_data_request(struct msgb *msg, u_int8_t link_id);
+int rsl_establish_request(struct gsm_lchan *lchan, u_int8_t link_id);
 int rsl_relase_request(struct gsm_lchan *lchan, u_int8_t link_id);
 
 /* ip.access specfic RSL extensions */
diff --git a/openbsc/include/openbsc/bsc_rll.h b/openbsc/include/openbsc/bsc_rll.h
new file mode 100644
index 0000000..b2898d1
--- /dev/null
+++ b/openbsc/include/openbsc/bsc_rll.h
@@ -0,0 +1,19 @@
+#ifndef _BSC_RLL_H
+#define _BSC_RLL_H
+
+#include <openbsc/gsm_data.h>
+
+enum bsc_rllr_ind {
+	BSC_RLLR_IND_EST_CONF,
+	BSC_RLLR_IND_REL_IND,
+	BSC_RLLR_IND_ERR_IND,
+	BSC_RLLR_IND_TIMEOUT,
+};
+
+int rll_establish(struct gsm_lchan *lchan, u_int8_t link_id,
+		  void (*cb)(struct gsm_lchan *, u_int8_t, void *,
+			     enum bsc_rllr_ind),
+		  void *data);
+void rll_indication(struct gsm_lchan *lchan, u_int8_t link_id, u_int8_t type);
+
+#endif /* _BSC_RLL_H */
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index ed63c8e..82415af 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -10,7 +10,7 @@
 		gsm_04_11.c telnet_interface.c subchan_demux.c \
 		trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
 		input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c \
-		transaction.c rtp_proxy.c
+		transaction.c rtp_proxy.c bsc_rll.c
 
 libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
 
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 726ef23..1e828a9 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -32,6 +32,7 @@
 #include <openbsc/gsm_04_08.h>
 #include <openbsc/abis_rsl.h>
 #include <openbsc/chan_alloc.h>
+#include <openbsc/bsc_rll.h>
 #include <openbsc/debug.h>
 #include <openbsc/tlv.h>
 #include <openbsc/paging.h>
@@ -840,6 +841,24 @@
 	return abis_rsl_sendmsg(msg);
 }
 
+/* Send "ESTABLISH REQUEST" message with given L3 Info payload */
+/* Chapter 8.3.1 */
+int rsl_establish_request(struct gsm_lchan *lchan, u_int8_t link_id)
+{
+	struct msgb *msg = rsl_msgb_alloc();
+	struct abis_rsl_rll_hdr *rh;
+
+	rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));
+	init_llm_hdr(rh, RSL_MT_REL_REQ);
+	//rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
+	rh->chan_nr = lchan2chan_nr(lchan);
+	rh->link_id = link_id;
+
+	msg->trx = lchan->ts->trx;
+
+	return abis_rsl_sendmsg(msg);
+}
+
 /* Chapter 8.3.7 Request the release of multiframe mode of RLL connection.
    This is what higher layers should call.  The BTS then responds with
    RELEASE CONFIRM, which we in turn use to trigger RSL CHANNEL RELEASE,
@@ -1212,6 +1231,8 @@
 	u_int8_t *rlm_cause = rllh->data;
 
 	DEBUGPC(DRLL, "ERROR INDICATION cause=0x%02x\n", rlm_cause[1]);
+
+	rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND);
 		
 	if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED)
 		return rsl_chan_release(msg->lchan);
@@ -1254,9 +1275,16 @@
 			return gsm0408_rcvmsg(msg);
 		}
 		break;
+	case RSL_MT_EST_CONF:
+		DEBUGPC(DRLL, "ESTABLISH CONFIRMATION\n");
+		rll_indication(msg->lchan, rllh->link_id,
+				  BSC_RLLR_IND_EST_CONF);
+		break;
 	case RSL_MT_REL_IND:
 		/* BTS informs us of having received  DISC from MS */
 		DEBUGPC(DRLL, "RELEASE INDICATION\n");
+		rll_indication(msg->lchan, rllh->link_id,
+				  BSC_RLLR_IND_REL_IND);
 		/* we can now releae the channel on the BTS/Abis side */
 		rsl_chan_release(msg->lchan);
 		break;
diff --git a/openbsc/src/bsc_rll.c b/openbsc/src/bsc_rll.c
new file mode 100644
index 0000000..09c3bc2
--- /dev/null
+++ b/openbsc/src/bsc_rll.c
@@ -0,0 +1,104 @@
+/* GSM BSC Radio Link Layer API
+ * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <errno.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/talloc.h>
+#include <openbsc/timer.h>
+#include <openbsc/linuxlist.h>
+#include <openbsc/bsc_rll.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/chan_alloc.h>
+#include <openbsc/abis_rsl.h>
+
+struct bsc_rll_req {
+	struct llist_head list;
+	struct timer_list timer;
+
+	struct gsm_lchan *lchan;
+	u_int8_t link_id;
+
+	void (*cb)(struct gsm_lchan *lchan, u_int8_t link_id,
+		   void *data, enum bsc_rllr_ind);
+	void *data;
+};
+
+/* we only compare C1, C2 and SAPI */
+#define LINKID_MASK	0xC7
+
+static LLIST_HEAD(bsc_rll_reqs);
+
+static void complete_rllr(struct bsc_rll_req *rllr, enum bsc_rllr_ind type)
+{
+	llist_del(&rllr->list);
+	put_lchan(rllr->lchan);
+	rllr->cb(rllr->lchan, rllr->link_id, rllr->data, type);
+	talloc_free(rllr);
+}
+
+static void timer_cb(void *_rllr)
+{
+	struct bsc_rll_req *rllr = _rllr;
+
+	complete_rllr(rllr, BSC_RLLR_IND_TIMEOUT);
+}
+
+/* establish a RLL connection with given SAPI / priority */
+int rll_establish(struct gsm_lchan *lchan, u_int8_t link_id,
+		  void (*cb)(struct gsm_lchan *, u_int8_t, void *,
+			     enum bsc_rllr_ind),
+		  void *data)
+{
+	struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req);
+	if (!rllr)
+		return -ENOMEM;
+
+	use_lchan(lchan);
+	rllr->lchan = lchan;
+	rllr->link_id = link_id;
+	rllr->cb = cb;
+	rllr->data = data;
+
+	rllr->timer.cb = &timer_cb;
+	/* start some timer? */
+	bsc_schedule_timer(&rllr->timer, 10, 0);
+
+	/* send the RSL RLL ESTablish REQuest */
+	return rsl_establish_request(rllr->lchan, rllr->link_id);
+}
+
+/* Called from RSL code in case we have received an indication regarding
+ * any RLL link */
+void rll_indication(struct gsm_lchan *lchan, u_int8_t link_id, u_int8_t type)
+{
+	struct bsc_rll_req *rllr, *rllr2;
+
+	llist_for_each_entry_safe(rllr, rllr2, &bsc_rll_reqs, list) {
+		if (rllr->lchan == lchan &&
+		    (rllr->link_id & LINKID_MASK) == (link_id & LINKID_MASK)) {
+			complete_rllr(rllr, type);
+			return;
+		}
+	}
+}