FreeCalypso > hg > themwi-interim
diff mncc/intswitch.c @ 2:053f04687106
mncc: initial import from old ThemWi
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sat, 08 Jun 2024 23:12:12 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/intswitch.c Sat Jun 08 23:12:12 2024 +0000 @@ -0,0 +1,233 @@ +/* + * 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)); + } +}