* add more GSM 04.08 IEIs
* implement function for CHANNEL MODE MODIFY
* don't use hard-coded SETUP message but construct it with tlv functions

diff --git a/include/openbsc/gsm_04_08.h b/include/openbsc/gsm_04_08.h
index cc93ff9..5fa92fa 100644
--- a/include/openbsc/gsm_04_08.h
+++ b/include/openbsc/gsm_04_08.h
@@ -34,6 +34,12 @@
 		 t3_low:3;
 } __attribute__ ((packed));
 
+/* Chapter 9.1.5 */
+struct gsm48_chan_mode_modify {
+	struct gsm48_chan_desc chan_desc;
+	u_int8_t mode;
+} __attribute__ ((packed));
+
 /* Chapter 9.1.18 */
 struct gsm48_imm_ass {
 	u_int8_t l2_plen;
@@ -360,9 +366,50 @@
 #define GSM48_IE_NET_TIME_TZ	0x47	/* 10.5.3.9 */
 #define GSM48_IE_LSA_IDENT	0x48	/* 10.5.3.11 */
 
+#define GSM48_IE_BEARER_CAP	0x04	/* 10.5.4.5 */
+#define GSM48_IE_CAUSE		0x08	/* 10.5.4.11 */
+#define GSM48_IE_CC_CAP		0x15	/* 10.5.4.5a */
+#define GSM48_IE_ALERT		0x19	/* 10.5.4.26 */
+#define GSM48_IE_FACILITY	0x1c	/* 10.5.4.15 */
+#define GSM48_IE_PROGR_IND	0x1e	/* 10.5.4.21 */
+#define GSM48_IE_AUX_STATUS	0x24	/* 10.5.4.4 */
+#define GSM48_IE_KPD_FACILITY	0x2c	/* 10.5.4.17 */
+#define GSM48_IE_SIGNAL		0x34	/* 10.5.4.23 */
+#define GSM48_IE_CONN_NUM	0x4c	/* 10.5.4.13 */
+#define GSM48_IE_CONN_SUBADDR	0x4d	/* 10.5.4.14 */
+#define GSM48_IE_CALLING_BCD	0x5c	/* 10.5.4.9 */
+#define GSM48_IE_CALLING_SUB	0x5d	/* 10.5.4.10 */
+#define GSM48_IE_CALLED_BCD	0x5e	/* 10.5.4.7 */
+#define GSM48_IE_CALLED_SUB	0x6d	/* 10.5.4.8 */
+#define GSM48_IE_REDIR_BCD	0x74	/* 10.5.4.21a */
+#define GSM48_IE_REDIR_SUB	0x75	/* 10.5.4.21b */
+#define GSM48_IE_LOWL_COMPAT	0x7c	/* 10.5.4.18 */
+#define GSM48_IE_HIGHL_COMPAT	0x7d	/* 10.5.4.16 */
+#define GSM48_IE_USER_USER	0x7e	/* 10.5.4.25 */
+#define GSM48_IE_SS_VERS	0x7f	/* 10.5.4.24 */
+#define GSM48_IE_MORE_DATA	0xa0	/* 10.5.4.19 */
+#define GSM48_IE_CLIR_SUPP	0xa1	/* 10.5.4.11a */
+#define GSM48_IE_CLIR_INVOC	0xa2	/* 10.5.4.11b */
+#define GSM48_IE_REV_C_SETUP	0xa3	/* 10.5.4.22a */
+
 /* Section 10.5.4.11 / Table 10.5.122 */
 #define GSM48_CAUSE_CS_GSM	0x60
 
+/* Section 10.5.4.23 / Table 10.5.130 */
+enum gsm48_signal_val {
+	GSM48_SIGNAL_DIALTONE	= 0x00,
+	GSM48_SIGNAL_RINGBACK	= 0x01,
+	GSM48_SIGNAL_INTERCEPT	= 0x02,
+	GSM48_SIGNAL_NET_CONG	= 0x03,
+	GSM48_SIGNAL_BUSY	= 0x04,
+	GSM48_SIGNAL_CONFIRM	= 0x05,
+	GSM48_SIGNAL_ANSWER	= 0x06,
+	GSM48_SIGNAL_CALL_WAIT	= 0x07,
+	GSM48_SIGNAL_OFF_HOOK	= 0x08,
+	GSM48_SIGNAL_OFF	= 0x3f,
+	GSM48_SIGNAL_ALERT_OFF	= 0x4f,
+};
+
 enum gsm48_cause_loc {
 	GSM48_CAUSE_LOC_USER		= 0x00,
 	GSM48_CAUSE_LOC_PRN_S_LU	= 0x01,
diff --git a/src/gsm_04_08.c b/src/gsm_04_08.c
index 602fbe2..7e80e29 100644
--- a/src/gsm_04_08.c
+++ b/src/gsm_04_08.c
@@ -32,6 +32,7 @@
 
 #include <openbsc/db.h>
 #include <openbsc/msgb.h>
+#include <openbsc/tlv.h>
 #include <openbsc/debug.h>
 #include <openbsc/gsm_data.h>
 #include <openbsc/gsm_subscriber.h>
@@ -453,6 +454,30 @@
 	return gsm0408_loc_upd_acc(lchan, tmsi);
 }
 
+/* 9.1.5 Channel mode modify */
+int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode)
+{
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+	struct gsm48_chan_mode_modify *cmm =
+		(struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm));
+	u_int16_t arfcn = lchan->ts->trx->arfcn;
+
+	msg->lchan = lchan;
+	gh->proto_discr = GSM48_PDISC_RR;
+	gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF;
+
+	/* fill the channel information element, this code
+	 * should probably be shared with rsl_rx_chan_rqd() */
+	cmm->chan_desc.chan_nr = lchan2chan_nr(lchan);
+	cmm->chan_desc.h0.h = 0;
+	cmm->chan_desc.h0.arfcn_high = arfcn >> 8;
+	cmm->chan_desc.h0.arfcn_low = arfcn & 0xff;
+	cmm->mode = mode;
+
+	return gsm48_sendmsg(msg);
+}
+
 /* Section 9.2.15a */
 int gsm48_tx_mm_info(struct gsm_lchan *lchan)
 {
@@ -682,6 +707,9 @@
 		subscr_put(subscr);
 	}
 
+	/* FIXME: somehow signal the completion of the PAGING to
+	 * the entity that requested the paging */
+
 	return rc;
 }
 
@@ -733,6 +761,11 @@
 
 /* Call Control */
 
+/* The entire call control code is written in accordance with Figure 7.10c
+ * for 'very early assignment', i.e. we allocate a TCH/F during IMMEDIATE
+ * ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY
+ * it for voice */
+
 static int gsm48_cc_tx_status(struct gsm_lchan *lchan)
 {
 	struct msgb *msg = gsm48_msgb_alloc();
@@ -775,13 +808,45 @@
 	return gsm48_cc_tx_status(msg->lchan);
 }
 
-#if 0
 static int gsm48_cc_rx_setup(struct msgb *msg)
 {
-	return gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
-			       GSM48_MT_CC_CALL_CONF);
+	struct gsm_call *call = &msg->lchan->call;
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	struct gsm_subscriber *called_subscr;
+	int ret;
+
+	if (call->state == GSM_CSTATE_NULL ||
+	    call->state == GSM_CSTATE_RELEASE_REQ)
+		use_lchan(msg->lchan);
+
+	call->type = GSM_CT_MO;
+	call->state = GSM_CSTATE_INITIATED;
+	call->transaction_id = gh->proto_discr & 0xf0;
+
+	DEBUGP(DCC, "SETUP(tid=0x%02x)\n", call->transaction_id);
+
+	/* Parse the number that was dialed and lookup subscriber */
+	called_subscr = NULL;
+
+	if (!called_subscr) {
+		DEBUGP(DCC, "could not find subscriber, RELEASE\n");
+		put_lchan(msg->lchan);
+		return gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
+					GSM48_MT_CC_RELEASE_COMPL);
+	}
+
+	/* start paging of the receiving end of the call */
+	paging_request(msg->trx->bts, called_subscr, RSL_CHANNEED_TCH_F);
+
+	/* send a CALL PROCEEDING message to the MO */
+	ret = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
+			       GSM48_MT_CC_CALL_PROC);
+
+	/* change TCH/F mode to voice */ 
+	return gsm48_tx_chan_mode_modify(msg->lchan, 0x01);
 }
-#endif
+
+static const u_int8_t calling_bcd[] = { 0xb9, 0x83, 0x32, 0x24 };
 
 int gsm48_cc_tx_setup(struct gsm_lchan *lchan)
 {
@@ -789,7 +854,8 @@
 	struct gsm48_hdr *gh;
 	struct gsm_call *call = &lchan->call;
 
-	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 8);
+	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 4 +
+						sizeof(calling_bcd));
 
 	call->type = GSM_CT_MT;
 	msg->lchan = lchan;
@@ -797,14 +863,9 @@
 
 	gh->proto_discr = GSM48_PDISC_CC;
 	gh->msg_type = GSM48_MT_CC_SETUP;
-	gh->data[0] = 0x34;
-	gh->data[1] = 0x00;
-	gh->data[2] = 0x5c;
-	gh->data[3] = 0x04;
-	gh->data[4] = 0xb9;
-	gh->data[5] = 0x83;
-	gh->data[6] = 0x32;
-	gh->data[7] = 0x24;
+	msgb_tv_put(msg, GSM48_IE_SIGNAL, GSM48_SIGNAL_DIALTONE);
+	msgb_tlv_put(msg, GSM48_IE_CALLING_BCD,
+		     sizeof(calling_bcd), calling_bcd);
 
 	DEBUGP(DCC, "Sending SETUP\n");
 
@@ -860,15 +921,7 @@
 				     GSM48_MT_CC_RELEASE);
 		break;
 	case GSM48_MT_CC_SETUP:
-		if (call->state == GSM_CSTATE_NULL || call->state == GSM_CSTATE_RELEASE_REQ)
-			use_lchan(msg->lchan);
-		call->type = GSM_CT_MO;
-		call->state = GSM_CSTATE_INITIATED;
-		call->transaction_id = gh->proto_discr & 0xf0;
-		DEBUGP(DCC, "SETUP(tid=0x%02x)\n", call->transaction_id);
-		rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
-				     GSM48_MT_CC_CONNECT);
-		/* FIXME: continue with CALL_PROCEEDING, ALERTING, CONNECT, RELEASE_COMPLETE */
+		rc = gsm48_cc_rx_setup(msg);
 		break;
 	case GSM48_MT_CC_EMERG_SETUP:
 		DEBUGP(DCC, "EMERGENCY SETUP\n");