view mgw/dtmf_ctrl.c @ 150:529906fddcfa

mgw DTMF timing fix in the corner case of no speech arriving between the end of one DTMF and the start of next
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 11 Oct 2022 14:10:29 -0800
parents f062c32a5116
children a6eb2de277f6
line wrap: on
line source

/*
 * 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_last, 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)
		tv_last = ep->dtmf_last_time;
	else {
		ep->dtmf_last_ts = ep->g2p_last_ts;
		tv_last = ep->g2p_local_time;
	}
	ep->dtmf_m_bit = 0;
	if (timercmp(&cur_event_time, &tv_last, >)) {
		timersub(&cur_event_time, &tv_last, &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);
	}
}