summaryrefslogtreecommitdiff
path: root/channels/dahdi
diff options
context:
space:
mode:
Diffstat (limited to 'channels/dahdi')
-rw-r--r--channels/dahdi/bridge_native_dahdi.c928
-rw-r--r--channels/dahdi/bridge_native_dahdi.h47
2 files changed, 975 insertions, 0 deletions
diff --git a/channels/dahdi/bridge_native_dahdi.c b/channels/dahdi/bridge_native_dahdi.c
new file mode 100644
index 000000000..611994f5e
--- /dev/null
+++ b/channels/dahdi/bridge_native_dahdi.c
@@ -0,0 +1,928 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013 Digium, Inc.
+ *
+ * Richard Mudgett <rmudgett@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Native DAHDI bridging support.
+ *
+ * \author Richard Mudgett <rmudgett@digium.com>
+ *
+ * See Also:
+ * \arg \ref AstCREDITS
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "../sig_analog.h"
+#if defined(HAVE_PRI)
+#include "../sig_pri.h"
+#endif /* defined(HAVE_PRI) */
+#include "../chan_dahdi.h"
+
+#include "asterisk/astobj.h"
+#include "bridge_native_dahdi.h"
+#include "asterisk/bridging.h"
+#include "asterisk/bridging_technology.h"
+#include "asterisk/frame.h"
+
+/* ------------------------------------------------------------------- */
+
+static const struct ast_channel_tech *dahdi_tech;
+
+struct native_pvt_chan {
+ /*! Original private. */
+ struct dahdi_pvt *pvt;
+ /*! Original private owner. */
+ struct ast_channel *owner;
+ /*! Original owner index. */
+ int index;
+ /*! Original file descriptor 0. */
+ int fd0;
+ /*! Original channel state. */
+ int state;
+ /*! Original inthreeway. */
+ unsigned int inthreeway:1;
+};
+
+struct native_pvt_bridge {
+ /*! Master channel in the native bridge. */
+ struct dahdi_pvt *master;
+ /*! Slave channel in the native bridge. */
+ struct dahdi_pvt *slave;
+ /*! TRUE if the bridge can start when ready. */
+ unsigned int saw_start:1;
+ /*! TRUE if the channels are connected in a conference. */
+ unsigned int connected:1;
+#if defined(HAVE_PRI) && defined(PRI_2BCT)
+ /*!
+ * \brief TRUE if tried to eliminate possible PRI tromboned call.
+ *
+ * \note A tromboned call uses two B channels of the same ISDN
+ * span. One leg comes into Asterisk, the other leg goes out of
+ * Asterisk, and Asterisk is natively bridging the two legs.
+ */
+ unsigned int tried_trombone_removal:1;
+#endif /* defined(HAVE_PRI) && defined(PRI_2BCT) */
+};
+
+/*!
+ * \internal
+ * \brief Create a bridge technology instance for a bridge.
+ * \since 12.0.0
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * \note On entry, bridge may or may not already be locked.
+ * However, it can be accessed as if it were locked.
+ */
+static int native_bridge_create(struct ast_bridge *bridge)
+{
+ struct native_pvt_bridge *tech_pvt;
+
+ ast_assert(!bridge->tech_pvt);
+
+ tech_pvt = ast_calloc(1, sizeof(*tech_pvt));
+ if (!tech_pvt) {
+ return -1;
+ }
+
+ bridge->tech_pvt = tech_pvt;
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Destroy a bridging technology instance for a bridge.
+ * \since 12.0.0
+ *
+ * \note On entry, bridge must NOT be locked.
+ */
+static void native_bridge_destroy(struct ast_bridge *bridge)
+{
+ struct native_pvt_bridge *tech_pvt;
+
+ tech_pvt = bridge->tech_pvt;
+ bridge->tech_pvt = NULL;
+ ast_free(tech_pvt);
+}
+
+/*!
+ * \internal
+ * \brief Stop native bridging activity.
+ * \since 12.0.0
+ *
+ * \param bridge What to operate upon.
+ *
+ * \return Nothing
+ *
+ * \note On entry, bridge is already locked.
+ */
+static void native_stop(struct ast_bridge *bridge)
+{
+ struct native_pvt_bridge *bridge_tech_pvt;
+ struct ast_bridge_channel *cur;
+
+ ast_assert(bridge->tech_pvt != NULL);
+
+ AST_LIST_TRAVERSE(&bridge->channels, cur, entry) {
+ struct native_pvt_chan *chan_tech_pvt;
+
+ chan_tech_pvt = cur->tech_pvt;
+ if (!chan_tech_pvt) {
+ continue;
+ }
+
+ ast_mutex_lock(&chan_tech_pvt->pvt->lock);
+ if (chan_tech_pvt->pvt == ast_channel_tech_pvt(cur->chan)) {
+ dahdi_enable_ec(chan_tech_pvt->pvt);
+ }
+ if (chan_tech_pvt->index == SUB_REAL) {
+ enable_dtmf_detect(chan_tech_pvt->pvt);
+ }
+ ast_mutex_unlock(&chan_tech_pvt->pvt->lock);
+ }
+
+ bridge_tech_pvt = bridge->tech_pvt;
+ dahdi_unlink(bridge_tech_pvt->slave, bridge_tech_pvt->master, 1);
+
+ ast_debug(2, "Stop native bridging %s and %s\n",
+ ast_channel_name(AST_LIST_FIRST(&bridge->channels)->chan),
+ ast_channel_name(AST_LIST_LAST(&bridge->channels)->chan));
+}
+
+/*!
+ * \internal
+ * \brief Request to stop native bridging activity.
+ * \since 12.0.0
+ *
+ * \param bridge What to operate upon.
+ *
+ * \return Nothing
+ *
+ * \note On entry, bridge is already locked.
+ */
+static void native_request_stop(struct ast_bridge *bridge)
+{
+ struct native_pvt_bridge *tech_pvt;
+
+ ast_assert(bridge->tech_pvt != NULL);
+
+ tech_pvt = bridge->tech_pvt;
+ if (!tech_pvt->connected) {
+ return;
+ }
+ tech_pvt->connected = 0;
+
+ /* Now to actually stop the bridge. */
+ native_stop(bridge);
+}
+
+/*!
+ * \internal
+ * \brief Start native bridging activity.
+ * \since 12.0.0
+ *
+ * \param bridge What to operate upon.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error. Could not start the bridge.
+ *
+ * \note On entry, bridge may or may not already be locked.
+ * However, it can be accessed as if it were locked.
+ */
+static int native_start(struct ast_bridge *bridge)
+{
+ struct native_pvt_bridge *tech_pvt;
+ struct ast_bridge_channel *bc0;
+ struct ast_bridge_channel *bc1;
+ struct native_pvt_chan *npc0;
+ struct native_pvt_chan *npc1;
+ struct ast_channel *c0;
+ struct ast_channel *c1;
+ struct dahdi_pvt *p0;
+ struct dahdi_pvt *p1;
+ struct dahdi_pvt *master;
+ struct dahdi_pvt *slave;
+ int inconf;
+ int nothing_ok;
+
+ ast_assert(bridge->tech_pvt != NULL);
+
+ bc0 = AST_LIST_FIRST(&bridge->channels);
+ bc1 = AST_LIST_LAST(&bridge->channels);
+ c0 = bc0->chan;
+ c1 = bc1->chan;
+
+ /* Lock channels and privates */
+ for (;;) {
+ ast_channel_lock(c0);
+ if (!ast_channel_trylock(c1)) {
+ p0 = ast_channel_tech_pvt(c0);
+ if (!ast_mutex_trylock(&p0->lock)) {
+ p1 = ast_channel_tech_pvt(c1);
+ if (!ast_mutex_trylock(&p1->lock)) {
+ /* Got all locks */
+ break;
+ }
+ ast_mutex_unlock(&p0->lock);
+ }
+ ast_channel_unlock(c1);
+ }
+ ast_channel_unlock(c0);
+ sched_yield();
+ }
+
+ npc0 = bc0->tech_pvt;
+ ast_assert(npc0 != NULL);
+ npc0->pvt = p0;
+ npc0->owner = p0->owner;
+ npc0->index = dahdi_get_index(c0, p0, 0);
+ npc0->fd0 = ast_channel_fd(c0, 0);
+ npc0->state = -1;
+ npc0->inthreeway = p0->subs[SUB_REAL].inthreeway;
+
+ npc1 = bc1->tech_pvt;
+ ast_assert(npc1 != NULL);
+ npc1->pvt = p1;
+ npc1->owner = p1->owner;
+ npc1->index = dahdi_get_index(c1, p1, 0);
+ npc1->fd0 = ast_channel_fd(c1, 0);
+ npc1->state = -1;
+ npc1->inthreeway = p1->subs[SUB_REAL].inthreeway;
+
+ /*
+ * Check things that can change on the privates while in native
+ * bridging and cause native to not activate.
+ */
+ if (npc0->index < 0 || npc1->index < 0
+#if defined(HAVE_PRI)
+ /*
+ * PRI nobch channels (hold and call waiting) are equivalent to
+ * pseudo channels and cannot be nativly bridged.
+ */
+ || (dahdi_sig_pri_lib_handles(p0->sig)
+ && ((struct sig_pri_chan *) p0->sig_pvt)->no_b_channel)
+ || (dahdi_sig_pri_lib_handles(p1->sig)
+ && ((struct sig_pri_chan *) p1->sig_pvt)->no_b_channel)
+#endif /* defined(HAVE_PRI) */
+ ) {
+ ast_mutex_unlock(&p0->lock);
+ ast_mutex_unlock(&p1->lock);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return -1;
+ }
+
+ inconf = 0;
+ nothing_ok = 1;
+ master = NULL;
+ slave = NULL;
+ if (npc0->index == SUB_REAL && npc1->index == SUB_REAL) {
+ if (p0->owner && p1->owner) {
+ /*
+ * If we don't have a call-wait in a 3-way, and we aren't in a
+ * 3-way, we can be master.
+ */
+ if (!p0->subs[SUB_CALLWAIT].inthreeway && !p1->subs[SUB_REAL].inthreeway) {
+ master = p0;
+ slave = p1;
+ inconf = 1;
+ } else if (!p1->subs[SUB_CALLWAIT].inthreeway && !p0->subs[SUB_REAL].inthreeway) {
+ master = p1;
+ slave = p0;
+ inconf = 1;
+ } else {
+ ast_log(LOG_WARNING, "Huh? Both calls are callwaits or 3-ways? That's clever...?\n");
+ ast_log(LOG_WARNING, "p0: chan %d/%d/CW%d/3W%d, p1: chan %d/%d/CW%d/3W%d\n",
+ p0->channel,
+ npc0->index, (p0->subs[SUB_CALLWAIT].dfd > -1) ? 1 : 0,
+ p0->subs[SUB_REAL].inthreeway,
+ p0->channel,
+ npc0->index, (p1->subs[SUB_CALLWAIT].dfd > -1) ? 1 : 0,
+ p1->subs[SUB_REAL].inthreeway);
+ }
+ nothing_ok = 0;
+ }
+ } else if (npc0->index == SUB_REAL && npc1->index == SUB_THREEWAY) {
+ if (p1->subs[SUB_THREEWAY].inthreeway) {
+ master = p1;
+ slave = p0;
+ nothing_ok = 0;
+ }
+ } else if (npc0->index == SUB_THREEWAY && npc1->index == SUB_REAL) {
+ if (p0->subs[SUB_THREEWAY].inthreeway) {
+ master = p0;
+ slave = p1;
+ nothing_ok = 0;
+ }
+ } else if (npc0->index == SUB_REAL && npc1->index == SUB_CALLWAIT) {
+ /*
+ * We have a real and a call wait. If we're in a three way
+ * call, put us in it, otherwise, don't put us in anything.
+ */
+ if (p1->subs[SUB_CALLWAIT].inthreeway) {
+ master = p1;
+ slave = p0;
+ nothing_ok = 0;
+ }
+ } else if (npc0->index == SUB_CALLWAIT && npc1->index == SUB_REAL) {
+ /* Same as previous */
+ if (p0->subs[SUB_CALLWAIT].inthreeway) {
+ master = p0;
+ slave = p1;
+ nothing_ok = 0;
+ }
+ }
+ ast_debug(3, "master: %d, slave: %d, nothing_ok: %d\n",
+ master ? master->channel : 0,
+ slave ? slave->channel : 0,
+ nothing_ok);
+ if (master && slave) {
+ /*
+ * Stop any tones, or play ringtone as appropriate. If they are
+ * bridged in an active threeway call with a channel that is
+ * ringing, we should indicate ringing.
+ */
+ if (npc1->index == SUB_THREEWAY
+ && p1->subs[SUB_THREEWAY].inthreeway
+ && p1->subs[SUB_REAL].owner
+ && p1->subs[SUB_REAL].inthreeway
+ && ast_channel_state(p1->subs[SUB_REAL].owner) == AST_STATE_RINGING) {
+ ast_debug(2,
+ "Playing ringback on %d/%d(%s) since %d/%d(%s) is in a ringing three-way\n",
+ p0->channel, npc0->index, ast_channel_name(c0),
+ p1->channel, npc1->index, ast_channel_name(c1));
+ tone_zone_play_tone(p0->subs[npc0->index].dfd, DAHDI_TONE_RINGTONE);
+ npc1->state = ast_channel_state(p1->subs[SUB_REAL].owner);
+ } else {
+ ast_debug(2, "Stopping tones on %d/%d(%s) talking to %d/%d(%s)\n",
+ p0->channel, npc0->index, ast_channel_name(c0),
+ p1->channel, npc1->index, ast_channel_name(c1));
+ tone_zone_play_tone(p0->subs[npc0->index].dfd, -1);
+ }
+
+ if (npc0->index == SUB_THREEWAY
+ && p0->subs[SUB_THREEWAY].inthreeway
+ && p0->subs[SUB_REAL].owner
+ && p0->subs[SUB_REAL].inthreeway
+ && ast_channel_state(p0->subs[SUB_REAL].owner) == AST_STATE_RINGING) {
+ ast_debug(2,
+ "Playing ringback on %d/%d(%s) since %d/%d(%s) is in a ringing three-way\n",
+ p1->channel, npc1->index, ast_channel_name(c1),
+ p0->channel, npc0->index, ast_channel_name(c0));
+ tone_zone_play_tone(p1->subs[npc1->index].dfd, DAHDI_TONE_RINGTONE);
+ npc0->state = ast_channel_state(p0->subs[SUB_REAL].owner);
+ } else {
+ ast_debug(2, "Stopping tones on %d/%d(%s) talking to %d/%d(%s)\n",
+ p1->channel, npc1->index, ast_channel_name(c1),
+ p0->channel, npc0->index, ast_channel_name(c0));
+ tone_zone_play_tone(p1->subs[npc1->index].dfd, -1);
+ }
+
+ if (npc0->index == SUB_REAL && npc1->index == SUB_REAL) {
+ if (!p0->echocanbridged || !p1->echocanbridged) {
+ /* Disable echo cancellation if appropriate */
+ dahdi_disable_ec(p0);
+ dahdi_disable_ec(p1);
+ }
+ }
+ dahdi_link(slave, master);
+ master->inconference = inconf;
+ } else if (!nothing_ok) {
+ ast_log(LOG_WARNING, "Can't link %d/%s with %d/%s\n",
+ p0->channel, subnames[npc0->index],
+ p1->channel, subnames[npc1->index]);
+ }
+ update_conf(p0);
+ update_conf(p1);
+
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+
+ /* Native bridge failed */
+ if ((!master || !slave) && !nothing_ok) {
+ ast_mutex_unlock(&p0->lock);
+ ast_mutex_unlock(&p1->lock);
+ return -1;
+ }
+
+ if (npc0->index == SUB_REAL) {
+ disable_dtmf_detect(p0);
+ }
+ if (npc1->index == SUB_REAL) {
+ disable_dtmf_detect(p1);
+ }
+
+ ast_mutex_unlock(&p0->lock);
+ ast_mutex_unlock(&p1->lock);
+
+ tech_pvt = bridge->tech_pvt;
+ tech_pvt->master = master;
+ tech_pvt->slave = slave;
+
+ ast_debug(2, "Start native bridging %s and %s\n",
+ ast_channel_name(c0), ast_channel_name(c1));
+
+#if defined(HAVE_PRI) && defined(PRI_2BCT)
+ if (!tech_pvt->tried_trombone_removal) {
+ tech_pvt->tried_trombone_removal = 1;
+
+ if (p0->pri && p0->pri == p1->pri && p0->pri->transfer) {
+ q931_call *q931_c0;
+ q931_call *q931_c1;
+
+ /* Try to eliminate the tromboned call. */
+ ast_mutex_lock(&p0->pri->lock);
+ ast_assert(dahdi_sig_pri_lib_handles(p0->sig));
+ ast_assert(dahdi_sig_pri_lib_handles(p1->sig));
+ q931_c0 = ((struct sig_pri_chan *) (p0->sig_pvt))->call;
+ q931_c1 = ((struct sig_pri_chan *) (p1->sig_pvt))->call;
+ if (q931_c0 && q931_c1) {
+ pri_channel_bridge(q931_c0, q931_c1);
+ ast_debug(2, "Attempt to eliminate tromboned call with %s and %s\n",
+ ast_channel_name(c0), ast_channel_name(c1));
+ }
+ ast_mutex_unlock(&p0->pri->lock);
+ }
+ }
+#endif /* defined(HAVE_PRI) && defined(PRI_2BCT) */
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Request to start native bridging activity.
+ * \since 12.0.0
+ *
+ * \param bridge What to operate upon.
+ *
+ * \return Nothing
+ *
+ * \note On entry, bridge may or may not already be locked.
+ * However, it can be accessed as if it were locked.
+ */
+static void native_request_start(struct ast_bridge *bridge)
+{
+ struct native_pvt_bridge *tech_pvt;
+ struct ast_bridge_channel *cur;
+
+ ast_assert(bridge->tech_pvt != NULL);
+
+ tech_pvt = bridge->tech_pvt;
+
+ if (bridge->num_channels != 2 || !tech_pvt->saw_start || tech_pvt->connected) {
+ return;
+ }
+ AST_LIST_TRAVERSE(&bridge->channels, cur, entry) {
+ if (cur->suspended || !cur->tech_pvt) {
+ return;
+ }
+ }
+
+ /* Actually try starting the native bridge. */
+ if (native_start(bridge)) {
+ return;
+ }
+ tech_pvt->connected = 1;
+}
+
+/*!
+ * \internal
+ * \brief Request a bridge technology instance start operations.
+ * \since 12.0.0
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * \note On entry, bridge may or may not already be locked.
+ * However, it can be accessed as if it were locked.
+ */
+static int native_bridge_start(struct ast_bridge *bridge)
+{
+ struct native_pvt_bridge *tech_pvt;
+
+ ast_assert(bridge->tech_pvt != NULL);
+
+ tech_pvt = bridge->tech_pvt;
+ tech_pvt->saw_start = 1;
+
+ native_request_start(bridge);
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Request a bridge technology instance stop in preparation for being destroyed.
+ * \since 12.0.0
+ *
+ * \note On entry, bridge is already locked.
+ */
+static void native_bridge_stop(struct ast_bridge *bridge)
+{
+ struct native_pvt_bridge *tech_pvt;
+
+ tech_pvt = bridge->tech_pvt;
+ if (!tech_pvt) {
+ return;
+ }
+
+ tech_pvt->saw_start = 0;
+ native_request_stop(bridge);
+}
+
+/*!
+ * \internal
+ * \brief Add a channel to a bridging technology instance for a bridge.
+ * \since 12.0.0
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * \note On entry, bridge is already locked.
+ */
+static int native_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ struct native_pvt_chan *tech_pvt;
+ struct ast_channel *c0;
+ struct ast_channel *c1;
+
+ ast_assert(!bridge_channel->tech_pvt);
+
+ tech_pvt = ast_calloc(1, sizeof(*tech_pvt));
+ if (!tech_pvt) {
+ return -1;
+ }
+
+ bridge_channel->tech_pvt = tech_pvt;
+ native_request_start(bridge);
+
+ c0 = AST_LIST_FIRST(&bridge->channels)->chan;
+ c1 = AST_LIST_LAST(&bridge->channels)->chan;
+ if (c0 != c1) {
+ /*
+ * Make the channels compatible in case the native bridge did
+ * not start for some reason and we need to fallback to 1-1
+ * bridging.
+ */
+ ast_channel_make_compatible(c0, c1);
+ }
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Remove a channel from a bridging technology instance for a bridge.
+ * \since 12.0.0
+ *
+ * \note On entry, bridge is already locked.
+ */
+static void native_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ struct native_pvt_chan *tech_pvt;
+
+ native_request_stop(bridge);
+
+ tech_pvt = bridge_channel->tech_pvt;
+ bridge_channel->tech_pvt = NULL;
+ ast_free(tech_pvt);
+}
+
+/*!
+ * \internal
+ * \brief Suspend a channel on a bridging technology instance for a bridge.
+ * \since 12.0.0
+ *
+ * \note On entry, bridge is already locked.
+ */
+static void native_bridge_suspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ native_request_stop(bridge);
+}
+
+/*!
+ * \internal
+ * \brief Unsuspend a channel on a bridging technology instance for a bridge.
+ * \since 12.0.0
+ *
+ * \note On entry, bridge is already locked.
+ */
+static void native_bridge_unsuspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ native_request_start(bridge);
+}
+
+/*!
+ * \internal
+ * \brief Check if channel is compatible.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Is this channel compatible.
+ *
+ * \retval TRUE if channel is compatible with native DAHDI bridge.
+ */
+static int native_bridge_is_capable(struct ast_bridge_channel *bridge_channel)
+{
+ struct ast_channel *chan = bridge_channel->chan;
+ struct dahdi_pvt *pvt;
+ int is_capable;
+
+ if (ao2_container_count(bridge_channel->features->dtmf_hooks)) {
+ ast_debug(2, "Channel '%s' has DTMF hooks.\n", ast_channel_name(chan));
+ return 0;
+ }
+
+ ast_channel_lock(chan);
+
+ if (dahdi_tech != ast_channel_tech(chan)) {
+ ast_debug(2, "Channel '%s' is not %s.\n",
+ ast_channel_name(chan), dahdi_tech->type);
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ if (ast_channel_has_audio_frame_or_monitor(chan)) {
+ ast_debug(2, "Channel '%s' has an active monitor, audiohook, or framehook.\n",
+ ast_channel_name(chan));
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ pvt = ast_channel_tech_pvt(chan);
+ if (!pvt || !pvt->sig) {
+ /* No private; or signaling is for a pseudo channel. */
+ ast_channel_unlock(chan);
+ return 0;
+ }
+
+ is_capable = 1;
+ ast_mutex_lock(&pvt->lock);
+
+ if (pvt->callwaiting && pvt->callwaitingcallerid) {
+ /*
+ * Call Waiting Caller ID requires DTMF detection to know if it
+ * can send the CID spill.
+ */
+ ast_debug(2, "Channel '%s' has call waiting caller ID enabled.\n",
+ ast_channel_name(chan));
+ is_capable = 0;
+ }
+
+ ast_mutex_unlock(&pvt->lock);
+ ast_channel_unlock(chan);
+
+ return is_capable;
+}
+
+/*!
+ * \internal
+ * \brief Check if a bridge is compatible with the bridging technology.
+ * \since 12.0.0
+ *
+ * \retval 0 if not compatible
+ * \retval non-zero if compatible
+ *
+ * \note On entry, bridge may or may not already be locked.
+ * However, it can be accessed as if it were locked.
+ */
+static int native_bridge_compatible(struct ast_bridge *bridge)
+{
+ struct ast_bridge_channel *cur;
+
+ /* We require two channels before even considering native bridging. */
+ if (bridge->num_channels != 2) {
+ ast_debug(1, "Bridge %s: Cannot use native DAHDI. Must have two channels.\n",
+ bridge->uniqueid);
+ return 0;
+ }
+
+ AST_LIST_TRAVERSE(&bridge->channels, cur, entry) {
+ if (!native_bridge_is_capable(cur)) {
+ ast_debug(1, "Bridge %s: Cannot use native DAHDI. Channel '%s' not compatible.\n",
+ bridge->uniqueid, ast_channel_name(cur->chan));
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/*!
+ * \internal
+ * \brief Check if something changed on the channel.
+ * \since 12.0.0
+ *
+ * \param bridge_channel What to operate upon.
+ *
+ * \retval 0 Nothing changed.
+ * \retval -1 Something changed.
+ *
+ * \note On entry, bridge_channel->bridge is already locked.
+ */
+static int native_chan_changed(struct ast_bridge_channel *bridge_channel)
+{
+ struct native_pvt_chan *tech_pvt;
+ struct ast_channel *chan;
+ struct dahdi_pvt *pvt;
+ int idx = -1;
+
+ ast_assert(bridge_channel->tech_pvt != NULL);
+
+ tech_pvt = bridge_channel->tech_pvt;
+
+ chan = bridge_channel->chan;
+ ast_channel_lock(chan);
+ pvt = ast_channel_tech_pvt(chan);
+ if (tech_pvt->pvt == pvt) {
+ idx = dahdi_get_index(chan, pvt, 1);
+ }
+ ast_channel_unlock(chan);
+
+ if (/* Did chan get masqueraded or PRI change associated B channel? */
+ tech_pvt->pvt != pvt
+ /* Did the pvt active owner change? */
+ || tech_pvt->owner != pvt->owner
+ /* Did the pvt three way call status change? */
+ || tech_pvt->inthreeway != pvt->subs[SUB_REAL].inthreeway
+ /* Did the owner index change? */
+ || tech_pvt->index != idx
+ /*
+ * Did chan file descriptor change? (This seems redundant with
+ * masquerade and active owner change checks.)
+ */
+ || tech_pvt->fd0 != ast_channel_fd(chan, 0)
+ /* Did chan state change? i.e. Did it stop ringing? */
+ || (pvt->subs[SUB_REAL].owner
+ && tech_pvt->state > -1
+ && tech_pvt->state != ast_channel_state(pvt->subs[SUB_REAL].owner))) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Check if something changed on the bridge channels.
+ * \since 12.0.0
+ *
+ * \param bridge What to operate upon.
+ *
+ * \retval 0 Nothing changed.
+ * \retval -1 Something changed.
+ *
+ * \note On entry, bridge is already locked.
+ */
+static int native_bridge_changed(struct ast_bridge *bridge)
+{
+ struct ast_bridge_channel *cur;
+
+ AST_LIST_TRAVERSE(&bridge->channels, cur, entry) {
+ if (native_chan_changed(cur)) {
+ ast_debug(1, "Bridge %s: Something changed on channel '%s'.\n",
+ bridge->uniqueid, ast_channel_name(cur->chan));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Write a frame into the bridging technology instance for a bridge.
+ * \since 12.0.0
+ *
+ * \note The bridge must be tolerant of bridge_channel being NULL.
+ *
+ * \retval 0 Frame accepted into the bridge.
+ * \retval -1 Frame needs to be deferred.
+ *
+ * \note On entry, bridge is already locked.
+ */
+static int native_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+ struct native_pvt_bridge *tech_pvt;
+
+ /*
+ * When we are not native bridged by DAHDI, we are like a normal
+ * 1-1 bridge.
+ */
+
+ ast_assert(bridge->tech_pvt != NULL);
+
+ /* Recheck native bridging validity. */
+ tech_pvt = bridge->tech_pvt;
+ switch (frame->frametype) {
+ case AST_FRAME_VOICE:
+ case AST_FRAME_VIDEO:
+ if (!tech_pvt->connected) {
+ /* Don't try to start native mode on media frames. */
+ break;
+ }
+ if (native_bridge_changed(bridge)) {
+ native_request_stop(bridge);
+ native_request_start(bridge);
+ if (!tech_pvt->connected) {
+ break;
+ }
+ }
+
+ /*
+ * Native bridge handles voice frames in hardware. However, it
+ * also passes the frames up to Asterisk anyway. Discard the
+ * media frames.
+ */
+ return 0;
+ default:
+ if (!tech_pvt->connected) {
+ native_request_start(bridge);
+ break;
+ }
+ if (native_bridge_changed(bridge)) {
+ native_request_stop(bridge);
+ native_request_start(bridge);
+ }
+ break;
+ }
+
+ return ast_bridge_queue_everyone_else(bridge, bridge_channel, frame);
+}
+
+static struct ast_bridge_technology native_bridge = {
+ .name = "native_dahdi",
+ .capabilities = AST_BRIDGE_CAPABILITY_NATIVE,
+ .preference = AST_BRIDGE_PREFERENCE_BASE_NATIVE,
+ .create = native_bridge_create,
+ .start = native_bridge_start,
+ .stop = native_bridge_stop,
+ .destroy = native_bridge_destroy,
+ .join = native_bridge_join,
+ .leave = native_bridge_leave,
+ .suspend = native_bridge_suspend,
+ .unsuspend = native_bridge_unsuspend,
+ .compatible = native_bridge_compatible,
+ .write = native_bridge_write,
+};
+
+/*!
+ * \internal
+ * \brief Destroy the DAHDI native bridge support.
+ * \since 12.0.0
+ *
+ * \return Nothing
+ */
+void dahdi_native_unload(void)
+{
+ ast_bridge_technology_unregister(&native_bridge);
+ ast_format_cap_destroy(native_bridge.format_capabilities);
+}
+
+/*!
+ * \internal
+ * \brief Initialize the DAHDI native bridge support.
+ * \since 12.0.0
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int dahdi_native_load(struct ast_module *mod, const struct ast_channel_tech *tech)
+{
+ struct ast_format format;
+
+ dahdi_tech = tech;
+
+ native_bridge.format_capabilities = ast_format_cap_alloc();
+ if (!native_bridge.format_capabilities) {
+ return -1;
+ }
+
+ /*
+ * This is used to make channels compatible with the bridge
+ * itself not with each other.
+ */
+ ast_format_cap_add(native_bridge.format_capabilities, ast_format_set(&format, AST_FORMAT_SLINEAR, 0));
+ ast_format_cap_add(native_bridge.format_capabilities, ast_format_set(&format, AST_FORMAT_ULAW, 0));
+ ast_format_cap_add(native_bridge.format_capabilities, ast_format_set(&format, AST_FORMAT_ALAW, 0));
+
+ return __ast_bridge_technology_register(&native_bridge, mod);
+}
diff --git a/channels/dahdi/bridge_native_dahdi.h b/channels/dahdi/bridge_native_dahdi.h
new file mode 100644
index 000000000..91e8d16b7
--- /dev/null
+++ b/channels/dahdi/bridge_native_dahdi.h
@@ -0,0 +1,47 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013 Digium, Inc.
+ *
+ * Richard Mudgett <rmudgett@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Native DAHDI bridging support.
+ *
+ * \author Richard Mudgett <rmudgett@digium.com>
+ *
+ * See Also:
+ * \arg \ref AstCREDITS
+ */
+
+#ifndef _ASTERISK_BRIDGE_NATIVE_DAHDI_H
+#define _ASTERISK_BRIDGE_NATIVE_DAHDI_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/* ------------------------------------------------------------------- */
+
+void dahdi_native_unload(void);
+int dahdi_native_load(struct ast_module *mod, const struct ast_channel_tech *tech);
+
+/* ------------------------------------------------------------------- */
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_BRIDGE_NATIVE_DAHDI_H */