changeset 141:e499e8db8b82

sip-in: handle call hold and retrieve
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 08 Oct 2022 13:28:30 -0800
parents 01fe81914bd6
children bb9a75557f59
files sip-in/call.h sip-in/disconnect.c sip-in/mgw_ops.c sip-in/mncc_handle.c
diffstat 4 files changed, 185 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/sip-in/call.h	Sat Oct 08 11:48:26 2022 -0800
+++ b/sip-in/call.h	Sat Oct 08 13:28:30 2022 -0800
@@ -78,3 +78,6 @@
 #define	MGW_STATE_COMPLETE		3
 #define	MGW_STATE_DELETING		4
 #define	MGW_STATE_DTMF_OP		5
+#define	MGW_STATE_HELD			6
+#define	MGW_STATE_HOLD_OP		7
+#define	MGW_STATE_RETRIEVE_OP		8
--- a/sip-in/disconnect.c	Sat Oct 08 11:48:26 2022 -0800
+++ b/sip-in/disconnect.c	Sat Oct 08 13:28:30 2022 -0800
@@ -45,9 +45,12 @@
 	case MGW_STATE_CONNECTING:
 	case MGW_STATE_DELETING:
 	case MGW_STATE_DTMF_OP:
+	case MGW_STATE_HOLD_OP:
+	case MGW_STATE_RETRIEVE_OP:
 		return;
 	case MGW_STATE_ALLOCATED:
 	case MGW_STATE_COMPLETE:
+	case MGW_STATE_HELD:
 		tmgw_send_dlcx(call);
 		return;
 	default:
--- a/sip-in/mgw_ops.c	Sat Oct 08 11:48:26 2022 -0800
+++ b/sip-in/mgw_ops.c	Sat Oct 08 13:28:30 2022 -0800
@@ -14,6 +14,7 @@
 #include <strings.h>
 #include <syslog.h>
 #include "../include/gsm48_const.h"
+#include "../include/mncc.h"
 #include "../include/tmgw_ctrl.h"
 #include "../include/tmgw_const.h"
 #include "call.h"
@@ -87,6 +88,42 @@
 }
 
 void
+tmgw_send_mdcx_hold(call)
+	struct call *call;
+{
+	struct tmgw_ctrl_req req;
+
+	bzero(&req, sizeof req);
+	req.opcode = TMGW_CTRL_OP_MDCX;
+	req.transact_ref = get_new_tmgw_xact_id();
+	req.ep_id = call->mgw_ep_id;
+	req.setup_mask = TMGW_CTRL_MASK_FWD_MODE;
+	req.fwd_mode = TMGW_FWD_MODE_INACTIVE;
+	send_req_to_tmgw(&req);
+	call->mgw_state = MGW_STATE_HOLD_OP;
+	call->mgw_xact = TMGW_CTRL_OP_MDCX;
+	call->mgw_xact_id = req.transact_ref;
+}
+
+void
+tmgw_send_mdcx_retrieve(call)
+	struct call *call;
+{
+	struct tmgw_ctrl_req req;
+
+	bzero(&req, sizeof req);
+	req.opcode = TMGW_CTRL_OP_MDCX;
+	req.transact_ref = get_new_tmgw_xact_id();
+	req.ep_id = call->mgw_ep_id;
+	req.setup_mask = TMGW_CTRL_MASK_FWD_MODE;
+	req.fwd_mode = TMGW_FWD_MODE_SENDRECV;
+	send_req_to_tmgw(&req);
+	call->mgw_state = MGW_STATE_RETRIEVE_OP;
+	call->mgw_xact = TMGW_CTRL_OP_MDCX;
+	call->mgw_xact_id = req.transact_ref;
+}
+
+void
 tmgw_send_dlcx(call)
 	struct call *call;
 {
@@ -186,7 +223,7 @@
 }
 
 static void
-handle_mdcx_fail(call, msg)
+handle_mdcx_connect_fail(call, msg)
 	struct call *call;
 	struct tmgw_ctrl_resp *msg;
 {
@@ -212,7 +249,7 @@
 }
 
 static void
-mdcx_response(call, msg)
+mdcx_connect_response(call, msg)
 	struct call *call;
 	struct tmgw_ctrl_resp *msg;
 {
@@ -236,7 +273,7 @@
 		tmgw_send_dlcx(call);
 		switch (call->overall_state) {
 		case OVERALL_STATE_ANSWERED:
-			handle_mdcx_fail(call, msg);
+			handle_mdcx_connect_fail(call, msg);
 			return;
 		case OVERALL_STATE_TEARDOWN:
 			return;
@@ -246,6 +283,77 @@
 	}
 }
 
+static struct gsm_mncc_cause mgw_hold_retrieve_error = {
+	.coding		= GSM48_CAUSE_CODING_GSM,
+	.location	= GSM48_CAUSE_LOC_PRN_S_LU,
+	.value		= GSM48_CC_CAUSE_NETWORK_OOO,
+};
+
+static void
+mdcx_hold_response(call, msg)
+	struct call *call;
+	struct tmgw_ctrl_resp *msg;
+{
+	if (call->overall_state == OVERALL_STATE_TEARDOWN) {
+		tmgw_send_dlcx(call);
+		return;
+	}
+	if (msg->res == TMGW_RESP_OK) {
+		call->mgw_state = MGW_STATE_HELD;
+		mncc_send_hold_ack(call);
+	} else {
+		call->overall_state = OVERALL_STATE_TEARDOWN;
+		tmgw_send_dlcx(call);
+		disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU,
+				GSM48_CC_CAUSE_NETWORK_OOO);
+		disconnect_sip(call, &mgw_hold_retrieve_error);
+	}
+}
+
+static void
+mdcx_retrieve_response(call, msg)
+	struct call *call;
+	struct tmgw_ctrl_resp *msg;
+{
+	if (call->overall_state == OVERALL_STATE_TEARDOWN) {
+		tmgw_send_dlcx(call);
+		return;
+	}
+	if (msg->res == TMGW_RESP_OK) {
+		call->mgw_state = MGW_STATE_COMPLETE;
+		mncc_send_retrieve_ack(call);
+	} else {
+		call->overall_state = OVERALL_STATE_TEARDOWN;
+		tmgw_send_dlcx(call);
+		disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU,
+				GSM48_CC_CAUSE_NETWORK_OOO);
+		disconnect_sip(call, &mgw_hold_retrieve_error);
+	}
+}
+
+static void
+mdcx_response(call, msg)
+	struct call *call;
+	struct tmgw_ctrl_resp *msg;
+{
+	switch (call->mgw_state) {
+	case MGW_STATE_CONNECTING:
+		mdcx_connect_response(call, msg);
+		return;
+	case MGW_STATE_HOLD_OP:
+		mdcx_hold_response(call, msg);
+		return;
+	case MGW_STATE_RETRIEVE_OP:
+		mdcx_retrieve_response(call, msg);
+		return;
+	default:
+		syslog(LOG_CRIT,
+			"FATAL: invalid MGW state 0x%x on MDCX response",
+			call->mgw_state);
+		exit(1);
+	}
+}
+
 static void
 dlcx_response(call, msg)
 	struct call *call;
--- a/sip-in/mncc_handle.c	Sat Oct 08 11:48:26 2022 -0800
+++ b/sip-in/mncc_handle.c	Sat Oct 08 13:28:30 2022 -0800
@@ -79,6 +79,30 @@
 	send_mncc_to_gsm(&msg, sizeof(struct gsm_mncc));
 }
 
+void
+mncc_send_hold_ack(call)
+	struct call *call;
+{
+	struct gsm_mncc msg;
+
+	bzero(&msg, sizeof(struct gsm_mncc));
+	msg.msg_type = MNCC_HOLD_CNF;
+	msg.callref = call->mncc_callref;
+	send_mncc_to_gsm(&msg, sizeof(struct gsm_mncc));
+}
+
+void
+mncc_send_retrieve_ack(call)
+	struct call *call;
+{
+	struct gsm_mncc msg;
+
+	bzero(&msg, sizeof(struct gsm_mncc));
+	msg.msg_type = MNCC_RETRIEVE_CNF;
+	msg.callref = call->mncc_callref;
+	send_mncc_to_gsm(&msg, sizeof(struct gsm_mncc));
+}
+
 static void
 send_rtp_connect(call)
 	struct call *call;
@@ -205,11 +229,50 @@
 	struct call *call;
 	struct gsm_mncc *msg;
 {
-	if (call->overall_state == OVERALL_STATE_CONNECTED &&
-	    call->mgw_state == MGW_STATE_COMPLETE)
+	if (call->overall_state != OVERALL_STATE_CONNECTED)
+		return;
+	if (call->mgw_state == MGW_STATE_COMPLETE)
 		tmgw_send_dtmf_stop(call);
-	else
+	else if (call->mgw_state == MGW_STATE_DTMF_OP)
 		call->dtmf_pending_stop = 1;
+	else {
+		/* dummy OK response */
+		msg->msg_type = MNCC_STOP_DTMF_RSP;
+		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+	}
+}
+
+static void
+handle_call_hold(call, msg)
+	struct call *call;
+	struct gsm_mncc *msg;
+{
+	if (call->overall_state != OVERALL_STATE_CONNECTED ||
+	    call->mgw_state != MGW_STATE_COMPLETE) {
+		msg->msg_type = MNCC_HOLD_REJ;
+		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
+				GSM48_CC_CAUSE_MSGTYPE_INCOMPAT);
+		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+		return;
+	}
+	tmgw_send_mdcx_hold(call);
+}
+
+static void
+handle_call_retrieve(call, msg)
+	struct call *call;
+	struct gsm_mncc *msg;
+{
+	if (call->overall_state != OVERALL_STATE_CONNECTED ||
+	    call->mgw_state != MGW_STATE_HELD) {
+		msg->msg_type = MNCC_RETRIEVE_REJ;
+		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
+				GSM48_CC_CAUSE_MSGTYPE_INCOMPAT);
+		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+		return;
+	}
+	send_rtp_connect(call);
+	tmgw_send_mdcx_retrieve(call);
 }
 
 static void
@@ -260,16 +323,10 @@
 		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));
+		handle_call_hold(call, msg);
 		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));
+		handle_call_retrieve(call, msg);
 		return;
 	}
 }