view mncc/intswitch.c @ 101:ca21fbf9d2ea

mgw: implement G.711 encoding
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 26 Sep 2022 15:21:05 -0800
parents 660126bd5f59
children
line wrap: on
line source

/*
 * In this module we implement internally switched calls,
 * going from one GSM subscriber to another.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include "../include/mncc.h"
#include "../include/gsm48_const.h"
#include "struct.h"
#include "gsm_call.h"

extern char *mncc_msg_name();

void
internal_switch_mo_setup(call, msg)
	struct gsm_call *call;
	struct gsm_mncc *msg;
{
	struct gsm_call *mt;
	struct gsm_mncc callproc;

	if (!(msg->fields & MNCC_F_BEARER_CAP)) {
		syslog(LOG_ERR, "rejecting intsw call 0x%x: no bearer cap",
			msg->callref);
		reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_INVAL_MAND_INF);
		call->gc_flag = 1;
		return;
	}
	/* same speech-only restriction as in OsmoMSC's mncc_builtin */
	if (msg->bearer_cap.transfer != GSM48_BCAP_ITCAP_SPEECH ||
	    msg->bearer_cap.mode != GSM48_BCAP_TMOD_CIRCUIT) {
		syslog(LOG_ERR, "rejecting intsw call 0x%x: bad bearer cap",
			msg->callref);
		reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
		call->gc_flag = 1;
		return;
	}
	mt = create_new_mt_call();
	if (!mt) {
		syslog(LOG_ERR,
			"rejecting intsw call 0x%x: no memory for MT call",
			msg->callref);
		reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
		call->gc_flag = 1;
		return;
	}
	syslog(LOG_DEBUG, "intsw: MO=0x%x MT=0x%x", call->callref, mt->callref);
	call->other_leg = mt;
	mt->other_leg = call;
	/* send call proceeding */
	bzero(&callproc, sizeof(struct gsm_mncc));
	callproc.msg_type = MNCC_CALL_PROC_REQ;
	callproc.callref = call->callref;
	send_mncc_to_gsm(&callproc, sizeof(struct gsm_mncc));
	/* turn MNCC_SETUP_IND into MNCC_SETUP_REQ for MT */
	msg->msg_type = MNCC_SETUP_REQ;
	msg->callref = mt->callref;
	msg->imsi[0] = '\0';
	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
}

static void
handle_setup_cnf(call, msg)
	struct gsm_call *call;
	struct gsm_mncc *msg;
{
	struct gsm_mncc ack;
	struct gsm_mncc_bridge bridge;

	/* acknowledge connect */
	bzero(&ack, sizeof(struct gsm_mncc));
	ack.msg_type = MNCC_SETUP_COMPL_REQ;
	ack.callref = call->callref;
	send_mncc_to_gsm(&ack, sizeof(struct gsm_mncc));
	/* do we have the far end? */
	if (!call->other_leg) {
		syslog(LOG_ERR, "intsw: missing other leg for MNCC_SETUP_CNF");
		return;
	}
	syslog(LOG_DEBUG, "MNCC_SETUP_CNF from 0x%x to 0x%x", call->callref,
		call->other_leg->callref);
	msg->msg_type = MNCC_SETUP_RSP;
	msg->callref = call->other_leg->callref;
	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
	/* bridge TCH */
	bzero(&bridge, sizeof(struct gsm_mncc_bridge));
	bridge.msg_type = MNCC_BRIDGE;
	bridge.callref[0] = call->callref;
	bridge.callref[1] = call->other_leg->callref;
	send_mncc_to_gsm(&bridge, sizeof(struct gsm_mncc_bridge));
}

static void
forward_to_remote(call, msg, new_msg_type)
	struct gsm_call *call;
	struct gsm_mncc *msg;
	uint32_t new_msg_type;
{
	if (!call->other_leg) {
		syslog(LOG_ERR, "intsw: missing other leg for msg forwarding");
		/* drop it like OsmoMSC's mncc_builtin does */
		return;
	}
	msg->msg_type = new_msg_type;
	msg->callref = call->other_leg->callref;
	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
}

static void
handle_disconnect(call, msg)
	struct gsm_call *call;
	struct gsm_mncc *msg;
{
	struct gsm_call *remote;

	/* release on near end */
	msg->msg_type = MNCC_REL_REQ;
	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
	/* disconnect on far end */
	remote = call->other_leg;
	if (!remote) {
		syslog(LOG_ERR, "intsw: missing other leg for MNCC_DISC_IND");
		return;
	}
	syslog(LOG_DEBUG, "MNCC_DISC_IND from 0x%x to 0x%x", call->callref,
		remote->callref);
	msg->msg_type = MNCC_DISC_REQ;
	msg->callref = remote->callref;
	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
	/* sever the end-to-end association */
	call->other_leg = 0;
	remote->other_leg = 0;
}

static void
handle_release(call, msg)
	struct gsm_call *call;
	struct gsm_mncc *msg;
{
	struct gsm_call *remote;

	/* do we have a far end? */
	remote = call->other_leg;
	/* free the near end */
	call->gc_flag = 1;
	/* if no remote, nothing more to do */
	if (!remote)
		return;
	syslog(LOG_DEBUG, "release with remote: from 0x%x to 0x%x",
		call->callref, remote->callref);
	/* send them a release request */
	msg->msg_type = MNCC_REL_REQ;
	msg->callref = remote->callref;
	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
	/* sever the end-to-end association */
	remote->other_leg = 0;
}

void
internal_switch_mncc(call, msg)
	struct gsm_call *call;
	struct gsm_mncc *msg;
{
	switch (msg->msg_type) {
	case MNCC_SETUP_CNF:
		handle_setup_cnf(call, msg);
		return;
	case MNCC_ALERT_IND:
		forward_to_remote(call, msg, MNCC_ALERT_REQ);
		return;
	case MNCC_NOTIFY_IND:
		forward_to_remote(call, msg, MNCC_NOTIFY_REQ);
		return;
	case MNCC_USERINFO_IND:
		forward_to_remote(call, msg, MNCC_USERINFO_REQ);
		return;
	case MNCC_DISC_IND:
		handle_disconnect(call, msg);
		return;
	case MNCC_START_DTMF_IND:
		msg->msg_type = MNCC_START_DTMF_REJ;
		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
		return;
	case MNCC_STOP_DTMF_IND:
		msg->msg_type = MNCC_STOP_DTMF_RSP;
		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
		return;
	case MNCC_MODIFY_IND:
		msg->msg_type = MNCC_MODIFY_REJ;
		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
		return;
	case MNCC_HOLD_IND:
		msg->msg_type = MNCC_HOLD_REJ;
		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
		return;
	case MNCC_RETRIEVE_IND:
		msg->msg_type = MNCC_RETRIEVE_REJ;
		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
		return;
	case MNCC_SETUP_COMPL_IND:
	case MNCC_CALL_CONF_IND:
		/* no handling needed */
		return;
	case MNCC_REL_IND:
	case MNCC_REJ_IND:
		handle_release(call, msg);
		return;
	case MNCC_REL_CNF:
		call->gc_flag = 1;
		return;
	default:
		syslog(LOG_ERR, "%s unhandled for internal switch",
			mncc_msg_name(msg->msg_type));
	}
}