changeset 127:f062c32a5116

mgw: implement DTMF
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 01 Oct 2022 20:31:15 -0800
parents 815e4c59162e
children 5685412bd6aa
files include/tmgw_ctrl.h mgw/Makefile mgw/crcx.c mgw/ctrl_prot.c mgw/ctrl_sock.c mgw/dlcx.c mgw/dtmf_ctrl.c mgw/dtmf_timer.c mgw/gsm2pstn.c mgw/main.c mgw/mdcx.c mgw/pstn2gsm.c mgw/readconf.c mgw/struct.h
diffstat 14 files changed, 298 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/include/tmgw_ctrl.h	Sat Oct 01 17:09:51 2022 -0800
+++ b/include/tmgw_ctrl.h	Sat Oct 01 20:31:15 2022 -0800
@@ -40,3 +40,5 @@
 #define	TMGW_RESP_ERR_OPCODE	3
 #define	TMGW_RESP_ERR_PARAM	4
 #define	TMGW_RESP_ERR_NOTSUP	5
+#define	TMGW_RESP_ERR_BUSY	6
+#define	TMGW_RESP_ERR_NOTRDY	7
--- a/mgw/Makefile	Sat Oct 01 17:09:51 2022 -0800
+++ b/mgw/Makefile	Sat Oct 01 20:31:15 2022 -0800
@@ -1,9 +1,9 @@
 CC=	gcc
 CFLAGS=	-O2
 PROG=	themwi-mgw
-OBJS=	crcx.o ctrl_prot.o ctrl_sock.o dlcx.o dtmf_init.o dtmf_table.o \
-	g711_decode.o g711_encode.o gsm2pstn.o main.o mdcx.o pstn2gsm.o \
-	readconf.o udpsink.o
+OBJS=	crcx.o ctrl_prot.o ctrl_sock.o dlcx.o dtmf_ctrl.o dtmf_init.o \
+	dtmf_table.o dtmf_timer.o g711_decode.o g711_encode.o gsm2pstn.o main.o\
+	mdcx.o pstn2gsm.o readconf.o udpsink.o
 LIBS=	../libutil/libutil.a
 INSTBIN=/usr/local/bin
 
--- a/mgw/crcx.c	Sat Oct 01 17:09:51 2022 -0800
+++ b/mgw/crcx.c	Sat Oct 01 20:31:15 2022 -0800
@@ -4,6 +4,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/time.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <stdio.h>
--- a/mgw/ctrl_prot.c	Sat Oct 01 17:09:51 2022 -0800
+++ b/mgw/ctrl_prot.c	Sat Oct 01 20:31:15 2022 -0800
@@ -4,6 +4,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/time.h>
 #include <netinet/in.h>
 #include <stdio.h>
 #include <stdint.h>
@@ -59,6 +60,12 @@
 	case TMGW_CTRL_OP_DLCX:
 		process_dlcx(conn, &req, &resp);
 		break;
+	case TMGW_CTRL_OP_DTMF_START:
+		process_dtmf_start(conn, &req, &resp);
+		break;
+	case TMGW_CTRL_OP_DTMF_STOP:
+		process_dtmf_stop(conn, &req, &resp);
+		break;
 	default:
 		resp.res = TMGW_RESP_ERR_OPCODE;
 	}
--- a/mgw/ctrl_sock.c	Sat Oct 01 17:09:51 2022 -0800
+++ b/mgw/ctrl_sock.c	Sat Oct 01 20:31:15 2022 -0800
@@ -4,7 +4,7 @@
  */
 
 #include <sys/types.h>
-#include <sys/stat.h>
+#include <sys/time.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <netinet/in.h>
--- a/mgw/dlcx.c	Sat Oct 01 17:09:51 2022 -0800
+++ b/mgw/dlcx.c	Sat Oct 01 20:31:15 2022 -0800
@@ -4,6 +4,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/time.h>
 #include <netinet/in.h>
 #include <stdio.h>
 #include <stdint.h>
@@ -79,6 +80,8 @@
 
 	for (ep = delq; ep; ep = np) {
 		np = ep->next;
+		if (ep->dtmf_pp)
+			dtmf_stop_immediate(ep);
 		if (ep->ep_type & TMGW_EP_HAS_GSM_SOCK)
 			free_rtp_end(&ep->rtp_gsm);
 		if (ep->ep_type & TMGW_EP_HAS_PSTN_SOCK)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mgw/dtmf_ctrl.c	Sat Oct 01 20:31:15 2022 -0800
@@ -0,0 +1,137 @@
+/*
+ * In this module we implement start/stop control of DTMF generation
+ * toward PSTN on GSM-to-PSTN gateway endpoints.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include "../include/tmgw_ctrl.h"
+#include "../include/tmgw_const.h"
+#include "struct.h"
+#include "int_defs.h"
+#include "dtmf_defs.h"
+
+extern struct endpoint *find_ep_by_id();
+
+extern struct timeval cur_event_time;
+extern struct dtmf_desc dtmf_table[];
+extern int dtmf_timer_running;
+
+struct endpoint *dtmf_list_head;
+
+static void
+add_to_dtmf_list(new_ep)
+	struct endpoint *new_ep;
+{
+	struct endpoint *ep, **epp;
+
+	for (epp = &dtmf_list_head; *epp; epp = &ep->dtmf_next)
+		;
+	*epp = new_ep;
+	new_ep->dtmf_pp = epp;
+}
+
+void
+dtmf_stop_immediate(ep)
+	struct endpoint *ep;
+{
+	if (ep->dtmf_frames_sent)
+		ep->dtmf_aftermath = 1;
+	*ep->dtmf_pp = ep->dtmf_next;
+	if (ep->dtmf_next)
+		ep->dtmf_next->dtmf_pp = ep->dtmf_pp;
+	ep->dtmf_pp = 0;
+	ep->dtmf_next = 0;
+	if (!dtmf_list_head)
+		dtmf_timer_running = 0;
+}
+
+void
+process_dtmf_start(conn, req, resp)
+	struct ctrl_conn *conn;
+	struct tmgw_ctrl_req *req;
+	struct tmgw_ctrl_resp *resp;
+{
+	struct endpoint *ep;
+	struct dtmf_desc *desc;
+	struct timeval tv_diff;
+	unsigned delta_frames;
+
+	ep = find_ep_by_id(conn, req->ep_id);
+	if (!ep) {
+protocol_err:	resp->res = TMGW_RESP_ERR_PROT;
+		return;
+	}
+	if (ep->ep_type != TMGW_EP_TYPE_GATEWAY)
+		goto protocol_err;
+	if (!(ep->fwd_mode & TMGW_FWD_ENABLE_GSM2PSTN))
+		goto protocol_err;
+	for (desc = dtmf_table; desc->digit; desc++)
+		if (desc->digit == req->fwd_mode)
+			break;
+	if (!desc->digit) {
+		resp->res = TMGW_RESP_ERR_PARAM;
+		return;
+	}
+	if (ep->dtmf_pp) {
+		resp->res = TMGW_RESP_ERR_BUSY;
+		return;
+	}
+	if (!ep->g2p_state) {
+		resp->res = TMGW_RESP_ERR_NOTRDY;
+		return;
+	}
+	/* figure out starting timestamp */
+	if (!ep->dtmf_aftermath)
+		ep->dtmf_last_ts = ep->g2p_last_ts;
+	ep->dtmf_m_bit = 0;
+	if (timercmp(&cur_event_time, &ep->g2p_local_time, >)) {
+		timersub(&cur_event_time, &ep->g2p_local_time, &tv_diff);
+		delta_frames = tv_diff.tv_sec * 50 + tv_diff.tv_usec / 20000;
+		if (delta_frames) {
+			ep->dtmf_last_ts += delta_frames * SAMPLES_PER_FRAME;
+			ep->dtmf_m_bit = 1;
+		}
+	}
+	/* initialize other state vars */
+	ep->dtmf_frames_sent = 0;
+	ep->dtmf_sample_ptr = desc->samples;
+	ep->dtmf_stop_req = 0;
+	/* link it and start it */
+	add_to_dtmf_list(ep);
+	start_dtmf_timer();
+	/* return success */
+	resp->res = TMGW_RESP_OK;
+}
+
+void
+process_dtmf_stop(conn, req, resp)
+	struct ctrl_conn *conn;
+	struct tmgw_ctrl_req *req;
+	struct tmgw_ctrl_resp *resp;
+{
+	struct endpoint *ep;
+
+	ep = find_ep_by_id(conn, req->ep_id);
+	if (!ep) {
+protocol_err:	resp->res = TMGW_RESP_ERR_PROT;
+		return;
+	}
+	if (ep->ep_type != TMGW_EP_TYPE_GATEWAY)
+		goto protocol_err;
+	/* return OK whether we stop a tone or if there was none running */
+	resp->res = TMGW_RESP_OK;
+	if (ep->dtmf_pp) {
+		ep->dtmf_stop_req = 1;
+		if (ep->dtmf_frames_sent >= DTMF_MIN_FRAMES)
+			dtmf_stop_immediate(ep);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mgw/dtmf_timer.c	Sat Oct 01 20:31:15 2022 -0800
@@ -0,0 +1,90 @@
+/*
+ * In this module we implement the timer function of DTMF generation.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include "struct.h"
+#include "int_defs.h"
+#include "dtmf_defs.h"
+
+extern struct timeval cur_event_time;
+extern struct endpoint *dtmf_list_head;
+
+int dtmf_timer_running;
+struct timeval dtmf_next_time;
+
+void
+start_dtmf_timer()
+{
+	if (dtmf_timer_running)
+		return;
+	dtmf_next_time = cur_event_time;
+	dtmf_timer_running = 1;
+}
+
+dtmf_timer_one(ep)
+	struct endpoint *ep;
+{
+	struct rtp_packet pkt;
+	socklen_t addrlen;
+	unsigned frame_limit;
+
+	pkt.v_p_x_cc = 0x80;
+	pkt.m_pt = ep->pstn_payload_type;
+	if (ep->dtmf_m_bit) {
+		pkt.m_pt |= 0x80;
+		ep->dtmf_m_bit = 0;
+	}
+	pkt.seq = htons(++ep->g2p_out_seq);
+	ep->dtmf_last_ts += SAMPLES_PER_FRAME;
+	pkt.tstamp = htonl(ep->dtmf_last_ts);
+	pkt.ssrc = ep->g2p_ssrc;
+	g711_encode_frame(ep->dtmf_sample_ptr, pkt.payload,
+			  ep->pstn_payload_type);
+	ep->dtmf_sample_ptr += SAMPLES_PER_FRAME;
+	addrlen = sizeof(struct sockaddr_in);
+	sendto(ep->rtp_pstn.rtp_fd, &pkt, RTP_PACKET_SIZE_PSTN, 0,
+		(struct sockaddr *) &ep->rtp_pstn.remote_addr, addrlen);
+	ep->dtmf_frames_sent++;
+	frame_limit = ep->dtmf_stop_req ? DTMF_MIN_FRAMES : DTMF_MAX_FRAMES;
+	if (ep->dtmf_frames_sent >= frame_limit)
+		return 1;
+	else
+		return 0;
+}
+
+void
+dtmf_timer_process()
+{
+	struct endpoint *ep;
+	int fin;
+
+	for (ep = dtmf_list_head; ep; ep = ep->dtmf_next) {
+		fin = dtmf_timer_one(ep);
+		if (!fin)
+			continue;
+		ep->dtmf_aftermath = 1;
+		*ep->dtmf_pp = ep->dtmf_next;
+		if (ep->dtmf_next)
+			ep->dtmf_next->dtmf_pp = ep->dtmf_pp;
+		ep->dtmf_pp = 0;
+		ep->dtmf_next = 0;
+	}
+	if (dtmf_list_head) {
+		dtmf_next_time.tv_usec += 20000;
+		if (dtmf_next_time.tv_usec >= 1000000) {
+			dtmf_next_time.tv_usec -= 1000000;
+			dtmf_next_time.tv_sec++;
+		}
+	} else
+		dtmf_timer_running = 0;
+}
--- a/mgw/gsm2pstn.c	Sat Oct 01 17:09:51 2022 -0800
+++ b/mgw/gsm2pstn.c	Sat Oct 01 20:31:15 2022 -0800
@@ -5,6 +5,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/time.h>
 #include <netinet/in.h>
 #include <stdio.h>
 #include <stdint.h>
@@ -20,6 +21,8 @@
 #include "select.h"
 #include "int_defs.h"
 
+extern struct timeval cur_event_time;
+
 #define	ERR_WRONG_UDP_SRC	0x0001
 #define	ERR_BAD_RTP_PACKET	0x0002
 #define	ERR_SSRC_CHANGE		0x0004
@@ -71,6 +74,8 @@
 			ep->g2p_err_flags |= ERR_SSRC_CHANGE;
 		}
 		ep->g2p_state = 0;
+		if (ep->dtmf_pp)
+			dtmf_stop_immediate(ep);
 	}
 	if (ep->g2p_state) {
 		seq_delta = ntohs(pkt.seq) - ep->g2p_last_seq;
@@ -104,15 +109,28 @@
 	ep->g2p_ssrc = pkt.ssrc;
 	ep->g2p_last_ts = ntohl(pkt.tstamp);
 	ep->g2p_last_seq = ntohs(pkt.seq);
+	ep->g2p_local_time = cur_event_time;
 	/* actual transcoding and forwarding */
 	if (!(ep->fwd_mode & TMGW_FWD_ENABLE_GSM2PSTN)) {
 		ep->g2p_drop_flag = 1;
 		return;
 	}
+	if (ep->dtmf_pp)
+		return;
 	if (ep->g2p_drop_flag) {
 		ep->g2p_drop_flag = 0;
 		m_out = 1;
 	}
+	if (ep->dtmf_aftermath) {
+		ts_delta = ep->g2p_last_ts - ep->dtmf_last_ts;
+		if (ts_delta <= 0)
+			return;
+		ep->dtmf_aftermath = 0;
+		if (ts_delta == SAMPLES_PER_FRAME)
+			m_out = 0;
+		else
+			m_out = 1;
+	}
 	switch (ep->gsm_payload_msg_type) {
 	case GSM_TCHF_FRAME:
 		gsm_decode(ep->gsm_decoder_state, pkt.payload, pcm_samples);
--- a/mgw/main.c	Sat Oct 01 17:09:51 2022 -0800
+++ b/mgw/main.c	Sat Oct 01 20:31:15 2022 -0800
@@ -3,6 +3,7 @@
  */
 
 #include <sys/types.h>
+#include <sys/time.h>
 #include <sys/errno.h>
 #include <stdio.h>
 #include <stdint.h>
@@ -13,9 +14,13 @@
 #include <syslog.h>
 #include <unistd.h>
 
+extern int dtmf_timer_running;
+extern struct timeval dtmf_next_time;
+
 fd_set select_for_read;
 void (*select_handlers[FD_SETSIZE])();
 void *select_data[FD_SETSIZE];
+struct timeval cur_event_time;
 
 static int max_fd;
 
@@ -34,6 +39,7 @@
 	char **argv;
 {
 	fd_set fds;
+	struct timeval timeout;
 	int cc, i;
 
 	openlog("themwi-mgw", 0, LOG_LOCAL5);
@@ -47,19 +53,31 @@
 	/* main select loop */
 	for (;;) {
 		bcopy(&select_for_read, &fds, sizeof(fd_set));
-		cc = select(max_fd+1, &fds, 0, 0, 0);
+		if (dtmf_timer_running) {
+			if (timercmp(&dtmf_next_time, &cur_event_time, >))
+				timersub(&dtmf_next_time, &cur_event_time,
+					 &timeout);
+			else
+				timerclear(&timeout);
+			cc = select(max_fd+1, &fds, 0, 0, &timeout);
+		} else
+			cc = select(max_fd+1, &fds, 0, 0, 0);
 		if (cc < 0) {
 			if (errno == EINTR)
 				continue;
 			syslog(LOG_CRIT, "select: %m");
 			exit(1);
 		}
+		gettimeofday(&cur_event_time, 0);
 		for (i = 0; cc && i <= max_fd; i++) {
 			if (FD_ISSET(i, &fds)) {
 				select_handlers[i](i, select_data[i]);
 				cc--;
 			}
 		}
+		if (dtmf_timer_running &&
+		    !timercmp(&cur_event_time, &dtmf_next_time, <))
+			dtmf_timer_process();
 		free_deleted_endpoints();
 	}
 }
--- a/mgw/mdcx.c	Sat Oct 01 17:09:51 2022 -0800
+++ b/mgw/mdcx.c	Sat Oct 01 20:31:15 2022 -0800
@@ -4,6 +4,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/time.h>
 #include <netinet/in.h>
 #include <stdio.h>
 #include <stdint.h>
@@ -87,14 +88,13 @@
 		    ep->rtp_gsm.remote_addr.sin_family != AF_INET ||
 		    ep->rtp_pstn.remote_addr.sin_family != AF_INET)
 			return TMGW_RESP_ERR_PROT;
-		if ((req->fwd_mode & TMGW_FWD_ENABLE_GSM2PSTN) &&
-		    !(ep->fwd_mode & TMGW_FWD_ENABLE_GSM2PSTN)) {
+		if (req->fwd_mode & TMGW_FWD_ENABLE_GSM2PSTN) {
 			rc = gsm2pstn_init(ep);
 			if (rc != TMGW_RESP_OK)
 				return rc;
-		}
-		if ((req->fwd_mode & TMGW_FWD_ENABLE_PSTN2GSM) &&
-		    !(ep->fwd_mode & TMGW_FWD_ENABLE_PSTN2GSM)) {
+		} else if (ep->dtmf_pp)
+			dtmf_stop_immediate(ep);
+		if (req->fwd_mode & TMGW_FWD_ENABLE_PSTN2GSM) {
 			rc = pstn2gsm_init(ep);
 			if (rc != TMGW_RESP_OK)
 				return rc;
@@ -111,7 +111,6 @@
 	struct tmgw_ctrl_resp *resp;
 {
 	struct endpoint *ep;
-	int rc;
 
 	ep = find_ep_by_id(conn, req->ep_id);
 	if (!ep) {
--- a/mgw/pstn2gsm.c	Sat Oct 01 17:09:51 2022 -0800
+++ b/mgw/pstn2gsm.c	Sat Oct 01 20:31:15 2022 -0800
@@ -5,6 +5,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/time.h>
 #include <netinet/in.h>
 #include <stdio.h>
 #include <stdint.h>
--- a/mgw/readconf.c	Sat Oct 01 17:09:51 2022 -0800
+++ b/mgw/readconf.c	Sat Oct 01 20:31:15 2022 -0800
@@ -6,6 +6,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/time.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <ctype.h>
--- a/mgw/struct.h	Sat Oct 01 17:09:51 2022 -0800
+++ b/mgw/struct.h	Sat Oct 01 20:31:15 2022 -0800
@@ -38,6 +38,14 @@
 	uint16_t	g2p_out_seq;
 	int		g2p_drop_flag;
 	int		g2p_err_flags;
+	struct timeval	g2p_local_time;
+	/* DTMF generation toward PSTN */
+	unsigned	dtmf_frames_sent;
+	uint32_t	dtmf_last_ts;
+	int16_t		*dtmf_sample_ptr;
+	int		dtmf_m_bit;
+	int		dtmf_stop_req;
+	int		dtmf_aftermath;
 	/* PSTN to GSM forwarding */
 	int		p2g_state;
 	uint32_t	p2g_ssrc;
@@ -49,6 +57,8 @@
 	/* linked list management */
 	unsigned	ep_id;
 	struct endpoint	*next;
+	struct endpoint	*dtmf_next;
+	struct endpoint	**dtmf_pp;
 };
 
 struct ctrl_conn {