FreeCalypso > hg > themwi-interim
view mncc/intswitch.c @ 14:44c1d4d787e9 default tip
themwi-test-mtc: add option to set bearer cap
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Thu, 28 Nov 2024 20:37:23 +0000 |
parents | 053f04687106 |
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)); } }