aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--configure.ac1
-rw-r--r--include/Makefile.am2
-rw-r--r--include/dtmf_scheduler.h25
-rw-r--r--include/mgcp/mgcp_internal.h2
-rw-r--r--include/mgcp_ss7.h1
-rw-r--r--src/Makefile.am3
-rw-r--r--src/dtmf_scheduler.c59
-rw-r--r--src/mgcp_ss7.c72
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/dtmf/Makefile.am8
-rw-r--r--tests/dtmf/dtmf_test.c103
-rw-r--r--tests/dtmf/dtmf_test.ok1
-rw-r--r--tests/testsuite.at6
14 files changed, 286 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index 867b766..5056481 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,9 @@ stamp-h1
*.o
*.sw?
*.orig
+*.gcda
+*.gcno
+*.info
# binaries
cellmgr_ng
@@ -34,6 +37,7 @@ aclocal.m4
autom4te.cache/
configure.lineno
tests/atconfig
+tests/dtmf/dtmf_test
tests/isup/isup_parse_test
tests/mgcp/mgcp_patch_test
tests/package.m4
diff --git a/configure.ac b/configure.ac
index b37d8d8..f0a50e6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -52,4 +52,5 @@ AC_OUTPUT(
tests/patching/Makefile
tests/isup/Makefile
tests/mgcp/Makefile
+ tests/dtmf/Makefile
Makefile)
diff --git a/include/Makefile.am b/include/Makefile.am
index 01c7b9c..c923515 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -2,6 +2,6 @@ noinst_HEADERS = mtp_level3.h mtp_data.h ipaccess.h thread.h mtp_pcap.h \
mgcp_ss7.h bss_patch.h bssap_sccp.h bsc_data.h udp_input.h \
snmp_mtp.h cellmgr_debug.h bsc_sccp.h bsc_ussd.h sctp_m2ua.h \
isup_types.h counter.h msc_connection.h ss7_application.h \
- mgcp_patch.h ss7_vty.h
+ mgcp_patch.h ss7_vty.h dtmf_scheduler.h
SUBDIRS = mgcp
diff --git a/include/dtmf_scheduler.h b/include/dtmf_scheduler.h
new file mode 100644
index 0000000..22a4e55
--- /dev/null
+++ b/include/dtmf_scheduler.h
@@ -0,0 +1,25 @@
+#ifndef DTMF_SCHEDULER_H
+#define DTMF_SCHEDULER_H
+
+/**
+ * The state/queue for DTMF signalling.
+ */
+struct dtmf_state {
+ int size; /* <! The last tone to play */
+ char tones[24]; /* <! Pending tones */
+ int playing; /* <! Playing a tone right now? */
+};
+
+/* initialize */
+void dtmf_state_init(struct dtmf_state *state);
+
+/* add a tone to the list */
+int dtmf_state_add(struct dtmf_state *state, char tone);
+
+/* tones that should be played, playing will be set to 1 */
+void dtmf_state_get_pending(struct dtmf_state *state, char *tones);
+
+/* call when the playout is done */
+void dtmf_state_played(struct dtmf_state *state);
+
+#endif
diff --git a/include/mgcp/mgcp_internal.h b/include/mgcp/mgcp_internal.h
index 1918cb0..6b2989d 100644
--- a/include/mgcp/mgcp_internal.h
+++ b/include/mgcp/mgcp_internal.h
@@ -24,6 +24,7 @@
#define OPENBSC_MGCP_DATA_H
#include <osmocom/core/select.h>
+#include <dtmf_scheduler.h>
#define CI_UNUSED 0
@@ -124,6 +125,7 @@ struct mgcp_endpoint {
struct mgcp_rtp_tap taps[MGCP_TAP_COUNT];
/* Special MGW handling */
+ struct dtmf_state dtmf_state;
int blocked;
unsigned int hw_dsp_port; /** This is index 1 based */
unsigned int audio_port;
diff --git a/include/mgcp_ss7.h b/include/mgcp_ss7.h
index 4847989..babfde6 100644
--- a/include/mgcp_ss7.h
+++ b/include/mgcp_ss7.h
@@ -46,6 +46,7 @@ enum {
MGCP_SS7_MUTE_STATUS,
MGCP_SS7_ALLOCATE,
MGCP_SS7_DELETE,
+ MGCP_SS7_DTMF,
};
struct mgcp_ss7_cmd {
diff --git a/src/Makefile.am b/src/Makefile.am
index 4ef3edf..e681537 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,7 +6,8 @@ AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOVTY_CFLAGS)
sbin_PROGRAMS = cellmgr_ng osmo_stp mgcp_mgw
mgcp_mgw_SOURCES = mgcp_ss7.c mgcp_ss7_vty.c mgcp_hw.c thread.c debug.c \
- mgcp/mgcp_protocol.c mgcp/mgcp_network.c mgcp/mgcp_vty.c
+ mgcp/mgcp_protocol.c mgcp/mgcp_network.c mgcp/mgcp_vty.c \
+ dtmf_scheduler.c
mgcp_mgw_LDADD = $(LAFORGE_LIBS) $(NEXUSWARE_C7_LIBS) $(NEXUSWARE_UNIPORTE_LIBS) \
$(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) -lpthread -lcrypto
diff --git a/src/dtmf_scheduler.c b/src/dtmf_scheduler.c
new file mode 100644
index 0000000..26dc090
--- /dev/null
+++ b/src/dtmf_scheduler.c
@@ -0,0 +1,59 @@
+/*
+ * (C) 2012 by Holger Hans Peter Freyther
+ * (C) 2012 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "dtmf_scheduler.h"
+#include <string.h>
+#include <stdio.h>
+
+void dtmf_state_init(struct dtmf_state *state)
+{
+ memset(state, 0, sizeof(*state));
+}
+
+int dtmf_state_add(struct dtmf_state *state, char tone)
+{
+ /* we would override the head */
+ if (state->size == sizeof(state->tones))
+ return -1;
+
+ state->tones[state->size++] = tone;
+ return 0;
+}
+
+void dtmf_state_get_pending(struct dtmf_state *state, char *tones)
+{
+ int pos;
+
+ for (pos = 0; pos < state->size; ++pos)
+ tones[pos] = state->tones[pos];
+
+ /* consume everything up to the tail */
+ state->size = 0;
+
+ /* remember that we play things */
+ if (pos > 0)
+ state->playing = 1;
+ tones[pos] = '\0';
+}
+
+void dtmf_state_played(struct dtmf_state *state)
+{
+ state->playing = 0;
+}
diff --git a/src/mgcp_ss7.c b/src/mgcp_ss7.c
index 47c162b..c9e9b4f 100644
--- a/src/mgcp_ss7.c
+++ b/src/mgcp_ss7.c
@@ -70,6 +70,44 @@ static void mgcp_ss7_do_exec(struct mgcp_ss7 *mgcp, uint8_t type, struct mgcp_en
/* Contains a mapping from UniPorte to the MGCP side of things */
static struct mgcp_endpoint *s_endpoints[240];
+static void play_pending_tones(struct mgcp_endpoint *endp)
+{
+ ToneGenerationT toneGeneration;
+ char tones[25];
+
+ /* Check if we need to play anything? */
+ dtmf_state_get_pending(&endp->dtmf_state, tones);
+
+ /* nothing to play? */
+ if (strlen(tones) == 0)
+ return;
+
+ /* fill out the data now */
+ osmo_static_assert(sizeof(tones) <= sizeof(toneGeneration.list), Enough_space_for_tones);
+ memset(&toneGeneration, 0, sizeof(toneGeneration));
+ toneGeneration.count = strlen(tones);
+ strcpy(toneGeneration.list, tones);
+ MtnSaSetMOB(endp->audio_port, ChannelType_PORT,
+ PredefMob_C_TONE_GENERATION, (char *) &toneGeneration,
+ sizeof(toneGeneration), 1);
+}
+
+static void send_dtmf(struct mgcp_endpoint *mgw_endp, int ascii_tone)
+{
+ int rc;
+ rc = dtmf_state_add(&mgw_endp->dtmf_state, ascii_tone);
+ if (rc != 0) {
+ fprintf(stderr, "DTMF queue too long on 0x%x\n",
+ ENDPOINT_NUMBER(mgw_endp));
+ syslog(LOG_ERR, "DTMF queue too long on 0x%x\n",
+ ENDPOINT_NUMBER(mgw_endp));
+ return;
+ }
+
+ if (!mgw_endp->dtmf_state.playing)
+ play_pending_tones(mgw_endp);
+}
+
static int select_voice_port(struct mgcp_endpoint *endp)
{
int mgw_port;
@@ -200,6 +238,24 @@ static int uniporte_events(unsigned long port, EventTypeT event,
fprintf(stderr, "State change on a non blocked port. ERROR.\n");
}
endp->block_processing = 0;
+ } else if (info->trapId == Trap_TONE_GENERATION_COMPLETE) {
+ sprintf(text, "DTMF complete on #%ld", port);
+ puts(text);
+
+ /* update the mgcp state */
+ if (port >= ARRAY_SIZE(s_endpoints)) {
+ syslog(LOG_ERR, "The port is bigger than we can manage.\n");
+ fprintf(stderr, "The port is bigger than we can manage.\n");
+ return 0;
+ }
+
+ endp = s_endpoints[port];
+ if (!endp) {
+ syslog(LOG_ERR, "Unexpected event on port %d\n", port);
+ fprintf(stderr, "Unexpected event on port %d\n", port);
+ return 0;
+ }
+ play_pending_tones(endp);
}
}
else if ( event == Event_MANAGED_OBJECT_SET_COMPLETE ) {
@@ -356,6 +412,9 @@ static void allocate_endp(struct mgcp_ss7 *ss7, struct mgcp_endpoint *endp)
int mgw_port;
unsigned long mgw_address, loc_address;
+ /* reset the DTMF state */
+ dtmf_state_init(&endp->dtmf_state);
+
/* now find the voice processor we want to use */
mgw_port = select_voice_port(endp);
if (mgw_port < 0)
@@ -471,6 +530,10 @@ static void mgcp_ss7_do_exec(struct mgcp_ss7 *mgcp, uint8_t type,
if (mgw_endp->audio_port != UINT_MAX)
update_mute_status(mgw_endp->audio_port, param);
break;
+ case MGCP_SS7_DTMF:
+ if (mgw_endp->audio_port != UINT_MAX)
+ send_dtmf(mgw_endp, param);
+ break;
case MGCP_SS7_DELETE:
if (mgw_endp->audio_port != UINT_MAX) {
rc = MtnSaDisconnect(mgw_endp->audio_port);
@@ -487,6 +550,7 @@ static void mgcp_ss7_do_exec(struct mgcp_ss7 *mgcp, uint8_t type,
mgw_endp->audio_port = UINT_MAX;
mgw_endp->block_processing = 1;
}
+ dtmf_state_init(&mgw_endp->dtmf_state);
hw_maybe_loop_endp(mgw_endp);
break;
case MGCP_SS7_ALLOCATE:
@@ -579,6 +643,12 @@ static int mgcp_ss7_policy(struct mgcp_trunk_config *tcfg, int endp_no, int stat
return rc;
}
+static int mgcp_dtmf_cb(struct mgcp_endpoint *endp, char tone, const char *data)
+{
+ mgcp_ss7_exec(endp, MGCP_SS7_DTMF, tone);
+ return 0;
+}
+
static void enqueue_msg(struct osmo_wqueue *queue, struct sockaddr_in *addr, struct msgb *msg)
{
struct sockaddr_in *data;
@@ -905,6 +975,8 @@ int main(int argc, char **argv)
return -1;
}
+ g_cfg->rqnt_cb = mgcp_dtmf_cb;
+
if (mgcp_parse_config(config_file, g_cfg) != 0) {
LOGP(DMGCP, LOGL_ERROR,
"Failed to parse the config file: '%s'\n", config_file);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6efc649..5c9cdd5 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = mtp patching isup mgcp
+SUBDIRS = mtp patching isup mgcp dtmf
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
diff --git a/tests/dtmf/Makefile.am b/tests/dtmf/Makefile.am
new file mode 100644
index 0000000..4241120
--- /dev/null
+++ b/tests/dtmf/Makefile.am
@@ -0,0 +1,8 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
+noinst_PROGRAMS = dtmf_test
+
+EXTRA_DIST = dtmf_test.ok
+
+dtmf_test_SOURCES = dtmf_test.c $(top_srcdir)/src/dtmf_scheduler.c
+dtmf_test_LDADD = $(LIBOSMOCORE_LIBS)
diff --git a/tests/dtmf/dtmf_test.c b/tests/dtmf/dtmf_test.c
new file mode 100644
index 0000000..5ffb121
--- /dev/null
+++ b/tests/dtmf/dtmf_test.c
@@ -0,0 +1,103 @@
+/*
+ * (C) 2012 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2012 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <dtmf_scheduler.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define ASSERT(got,want) \
+ if (got != want) { \
+ fprintf(stderr, "Values should be the same 0x%x 0x%x at %s:%d\n", \
+ got, want, __FILE__, __LINE__); \
+ abort(); \
+ }
+
+static void test_queue_while_play(void)
+{
+ struct dtmf_state state;
+ char tone[sizeof(state.tones) + 1];
+
+ dtmf_state_init(&state);
+
+ ASSERT(dtmf_state_add(&state, 'a'), 0);
+ ASSERT(dtmf_state_add(&state, 'b'), 0);
+ ASSERT(dtmf_state_add(&state, 'c'), 0);
+
+ dtmf_state_get_pending(&state, tone);
+ ASSERT(strlen(tone), 3);
+ ASSERT(state.playing, 1);
+ ASSERT(strcmp(tone, "abc"), 0);
+
+ ASSERT(dtmf_state_add(&state, 'd'), 0);
+ dtmf_state_played(&state);
+ ASSERT(state.playing, 0);
+
+ dtmf_state_get_pending(&state, tone);
+ ASSERT(strlen(tone), 1);
+ ASSERT(state.playing, 1);
+ ASSERT(strcmp(tone, "d"), 0);
+
+ ASSERT(state.playing, 1);
+ dtmf_state_played(&state);
+ ASSERT(state.playing, 0);
+
+ /* and check that nothing is played */
+ dtmf_state_get_pending(&state, tone);
+ ASSERT(strlen(tone), 0);
+ ASSERT(state.playing, 0);
+}
+
+static void test_queue_over_flow(void)
+{
+ struct dtmf_state state;
+ const size_t max_items = sizeof(state.tones);
+ char tone[sizeof(state.tones) + 1];
+ int i;
+
+ dtmf_state_init(&state);
+
+ /* add everything that should fit.. */
+ for (i = 0; i < max_items; ++i) {
+ ASSERT(dtmf_state_add(&state, 'a' + i), 0);
+ }
+
+ /* this should fail */
+ ASSERT(dtmf_state_add(&state, 'Z'), -1);
+
+ /* read all of it */
+ dtmf_state_get_pending(&state, tone);
+ ASSERT(strlen(tone), max_items);
+ for (i = 0; i < strlen(tone); ++i)
+ ASSERT(tone[i], 'a' + i);
+ ASSERT(state.playing, 1);
+ dtmf_state_played(&state);
+ ASSERT(state.playing, 0);
+}
+
+
+int main(int argc, char **argv)
+{
+ test_queue_while_play();
+ test_queue_over_flow();
+ printf("All tests passed.\n");
+ return 0;
+}
diff --git a/tests/dtmf/dtmf_test.ok b/tests/dtmf/dtmf_test.ok
new file mode 100644
index 0000000..828a010
--- /dev/null
+++ b/tests/dtmf/dtmf_test.ok
@@ -0,0 +1 @@
+All tests passed.
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 069a502..de06c92 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -24,3 +24,9 @@ AT_KEYWORDS([patching])
cat $abs_srcdir/patching/patching_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/patching/patching_test], [], [expout], [ignore])
AT_CLEANUP
+
+AT_SETUP([dtmf])
+AT_KEYWORDS([dtmf])
+cat $abs_srcdir/dtmf/dtmf_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/dtmf/dtmf_test], [], [expout], [ignore])
+AT_CLEANUP