FreeCalypso > hg > themwi-system-sw
diff mncc/intswitch.c @ 15:ccc5ab6d8388
first version of themwi-mncc for ThemWi2
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sun, 26 Jun 2022 16:31:47 -0800 |
parents | |
children | 52e801b5ebb1 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/intswitch.c Sun Jun 26 16:31:47 2022 -0800 @@ -0,0 +1,211 @@ +/* + * 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" + +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)) { + 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) { + 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) { + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + call->gc_flag = 1; + return; + } + 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) + return; + 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) { + /* 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) + return; + 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; + /* 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; + 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, + "MNCC message type 0x%x unhandled for internal switch", + msg->msg_type); + } +}