aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2010-12-22 23:54:51 +0100
committerHarald Welte <laforge@gnumonks.org>2010-12-26 10:01:40 +0100
commit461f9b97b639c3b4d2ce36c8f65bcd7796bdb5fe (patch)
treeaa392550a64961674eed3d9821507515a194e652
parentd235e2ebc4c1b5b208c8b5afad11e25246e2b645 (diff)
MNCC: split into generic mncc.c and mncc_builtin.c
The built-in code to handle call switching inside OpenBSC is now in mncc_builtin.c, whereas some core/utility functions remain in mncc.c
-rw-r--r--openbsc/src/Makefile.am2
-rw-r--r--openbsc/src/mncc.c366
-rw-r--r--openbsc/src/mncc_builtin.c400
3 files changed, 403 insertions, 365 deletions
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index 4f3bf9e31..029f33838 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -26,7 +26,7 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
bts_unknown.c bsc_version.c bsc_api.c bsc_vty.c meas_rep.c gsm_04_80.c
libmsc_a_SOURCES = gsm_subscriber.c db.c \
- mncc.c gsm_04_08.c gsm_04_11.c transaction.c \
+ mncc.c mncc_builtin.c gsm_04_08.c gsm_04_11.c transaction.c \
token_auth.c rrlp.c ussd.c silent_call.c \
handover_decision.c auth.c \
osmo_msc.c
diff --git a/openbsc/src/mncc.c b/openbsc/src/mncc.c
index 762e7f191..478a8a956 100644
--- a/openbsc/src/mncc.c
+++ b/openbsc/src/mncc.c
@@ -1,5 +1,5 @@
-/* mncc.c - default, minimal built-in MNCC Application for
- * standalone bsc_hack (netowrk-in-the-box mode) */
+/* mncc.c - utility routines for the MNCC API between the 04.08
+ * message parsing and the actual Call Control logic */
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
* (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de>
@@ -36,8 +36,6 @@
#include <openbsc/transaction.h>
#include <openbsc/rtp_proxy.h>
-void *tall_call_ctx;
-
static struct mncc_names {
char *name;
int value;
@@ -92,10 +90,6 @@ static struct mncc_names {
{NULL, 0} };
-static LLIST_HEAD(call_list);
-
-static u_int32_t new_callref = 0x00000001;
-
char *get_mncc_name(int value)
{
int i;
@@ -108,365 +102,9 @@ char *get_mncc_name(int value)
return "MNCC_Unknown";
}
-static void free_call(struct gsm_call *call)
-{
- llist_del(&call->entry);
- DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref);
- talloc_free(call);
-}
-
-
-static struct gsm_call *get_call_ref(u_int32_t callref)
-{
- struct gsm_call *callt;
-
- llist_for_each_entry(callt, &call_list, entry) {
- if (callt->callref == callref)
- return callt;
- }
- return NULL;
-}
-
void mncc_set_cause(struct gsm_mncc *data, int loc, int val)
{
data->fields |= MNCC_F_CAUSE;
data->cause.location = loc;
data->cause.value = val;
}
-
-/* on incoming call, look up database and send setup to remote subscr. */
-static int mncc_setup_ind(struct gsm_call *call, int msg_type,
- struct gsm_mncc *setup)
-{
- struct gsm_mncc mncc;
- struct gsm_call *remote;
-
- memset(&mncc, 0, sizeof(struct gsm_mncc));
- mncc.callref = call->callref;
-
- /* already have remote call */
- if (call->remote_ref)
- return 0;
-
- /* transfer mode 1 would be packet mode, which was never specified */
- if (setup->bearer_cap.mode != 0) {
- LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support "
- "packet mode\n", call->callref);
- mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
- goto out_reject;
- }
-
- /* we currently only do speech */
- if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) {
- LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support "
- "voice calls\n", call->callref);
- mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
- goto out_reject;
- }
-
- /* create remote call */
- if (!(remote = talloc(tall_call_ctx, struct gsm_call))) {
- mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
- goto out_reject;
- }
- llist_add_tail(&remote->entry, &call_list);
- remote->net = call->net;
- remote->callref = new_callref++;
- DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n",
- call->callref, remote->callref);
-
- /* link remote call */
- call->remote_ref = remote->callref;
- remote->remote_ref = call->callref;
-
- /* modify mode */
- memset(&mncc, 0, sizeof(struct gsm_mncc));
- mncc.callref = call->callref;
- mncc.lchan_mode = GSM48_CMODE_SPEECH_EFR;
- DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref);
- mncc_send(call->net, MNCC_LCHAN_MODIFY, &mncc);
-
- /* send call proceeding */
- memset(&mncc, 0, sizeof(struct gsm_mncc));
- mncc.callref = call->callref;
- DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref);
- mncc_send(call->net, MNCC_CALL_PROC_REQ, &mncc);
-
- /* send setup to remote */
-// setup->fields |= MNCC_F_SIGNAL;
-// setup->signal = GSM48_SIGNAL_DIALTONE;
- setup->callref = remote->callref;
- DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref);
- return mncc_send(remote->net, MNCC_SETUP_REQ, setup);
-
-out_reject:
- mncc_send(call->net, MNCC_REJ_REQ, &mncc);
- free_call(call);
- return 0;
-}
-
-static int mncc_alert_ind(struct gsm_call *call, int msg_type,
- struct gsm_mncc *alert)
-{
- struct gsm_call *remote;
-
- /* send alerting to remote */
- if (!(remote = get_call_ref(call->remote_ref)))
- return 0;
- alert->callref = remote->callref;
- DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref);
- return mncc_send(remote->net, MNCC_ALERT_REQ, alert);
-}
-
-static int mncc_notify_ind(struct gsm_call *call, int msg_type,
- struct gsm_mncc *notify)
-{
- struct gsm_call *remote;
-
- /* send notify to remote */
- if (!(remote = get_call_ref(call->remote_ref)))
- return 0;
- notify->callref = remote->callref;
- DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref);
- return mncc_send(remote->net, MNCC_NOTIFY_REQ, notify);
-}
-
-static int mncc_setup_cnf(struct gsm_call *call, int msg_type,
- struct gsm_mncc *connect)
-{
- struct gsm_mncc connect_ack, frame_recv;
- struct gsm_network *net = call->net;
- struct gsm_call *remote;
- u_int32_t refs[2];
-
- /* acknowledge connect */
- memset(&connect_ack, 0, sizeof(struct gsm_mncc));
- connect_ack.callref = call->callref;
- DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref);
- mncc_send(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack);
-
- /* send connect message to remote */
- if (!(remote = get_call_ref(call->remote_ref)))
- return 0;
- connect->callref = remote->callref;
- DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref);
- mncc_send(remote->net, MNCC_SETUP_RSP, connect);
-
- /* bridge tch */
- refs[0] = call->callref;
- refs[1] = call->remote_ref;
- DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref);
-
- /* in direct mode, we always have to bridge the channels */
- if (ipacc_rtp_direct)
- return mncc_send(call->net, MNCC_BRIDGE, refs);
-
- /* proxy mode */
- if (!net->handover.active) {
- /* in the no-handover case, we can bridge, i.e. use
- * the old RTP proxy code */
- return mncc_send(call->net, MNCC_BRIDGE, refs);
- } else {
- /* in case of handover, we need to re-write the RTP
- * SSRC, sequence and timestamp values and thus
- * need to enable RTP receive for both directions */
- memset(&frame_recv, 0, sizeof(struct gsm_mncc));
- frame_recv.callref = call->callref;
- mncc_send(call->net, MNCC_FRAME_RECV, &frame_recv);
- frame_recv.callref = call->remote_ref;
- return mncc_send(call->net, MNCC_FRAME_RECV, &frame_recv);
- }
-}
-
-static int mncc_disc_ind(struct gsm_call *call, int msg_type,
- struct gsm_mncc *disc)
-{
- struct gsm_call *remote;
-
- /* send release */
- DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n",
- call->callref, disc->cause.value);
- mncc_send(call->net, MNCC_REL_REQ, disc);
-
- /* send disc to remote */
- if (!(remote = get_call_ref(call->remote_ref))) {
- return 0;
- }
- disc->callref = remote->callref;
- DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n",
- remote->callref, disc->cause.value);
- return mncc_send(remote->net, MNCC_DISC_REQ, disc);
-}
-
-static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
-{
- struct gsm_call *remote;
-
- /* send release to remote */
- if (!(remote = get_call_ref(call->remote_ref))) {
- free_call(call);
- return 0;
- }
- rel->callref = remote->callref;
- DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n",
- call->callref, rel->cause.value);
- mncc_send(remote->net, MNCC_REL_REQ, rel);
-
- free_call(call);
-
- return 0;
-}
-
-static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
-{
- free_call(call);
- return 0;
-}
-
-/* receiving a TCH/F frame from the BSC code */
-static int mncc_rcv_tchf(struct gsm_call *call, int msg_type,
- struct gsm_data_frame *dfr)
-{
- struct gsm_trans *remote_trans;
-
- remote_trans = trans_find_by_callref(call->net, call->remote_ref);
-
- /* this shouldn't really happen */
- if (!remote_trans || !remote_trans->conn) {
- LOGP(DMNCC, LOGL_ERROR, "No transaction or transaction without lchan?!?\n");
- return -EIO;
- }
-
- /* RTP socket of remote end has meanwhile died */
- if (!remote_trans->conn->lchan->abis_ip.rtp_socket)
- return -EIO;
-
- return rtp_send_frame(remote_trans->conn->lchan->abis_ip.rtp_socket, dfr);
-}
-
-
-/* Internal MNCC handler input function (from CC -> MNCC -> here) */
-int int_mncc_recv(struct gsm_network *net, int msg_type, void *arg)
-{
- struct gsm_mncc *data = arg;
- int callref;
- struct gsm_call *call = NULL, *callt;
- int rc = 0;
-
- /* Special messages */
- switch(msg_type) {
- }
-
- /* find callref */
- callref = data->callref;
- llist_for_each_entry(callt, &call_list, entry) {
- if (callt->callref == callref) {
- call = callt;
- break;
- }
- }
-
- /* create callref, if setup is received */
- if (!call) {
- if (msg_type != MNCC_SETUP_IND)
- return 0; /* drop */
- /* create call */
- if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) {
- struct gsm_mncc rel;
-
- memset(&rel, 0, sizeof(struct gsm_mncc));
- rel.callref = callref;
- mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
- mncc_send(net, MNCC_REL_REQ, &rel);
- return 0;
- }
- llist_add_tail(&call->entry, &call_list);
- call->net = net;
- call->callref = callref;
- DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
- }
-
- switch (msg_type) {
- case GSM_TCHF_FRAME:
- case GSM_TCHF_FRAME_EFR:
- break;
- default:
- DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
- get_mncc_name(msg_type));
- break;
- }
-
- switch(msg_type) {
- case MNCC_SETUP_IND:
- rc = mncc_setup_ind(call, msg_type, arg);
- break;
- case MNCC_SETUP_CNF:
- rc = mncc_setup_cnf(call, msg_type, arg);
- break;
- case MNCC_SETUP_COMPL_IND:
- break;
- case MNCC_CALL_CONF_IND:
- /* we now need to MODIFY the channel */
- data->lchan_mode = GSM48_CMODE_SPEECH_EFR;
- mncc_send(call->net, MNCC_LCHAN_MODIFY, data);
- break;
- case MNCC_ALERT_IND:
- rc = mncc_alert_ind(call, msg_type, arg);
- break;
- case MNCC_NOTIFY_IND:
- rc = mncc_notify_ind(call, msg_type, arg);
- break;
- case MNCC_DISC_IND:
- rc = mncc_disc_ind(call, msg_type, arg);
- break;
- case MNCC_REL_IND:
- case MNCC_REJ_IND:
- rc = mncc_rel_ind(call, msg_type, arg);
- break;
- case MNCC_REL_CNF:
- rc = mncc_rel_cnf(call, msg_type, arg);
- break;
- case MNCC_FACILITY_IND:
- break;
- case MNCC_START_DTMF_IND:
- break;
- case MNCC_STOP_DTMF_IND:
- break;
- case MNCC_MODIFY_IND:
- mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
- DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n",
- call->callref, data->cause.value);
- rc = mncc_send(net, MNCC_MODIFY_REJ, data);
- break;
- case MNCC_MODIFY_CNF:
- break;
- case MNCC_HOLD_IND:
- mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
- DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n",
- call->callref, data->cause.value);
- rc = mncc_send(net, MNCC_HOLD_REJ, data);
- break;
- case MNCC_RETRIEVE_IND:
- mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
- DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n",
- call->callref, data->cause.value);
- rc = mncc_send(net, MNCC_RETRIEVE_REJ, data);
- break;
- case GSM_TCHF_FRAME:
- case GSM_TCHF_FRAME_EFR:
- rc = mncc_rcv_tchf(call, msg_type, arg);
- break;
- default:
- LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref);
- break;
- }
-
- return rc;
-}
diff --git a/openbsc/src/mncc_builtin.c b/openbsc/src/mncc_builtin.c
new file mode 100644
index 000000000..87f724cf6
--- /dev/null
+++ b/openbsc/src/mncc_builtin.c
@@ -0,0 +1,400 @@
+/* mncc_builtin.c - default, minimal built-in MNCC Application for
+ * standalone bsc_hack (netowrk-in-the-box mode) */
+
+/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de>
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/debug.h>
+#include <openbsc/mncc.h>
+#include <osmocore/talloc.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/transaction.h>
+#include <openbsc/rtp_proxy.h>
+
+void *tall_call_ctx;
+
+static LLIST_HEAD(call_list);
+
+static u_int32_t new_callref = 0x00000001;
+
+static void free_call(struct gsm_call *call)
+{
+ llist_del(&call->entry);
+ DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref);
+ talloc_free(call);
+}
+
+
+static struct gsm_call *get_call_ref(u_int32_t callref)
+{
+ struct gsm_call *callt;
+
+ llist_for_each_entry(callt, &call_list, entry) {
+ if (callt->callref == callref)
+ return callt;
+ }
+ return NULL;
+}
+
+
+/* on incoming call, look up database and send setup to remote subscr. */
+static int mncc_setup_ind(struct gsm_call *call, int msg_type,
+ struct gsm_mncc *setup)
+{
+ struct gsm_mncc mncc;
+ struct gsm_call *remote;
+
+ memset(&mncc, 0, sizeof(struct gsm_mncc));
+ mncc.callref = call->callref;
+
+ /* already have remote call */
+ if (call->remote_ref)
+ return 0;
+
+ /* transfer mode 1 would be packet mode, which was never specified */
+ if (setup->bearer_cap.mode != 0) {
+ LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support "
+ "packet mode\n", call->callref);
+ mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
+ goto out_reject;
+ }
+
+ /* we currently only do speech */
+ if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) {
+ LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support "
+ "voice calls\n", call->callref);
+ mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
+ goto out_reject;
+ }
+
+ /* create remote call */
+ if (!(remote = talloc(tall_call_ctx, struct gsm_call))) {
+ mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ goto out_reject;
+ }
+ llist_add_tail(&remote->entry, &call_list);
+ remote->net = call->net;
+ remote->callref = new_callref++;
+ DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n",
+ call->callref, remote->callref);
+
+ /* link remote call */
+ call->remote_ref = remote->callref;
+ remote->remote_ref = call->callref;
+
+ /* modify mode */
+ memset(&mncc, 0, sizeof(struct gsm_mncc));
+ mncc.callref = call->callref;
+ mncc.lchan_mode = GSM48_CMODE_SPEECH_EFR;
+ DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref);
+ mncc_send(call->net, MNCC_LCHAN_MODIFY, &mncc);
+
+ /* send call proceeding */
+ memset(&mncc, 0, sizeof(struct gsm_mncc));
+ mncc.callref = call->callref;
+ DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref);
+ mncc_send(call->net, MNCC_CALL_PROC_REQ, &mncc);
+
+ /* send setup to remote */
+// setup->fields |= MNCC_F_SIGNAL;
+// setup->signal = GSM48_SIGNAL_DIALTONE;
+ setup->callref = remote->callref;
+ DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref);
+ return mncc_send(remote->net, MNCC_SETUP_REQ, setup);
+
+out_reject:
+ mncc_send(call->net, MNCC_REJ_REQ, &mncc);
+ free_call(call);
+ return 0;
+}
+
+static int mncc_alert_ind(struct gsm_call *call, int msg_type,
+ struct gsm_mncc *alert)
+{
+ struct gsm_call *remote;
+
+ /* send alerting to remote */
+ if (!(remote = get_call_ref(call->remote_ref)))
+ return 0;
+ alert->callref = remote->callref;
+ DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref);
+ return mncc_send(remote->net, MNCC_ALERT_REQ, alert);
+}
+
+static int mncc_notify_ind(struct gsm_call *call, int msg_type,
+ struct gsm_mncc *notify)
+{
+ struct gsm_call *remote;
+
+ /* send notify to remote */
+ if (!(remote = get_call_ref(call->remote_ref)))
+ return 0;
+ notify->callref = remote->callref;
+ DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref);
+ return mncc_send(remote->net, MNCC_NOTIFY_REQ, notify);
+}
+
+static int mncc_setup_cnf(struct gsm_call *call, int msg_type,
+ struct gsm_mncc *connect)
+{
+ struct gsm_mncc connect_ack, frame_recv;
+ struct gsm_network *net = call->net;
+ struct gsm_call *remote;
+ u_int32_t refs[2];
+
+ /* acknowledge connect */
+ memset(&connect_ack, 0, sizeof(struct gsm_mncc));
+ connect_ack.callref = call->callref;
+ DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref);
+ mncc_send(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack);
+
+ /* send connect message to remote */
+ if (!(remote = get_call_ref(call->remote_ref)))
+ return 0;
+ connect->callref = remote->callref;
+ DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref);
+ mncc_send(remote->net, MNCC_SETUP_RSP, connect);
+
+ /* bridge tch */
+ refs[0] = call->callref;
+ refs[1] = call->remote_ref;
+ DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref);
+
+ /* in direct mode, we always have to bridge the channels */
+ if (ipacc_rtp_direct)
+ return mncc_send(call->net, MNCC_BRIDGE, refs);
+
+ /* proxy mode */
+ if (!net->handover.active) {
+ /* in the no-handover case, we can bridge, i.e. use
+ * the old RTP proxy code */
+ return mncc_send(call->net, MNCC_BRIDGE, refs);
+ } else {
+ /* in case of handover, we need to re-write the RTP
+ * SSRC, sequence and timestamp values and thus
+ * need to enable RTP receive for both directions */
+ memset(&frame_recv, 0, sizeof(struct gsm_mncc));
+ frame_recv.callref = call->callref;
+ mncc_send(call->net, MNCC_FRAME_RECV, &frame_recv);
+ frame_recv.callref = call->remote_ref;
+ return mncc_send(call->net, MNCC_FRAME_RECV, &frame_recv);
+ }
+}
+
+static int mncc_disc_ind(struct gsm_call *call, int msg_type,
+ struct gsm_mncc *disc)
+{
+ struct gsm_call *remote;
+
+ /* send release */
+ DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n",
+ call->callref, disc->cause.value);
+ mncc_send(call->net, MNCC_REL_REQ, disc);
+
+ /* send disc to remote */
+ if (!(remote = get_call_ref(call->remote_ref))) {
+ return 0;
+ }
+ disc->callref = remote->callref;
+ DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n",
+ remote->callref, disc->cause.value);
+ return mncc_send(remote->net, MNCC_DISC_REQ, disc);
+}
+
+static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
+{
+ struct gsm_call *remote;
+
+ /* send release to remote */
+ if (!(remote = get_call_ref(call->remote_ref))) {
+ free_call(call);
+ return 0;
+ }
+ rel->callref = remote->callref;
+ DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n",
+ call->callref, rel->cause.value);
+ mncc_send(remote->net, MNCC_REL_REQ, rel);
+
+ free_call(call);
+
+ return 0;
+}
+
+static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
+{
+ free_call(call);
+ return 0;
+}
+
+/* receiving a TCH/F frame from the BSC code */
+static int mncc_rcv_tchf(struct gsm_call *call, int msg_type,
+ struct gsm_data_frame *dfr)
+{
+ struct gsm_trans *remote_trans;
+
+ remote_trans = trans_find_by_callref(call->net, call->remote_ref);
+
+ /* this shouldn't really happen */
+ if (!remote_trans || !remote_trans->conn) {
+ LOGP(DMNCC, LOGL_ERROR, "No transaction or transaction without lchan?!?\n");
+ return -EIO;
+ }
+
+ /* RTP socket of remote end has meanwhile died */
+ if (!remote_trans->conn->lchan->abis_ip.rtp_socket)
+ return -EIO;
+
+ return rtp_send_frame(remote_trans->conn->lchan->abis_ip.rtp_socket, dfr);
+}
+
+
+/* Internal MNCC handler input function (from CC -> MNCC -> here) */
+int int_mncc_recv(struct gsm_network *net, int msg_type, void *arg)
+{
+ struct gsm_mncc *data = arg;
+ int callref;
+ struct gsm_call *call = NULL, *callt;
+ int rc = 0;
+
+ /* Special messages */
+ switch(msg_type) {
+ }
+
+ /* find callref */
+ callref = data->callref;
+ llist_for_each_entry(callt, &call_list, entry) {
+ if (callt->callref == callref) {
+ call = callt;
+ break;
+ }
+ }
+
+ /* create callref, if setup is received */
+ if (!call) {
+ if (msg_type != MNCC_SETUP_IND)
+ return 0; /* drop */
+ /* create call */
+ if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) {
+ struct gsm_mncc rel;
+
+ memset(&rel, 0, sizeof(struct gsm_mncc));
+ rel.callref = callref;
+ mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ mncc_send(net, MNCC_REL_REQ, &rel);
+ return 0;
+ }
+ llist_add_tail(&call->entry, &call_list);
+ call->net = net;
+ call->callref = callref;
+ DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
+ }
+
+ switch (msg_type) {
+ case GSM_TCHF_FRAME:
+ case GSM_TCHF_FRAME_EFR:
+ break;
+ default:
+ DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
+ get_mncc_name(msg_type));
+ break;
+ }
+
+ switch(msg_type) {
+ case MNCC_SETUP_IND:
+ rc = mncc_setup_ind(call, msg_type, arg);
+ break;
+ case MNCC_SETUP_CNF:
+ rc = mncc_setup_cnf(call, msg_type, arg);
+ break;
+ case MNCC_SETUP_COMPL_IND:
+ break;
+ case MNCC_CALL_CONF_IND:
+ /* we now need to MODIFY the channel */
+ data->lchan_mode = GSM48_CMODE_SPEECH_EFR;
+ mncc_send(call->net, MNCC_LCHAN_MODIFY, data);
+ break;
+ case MNCC_ALERT_IND:
+ rc = mncc_alert_ind(call, msg_type, arg);
+ break;
+ case MNCC_NOTIFY_IND:
+ rc = mncc_notify_ind(call, msg_type, arg);
+ break;
+ case MNCC_DISC_IND:
+ rc = mncc_disc_ind(call, msg_type, arg);
+ break;
+ case MNCC_REL_IND:
+ case MNCC_REJ_IND:
+ rc = mncc_rel_ind(call, msg_type, arg);
+ break;
+ case MNCC_REL_CNF:
+ rc = mncc_rel_cnf(call, msg_type, arg);
+ break;
+ case MNCC_FACILITY_IND:
+ break;
+ case MNCC_START_DTMF_IND:
+ break;
+ case MNCC_STOP_DTMF_IND:
+ break;
+ case MNCC_MODIFY_IND:
+ mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
+ DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n",
+ call->callref, data->cause.value);
+ rc = mncc_send(net, MNCC_MODIFY_REJ, data);
+ break;
+ case MNCC_MODIFY_CNF:
+ break;
+ case MNCC_HOLD_IND:
+ mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
+ DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n",
+ call->callref, data->cause.value);
+ rc = mncc_send(net, MNCC_HOLD_REJ, data);
+ break;
+ case MNCC_RETRIEVE_IND:
+ mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
+ DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n",
+ call->callref, data->cause.value);
+ rc = mncc_send(net, MNCC_RETRIEVE_REJ, data);
+ break;
+ case GSM_TCHF_FRAME:
+ case GSM_TCHF_FRAME_EFR:
+ rc = mncc_rcv_tchf(call, msg_type, arg);
+ break;
+ default:
+ LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref);
+ break;
+ }
+
+ return rc;
+}