[svn-commits] eliel: branch group/data_api_gsoc2009 r200145 - /team/group/data_api_gsoc2009...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Jun 11 14:59:13 CDT 2009


Author: eliel
Date: Thu Jun 11 14:59:10 2009
New Revision: 200145

URL: http://svn.asterisk.org/svn-view/asterisk?view=rev&rev=200145
Log:
I missed this files in my last manual merge.

Added:
    team/group/data_api_gsoc2009/channels/sig_analog.c   (with props)
    team/group/data_api_gsoc2009/channels/sig_analog.h   (with props)

Added: team/group/data_api_gsoc2009/channels/sig_analog.c
URL: http://svn.asterisk.org/svn-view/asterisk/team/group/data_api_gsoc2009/channels/sig_analog.c?view=auto&rev=200145
==============================================================================
--- team/group/data_api_gsoc2009/channels/sig_analog.c (added)
+++ team/group/data_api_gsoc2009/channels/sig_analog.c Thu Jun 11 14:59:10 2009
@@ -1,0 +1,3262 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2009, Digium, Inc.
+ *
+ * Mark Spencer <markster at 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 Analog signaling module
+ *
+ * \author Matthew Fredrickson <creslin at digium.com>
+ */
+
+#include "asterisk.h"
+
+#include <errno.h>
+#include <ctype.h>
+
+#include "asterisk/utils.h"
+#include "asterisk/options.h"
+#include "asterisk/pbx.h"
+#include "asterisk/file.h"
+#include "asterisk/callerid.h"
+#include "asterisk/say.h"
+#include "asterisk/manager.h"
+#include "asterisk/astdb.h"
+#include "asterisk/features.h"
+
+#include "sig_analog.h"
+
+#define POLARITY_IDLE 0
+#define POLARITY_REV    1
+#define MIN_MS_SINCE_FLASH			( (2000) )	/*!< 2000 ms */
+static int analog_matchdigittimeout = 3000;
+static int analog_gendigittimeout = 8000;
+static int analog_firstdigittimeout = 16000;
+static char analog_defaultcic[64] = "";
+static char analog_defaultozz[64] = "";
+
+static const struct {
+	enum analog_sigtype sigtype;
+	const char const *name;
+} sigtypes[] = {
+	{ ANALOG_SIG_FXOLS, "fxo_ls" },
+	{ ANALOG_SIG_FXOKS, "fxo_ks" },
+	{ ANALOG_SIG_FXOGS, "fxo_gs" },
+	{ ANALOG_SIG_FXSLS, "fxs_ls" },
+	{ ANALOG_SIG_FXSKS, "fxs_ks" },
+	{ ANALOG_SIG_FXSGS, "fxs_gs" },
+	{ ANALOG_SIG_EMWINK, "em_w" },
+	{ ANALOG_SIG_EM, "em" },
+	{ ANALOG_SIG_EM_E1, "em_e1" },
+	{ ANALOG_SIG_FEATD, "featd" },
+	{ ANALOG_SIG_FEATDMF, "featdmf" },
+	{ ANALOG_SIG_FEATDMF_TA, "featdmf_ta" },
+	{ ANALOG_SIG_FEATB, "featb" },
+	{ ANALOG_SIG_FGC_CAMA, "fgccama" },
+	{ ANALOG_SIG_FGC_CAMAMF, "fgccamamf" },
+	{ ANALOG_SIG_SF, "sf" },
+	{ ANALOG_SIG_SFWINK, "sf_w" },
+	{ ANALOG_SIG_SF_FEATD, "sf_featd" },
+	{ ANALOG_SIG_SF_FEATDMF, "sf_featdmf" },
+	{ ANALOG_SIG_SF_FEATB, "sf_featb" },
+	{ ANALOG_SIG_E911, "e911" },
+};
+
+static const struct {
+	unsigned int cid_type;
+	const char const *name;
+} cidtypes[] = {
+	{ CID_SIG_BELL,   "bell" },
+	{ CID_SIG_V23,    "v23" },
+	{ CID_SIG_V23_JP, "v23_jp" },
+	{ CID_SIG_DTMF,   "dtmf" },
+	/* "smdi" is intentionally not supported here, as there is a much better
+	 * way to do this in the dialplan now. */
+};
+
+enum analog_sigtype analog_str_to_sigtype(const char *name)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_LEN(sigtypes); i++) {
+		if (!strcasecmp(sigtypes[i].name, name)) {
+			return sigtypes[i].sigtype;
+		}
+	}
+
+	return 0;
+}
+
+const char *analog_sigtype_to_str(enum analog_sigtype sigtype)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_LEN(sigtypes); i++) {
+		if (sigtype == sigtypes[i].sigtype) {
+			return sigtypes[i].name;
+		}
+	}
+
+	return "Unknown";
+}
+
+unsigned int analog_str_to_cidtype(const char *name)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_LEN(cidtypes); i++) {
+		if (!strcasecmp(cidtypes[i].name, name)) {
+			return cidtypes[i].cid_type;
+		}
+	}
+
+	return 0;
+}
+
+const char *analog_cidtype_to_str(unsigned int cid_type)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_LEN(cidtypes); i++) {
+		if (cid_type == cidtypes[i].cid_type) {
+			return cidtypes[i].name;
+		}
+	}
+
+	return "Unknown";
+}
+
+static int analog_start_cid_detect(struct analog_pvt *p, int cid_signalling)
+{
+	if (p->calls->start_cid_detect)
+		return p->calls->start_cid_detect(p->chan_pvt, cid_signalling);
+	else
+		return -1;
+}
+
+static int analog_stop_cid_detect(struct analog_pvt *p)
+{
+	if (p->calls->stop_cid_detect)
+		return p->calls->stop_cid_detect(p->chan_pvt);
+	else
+		return -1;
+}
+
+static int analog_get_callerid(struct analog_pvt *p, char *name, char *number, enum analog_event *ev, size_t timeout)
+{
+	if (p->calls->get_callerid)
+		return p->calls->get_callerid(p->chan_pvt, name, number, ev, timeout);
+	else
+		return -1;
+}
+
+static int analog_get_event(struct analog_pvt *p)
+{
+	if (p->calls->get_event)
+		return p->calls->get_event(p->chan_pvt);
+	else
+		return -1;
+}
+
+static int analog_wait_event(struct analog_pvt *p)
+{
+	if (p->calls->wait_event)
+		return p->calls->wait_event(p->chan_pvt);
+	else
+		return -1;
+}
+
+enum analog_cid_start analog_str_to_cidstart(const char *value)
+{
+	if (!strcasecmp(value, "ring")) {
+		return ANALOG_CID_START_RING;
+	} else if (!strcasecmp(value, "polarity")) {
+		return ANALOG_CID_START_POLARITY;
+	} else if (!strcasecmp(value, "polarity_in")) {
+		return ANALOG_CID_START_POLARITY_IN;
+	}
+
+	return 0;
+}
+
+const char *analog_cidstart_to_str(enum analog_cid_start cid_start)
+{
+	switch (cid_start) {
+	case ANALOG_CID_START_RING:
+		return "Ring";
+	case ANALOG_CID_START_POLARITY:
+		return "Polarity";
+	case ANALOG_CID_START_POLARITY_IN:
+		return "Polarity_In";
+	}
+
+	return "Unknown";
+}
+
+static char *analog_event2str(enum analog_event event)
+{
+	char *res;
+	switch (event) {
+	case ANALOG_EVENT_DIALCOMPLETE:
+		res = "ANALOG_EVENT_DIALCOMPLETE";
+		break;
+	case ANALOG_EVENT_WINKFLASH:
+		res = "ANALOG_EVENT_WINKFLASH";
+		break;
+	case ANALOG_EVENT_ONHOOK:
+		res = "ANALOG_EVENT_ONHOOK";
+		break;
+	case ANALOG_EVENT_RINGOFFHOOK:
+		res = "ANALOG_EVENT_RINGOFFHOOK";
+		break;
+	case ANALOG_EVENT_ALARM:
+		res = "ANALOG_EVENT_ALARM";
+		break;
+	case ANALOG_EVENT_NOALARM:
+		res = "ANALOG_EVENT_NOALARM";
+		break;
+	case ANALOG_EVENT_HOOKCOMPLETE:
+		res = "ANALOG_EVENT_HOOKCOMPLETE";
+		break;
+	case ANALOG_EVENT_POLARITY:
+		res = "ANALOG_EVENT_POLARITY";
+		break;
+	case ANALOG_EVENT_RINGERON:
+		res = "ANALOG_EVENT_RINGERON";
+		break;
+	case ANALOG_EVENT_RINGEROFF:
+		res = "ANALOG_EVENT_RINGEROFF";
+		break;
+	case ANALOG_EVENT_RINGBEGIN:
+		res = "ANALOG_EVENT_RINGBEGIN";
+		break;
+	case ANALOG_EVENT_PULSE_START:
+		res = "ANALOG_EVENT_PULSE_START";
+		break;
+	case ANALOG_EVENT_NEONMWI_ACTIVE:
+		res = "ANALOG_EVENT_NEONMWI_ACTIVE";
+		break;
+	case ANALOG_EVENT_NEONMWI_INACTIVE:
+		res = "ANALOG_EVENT_NEONMWI_INACTIVE";
+		break;
+	default:
+		res = "UNKNOWN/OTHER";
+		break;
+	}
+
+	return res;
+}
+
+static void analog_swap_subs(struct analog_pvt *p, enum analog_sub a, enum analog_sub b)
+{
+	int tinthreeway;
+	struct ast_channel *towner;
+
+	ast_debug(1, "Swapping %d and %d\n", a, b);
+
+	towner = p->subs[a].owner;
+	tinthreeway = p->subs[a].inthreeway;
+
+	p->subs[a].owner = p->subs[b].owner;
+	p->subs[a].inthreeway = p->subs[b].inthreeway;
+
+	p->subs[b].owner = towner;
+	p->subs[b].inthreeway = tinthreeway;
+
+	if (p->calls->swap_subs)
+		p->calls->swap_subs(p->chan_pvt, a, p->subs[a].owner, b, p->subs[b].owner);
+
+}
+
+static int analog_alloc_sub(struct analog_pvt *p, enum analog_sub x)
+{
+	p->subs[x].allocd = 1;
+	if (p->calls->allocate_sub)
+		return p->calls->allocate_sub(p->chan_pvt, x);
+
+	return 0;
+}
+
+static int analog_unalloc_sub(struct analog_pvt *p, enum analog_sub x)
+{
+	p->subs[x].allocd = 0;
+	p->subs[x].owner = NULL;
+	if (p->calls->unallocate_sub)
+		return p->calls->unallocate_sub(p->chan_pvt, x);
+
+	return 0;
+}
+
+static int analog_send_callerid(struct analog_pvt *p, int cwcid, struct ast_callerid *cid)
+{
+	ast_debug(1, "Sending callerid.  CID_NAME: '%s' CID_NUM: '%s'\n", cid->cid_name, cid->cid_num);
+
+	if (cwcid) {
+		p->callwaitcas = 0;
+	}
+
+	if (p->calls->send_callerid)
+		return p->calls->send_callerid(p->chan_pvt, cwcid, cid);
+	else
+		return 0;
+}
+
+static int analog_get_index(struct ast_channel *ast, struct analog_pvt *p, int nullok)
+{
+	int res;
+	if (p->subs[ANALOG_SUB_REAL].owner == ast)
+		res = ANALOG_SUB_REAL;
+	else if (p->subs[ANALOG_SUB_CALLWAIT].owner == ast)
+		res = ANALOG_SUB_CALLWAIT;
+	else if (p->subs[ANALOG_SUB_THREEWAY].owner == ast)
+		res = ANALOG_SUB_THREEWAY;
+	else {
+		res = -1;
+		if (!nullok)
+			ast_log(LOG_WARNING, "Unable to get index, and nullok is not asserted\n");
+	}
+	return res;
+}
+
+static int analog_dsp_reset_and_flush_digits(struct analog_pvt *p)
+{
+	if (p->calls->dsp_reset_and_flush_digits)
+		return p->calls->dsp_reset_and_flush_digits(p->chan_pvt);
+	else
+		/* Return 0 since I think this is unnecessary to do in most cases it is used.  Mostly only for ast_dsp */
+		return 0;
+}
+
+static int analog_play_tone(struct analog_pvt *p, enum analog_sub sub, enum analog_tone tone)
+{
+	if (p->calls->play_tone)
+		return p->calls->play_tone(p->chan_pvt, sub, tone);
+	else
+		return -1;
+}
+
+static struct ast_channel * analog_new_ast_channel(struct analog_pvt *p, int state, int startpbx, enum analog_sub sub)
+{
+	struct ast_channel *c;
+
+	if (p->calls->new_ast_channel)
+		c = p->calls->new_ast_channel(p->chan_pvt, state, startpbx, sub);
+	else
+		return NULL;
+
+	p->subs[sub].owner = c;
+	if (!p->owner)
+		p->owner = c;
+	return c;
+}
+
+static int analog_set_echocanceller(struct analog_pvt *p, int enable)
+{
+	if (p->calls->set_echocanceller)
+		return p->calls->set_echocanceller(p->chan_pvt, enable);
+	else
+		return -1;
+}
+
+static int analog_train_echocanceller(struct analog_pvt *p)
+{
+	if (p->calls->train_echocanceller)
+		return p->calls->train_echocanceller(p->chan_pvt);
+	else
+		return -1;
+}
+
+static int analog_is_off_hook(struct analog_pvt *p)
+{
+	if (p->calls->is_off_hook)
+		return p->calls->is_off_hook(p->chan_pvt);
+	else
+		return -1;
+}
+
+static int analog_ring(struct analog_pvt *p)
+{
+	if (p->calls->ring)
+		return p->calls->ring(p->chan_pvt);
+	else
+		return -1;
+}
+
+static int analog_start(struct analog_pvt *p)
+{
+	if (p->calls->start)
+		return p->calls->start(p->chan_pvt);
+	else
+		return -1;
+}
+
+static int analog_dial_digits(struct analog_pvt *p, enum analog_sub sub, struct analog_dialoperation *dop)
+{
+	if (p->calls->dial_digits)
+		return p->calls->dial_digits(p->chan_pvt, sub, dop);
+	else
+		return -1;
+}
+
+static int analog_on_hook(struct analog_pvt *p)
+{
+	if (p->calls->on_hook)
+		return p->calls->on_hook(p->chan_pvt);
+	else
+		return -1;
+}
+
+static int analog_check_for_conference(struct analog_pvt *p)
+{
+	if (p->calls->check_for_conference)
+		return p->calls->check_for_conference(p->chan_pvt);
+	else
+		return -1;
+}
+
+static void analog_all_subchannels_hungup(struct analog_pvt *p)
+{
+	if (p->calls->all_subchannels_hungup)
+		p->calls->all_subchannels_hungup(p->chan_pvt);
+}
+
+static void analog_unlock_private(struct analog_pvt *p)
+{
+	if (p->calls->unlock_private)
+		p->calls->unlock_private(p->chan_pvt);
+}
+
+static void analog_lock_private(struct analog_pvt *p)
+{
+	if (p->calls->lock_private)
+		p->calls->lock_private(p->chan_pvt);
+}
+
+static int analog_off_hook(struct analog_pvt *p)
+{
+	if (p->calls->off_hook)
+		return p->calls->off_hook(p->chan_pvt);
+	else
+		return -1;
+}
+
+static int analog_dsp_set_digitmode(struct analog_pvt *p, enum analog_dsp_digitmode mode)
+{
+	if (p->calls->dsp_set_digitmode)
+		return p->calls->dsp_set_digitmode(p->chan_pvt, mode);
+	else
+		return -1;
+}
+
+static void analog_cb_handle_dtmfup(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest)
+{
+	if (p->calls->handle_dtmfup)
+		p->calls->handle_dtmfup(p->chan_pvt, ast, analog_index, dest);
+}
+
+static int analog_wink(struct analog_pvt *p, enum analog_sub index)
+{
+	if (p->calls->wink)
+		return p->calls->wink(p->chan_pvt, index);
+	else
+		return -1;
+}
+
+static int analog_has_voicemail(struct analog_pvt *p)
+{
+	if (p->calls->has_voicemail)
+		return p->calls->has_voicemail(p->chan_pvt);
+	else
+		return -1;
+}
+
+static int analog_is_dialing(struct analog_pvt *p, enum analog_sub index)
+{
+	if (p->calls->is_dialing)
+		return p->calls->is_dialing(p->chan_pvt, index);
+	else
+		return -1;
+}
+
+static int analog_attempt_transfer(struct analog_pvt *p)
+{
+	/* In order to transfer, we need at least one of the channels to
+	   actually be in a call bridge.  We can't conference two applications
+	   together (but then, why would we want to?) */
+	if (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)) {
+		/* The three-way person we're about to transfer to could still be in MOH, so
+		   stop if now if appropriate */
+		if (ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner))
+			ast_queue_control(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CONTROL_UNHOLD);
+		if (p->subs[ANALOG_SUB_REAL].owner->_state == AST_STATE_RINGING) {
+			ast_indicate(ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner), AST_CONTROL_RINGING);
+		}
+		if (p->subs[ANALOG_SUB_THREEWAY].owner->_state == AST_STATE_RING) {
+			analog_play_tone(p, ANALOG_SUB_THREEWAY, ANALOG_TONE_RINGTONE);
+		}
+		 if (ast_channel_masquerade(p->subs[ANALOG_SUB_THREEWAY].owner, ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner))) {
+			ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+					ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)->name, p->subs[ANALOG_SUB_THREEWAY].owner->name);
+			return -1;
+		}
+		/* Orphan the channel after releasing the lock */
+		ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
+		analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
+	} else if (ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) {
+		ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD);
+		if (p->subs[ANALOG_SUB_THREEWAY].owner->_state == AST_STATE_RINGING) {
+			ast_indicate(ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner), AST_CONTROL_RINGING);
+		}
+		if (p->subs[ANALOG_SUB_REAL].owner->_state == AST_STATE_RING) {
+			analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_RINGTONE);
+		}
+		if (ast_channel_masquerade(p->subs[ANALOG_SUB_REAL].owner, ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner))) {
+			ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+					ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)->name, p->subs[ANALOG_SUB_REAL].owner->name);
+			return -1;
+		}
+		/* Three-way is now the REAL */
+		analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
+		ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
+		analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
+		/* Tell the caller not to hangup */
+		return 1;
+	} else {
+		ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n",
+					p->subs[ANALOG_SUB_REAL].owner->name, p->subs[ANALOG_SUB_THREEWAY].owner->name);
+		ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
+		return -1;
+	}
+	return 0;
+}
+
+static int analog_update_conf(struct analog_pvt *p)
+{
+	int x;
+	int needconf = 0;
+
+	/* Start with the obvious, general stuff */
+	for (x = 0; x < 3; x++) {
+		/* Look for three way calls */
+		if ((p->subs[x].allocd) && p->subs[x].inthreeway) {
+			if (p->calls->conf_add)
+				p->calls->conf_add(p->chan_pvt, x);
+			needconf++;
+		} else {
+			if (p->calls->conf_del)
+				p->calls->conf_del(p->chan_pvt, x);
+		}
+	}
+	ast_debug(1, "Updated conferencing on %d, with %d conference users\n", p->channel, needconf);
+
+	if (p->calls->complete_conference_update)
+		p->calls->complete_conference_update(p->chan_pvt, needconf);
+	return 0;
+}
+
+struct ast_channel * analog_request(struct analog_pvt *p, int *callwait)
+{
+	ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
+	*callwait = (p->owner != NULL);
+
+	if (p->owner) {
+		if (analog_alloc_sub(p, ANALOG_SUB_CALLWAIT)) {
+			ast_log(LOG_ERROR, "Unable to alloc subchannel\n");
+			return NULL;
+		}
+	}
+
+	return analog_new_ast_channel(p, AST_STATE_RESERVED, 0, p->owner ? ANALOG_SUB_CALLWAIT : ANALOG_SUB_REAL);
+}
+
+int analog_available(struct analog_pvt *p, int channelmatch, ast_group_t groupmatch, int *busy, int *channelmatched, int *groupmatched)
+{
+	int offhook;
+
+	ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
+	/* We're at least busy at this point */
+	if (busy) {
+		if ((p->sig == ANALOG_SIG_FXOKS) || (p->sig == ANALOG_SIG_FXOLS) || (p->sig == ANALOG_SIG_FXOGS))
+			*busy = 1;
+	}
+	/* If do not disturb, definitely not */
+	if (p->dnd)
+		return 0;
+	/* If guard time, definitely not */
+	if (p->guardtime && (time(NULL) < p->guardtime)) 
+		return 0;
+
+	/* If no owner definitely available */
+	if (!p->owner) {
+		if (p->sig == ANALOG_SIG_FXSLS)
+			return 1;
+
+		offhook = analog_is_off_hook(p);
+
+		if ((p->sig == ANALOG_SIG_FXSKS) || (p->sig == ANALOG_SIG_FXSGS)) {
+			/* When "onhook" that means no battery on the line, and thus
+			  it is out of service..., if it's on a TDM card... If it's a channel
+			  bank, there is no telling... */
+			if (offhook)
+				return 1;
+			else
+				return 0;
+		} else if (offhook) {
+			ast_debug(1, "Channel %d off hook, can't use\n", p->channel);
+			/* Not available when the other end is off hook */
+			return 0;
+		}
+		return 1;
+	}
+
+	/* If it's not an FXO, forget about call wait */
+	if ((p->sig != ANALOG_SIG_FXOKS) && (p->sig != ANALOG_SIG_FXOLS) && (p->sig != ANALOG_SIG_FXOGS)) 
+		return 0;
+
+	if (!p->callwaiting) {
+		/* If they don't have call waiting enabled, then for sure they're unavailable at this point */
+		return 0;
+	}
+
+	if (p->subs[ANALOG_SUB_CALLWAIT].allocd) {
+		/* If there is already a call waiting call, then we can't take a second one */
+		return 0;
+	}
+
+	if ((p->owner->_state != AST_STATE_UP) &&
+	    ((p->owner->_state != AST_STATE_RINGING) || p->outgoing)) {
+		/* If the current call is not up, then don't allow the call */
+		return 0;
+	}
+	if ((p->subs[ANALOG_SUB_THREEWAY].owner) && (!p->subs[ANALOG_SUB_THREEWAY].inthreeway)) {
+		/* Can't take a call wait when the three way calling hasn't been merged yet. */
+		return 0;
+	}
+	/* We're cool */
+	return 1;
+}
+
+static int analog_stop_callwait(struct analog_pvt *p)
+{
+	if (p->callwaitingcallerid)
+		p->callwaitcas = 0;
+
+	if (p->calls->stop_callwait)
+		return p->calls->stop_callwait(p->chan_pvt);
+	else
+		return 0;
+}
+
+static int analog_callwait(struct analog_pvt *p)
+{
+	if (p->callwaitingcallerid)
+		p->callwaitcas = 1;
+	if (p->calls->callwait)
+		return p->calls->callwait(p->chan_pvt);
+	else
+		return 0;
+}
+
+int analog_call(struct analog_pvt *p, struct ast_channel *ast, char *rdest, int timeout)
+{
+	int res, index,mysig;
+	char *c, *n, *l;
+	char dest[256]; /* must be same length as p->dialdest */
+
+	ast_log(LOG_DEBUG, "CALLING CID_NAME: %s CID_NUM:: %s\n", ast->connected.id.name, ast->connected.id.number);
+
+	ast_copy_string(dest, rdest, sizeof(dest));
+	ast_copy_string(p->dialdest, rdest, sizeof(p->dialdest));
+
+	if ((ast->_state == AST_STATE_BUSY)) {
+		ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_BUSY);
+		return 0;
+	}
+
+	if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+		ast_log(LOG_WARNING, "analog_call called on %s, neither down nor reserved\n", ast->name);
+		return -1;
+	}
+
+	p->dialednone = 0;
+
+	mysig = p->sig;
+	if (p->outsigmod > -1)
+		mysig = p->outsigmod;
+
+	switch (mysig) {
+	case ANALOG_SIG_FXOLS:
+	case ANALOG_SIG_FXOGS:
+	case ANALOG_SIG_FXOKS:
+		if (p->owner == ast) {
+			/* Normal ring, on hook */
+
+			/* Don't send audio while on hook, until the call is answered */
+			p->dialing = 1;
+			/* XXX */
+#if 0
+			/* Choose proper cadence */
+			if ((p->distinctivering > 0) && (p->distinctivering <= num_cadence)) {
+				if (ioctl(p->subs[ANALOG_SUB_REAL].dfd, DAHDI_SETCADENCE, &cadences[p->distinctivering - 1]))
+					ast_log(LOG_WARNING, "Unable to set distinctive ring cadence %d on '%s': %s\n", p->distinctivering, ast->name, strerror(errno));
+				p->cidrings = cidrings[p->distinctivering - 1];
+			} else {
+				if (ioctl(p->subs[ANALOG_SUB_REAL].dfd, DAHDI_SETCADENCE, NULL))
+					ast_log(LOG_WARNING, "Unable to reset default ring on '%s': %s\n", ast->name, strerror(errno));
+				p->cidrings = p->sendcalleridafter;
+			}
+#endif
+			p->cidrings = p->sendcalleridafter;
+
+			/* nick at dccinc.com 4/3/03 mods to allow for deferred dialing */
+			c = strchr(dest, '/');
+			if (c)
+				c++;
+			if (c && (strlen(c) < p->stripmsd)) {
+				ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd);
+				c = NULL;
+			}
+			if (c) {
+				p->dop.op = ANALOG_DIAL_OP_REPLACE;
+				snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "Tw%s", c);
+				ast_debug(1, "FXO: setup deferred dialstring: %s\n", c);
+			} else {
+				p->dop.dialstr[0] = '\0';
+			}
+
+			if (analog_ring(p)) {
+				ast_log(LOG_WARNING, "Unable to ring phone: %s\n", strerror(errno));
+				return -1;
+			}
+			p->dialing = 1;
+		} else {
+			if (ast->connected.id.number)
+				ast_copy_string(p->callwait_num, ast->connected.id.number, sizeof(p->callwait_num));
+			else
+				p->callwait_num[0] = '\0';
+			if (ast->connected.id.name)
+				ast_copy_string(p->callwait_name, ast->connected.id.name, sizeof(p->callwait_name));
+			else
+				p->callwait_name[0] = '\0';
+
+			/* Call waiting tone instead */
+			if (analog_callwait(p)) {
+				return -1;
+			}
+			/* Make ring-back */
+			if (analog_play_tone(p, ANALOG_SUB_CALLWAIT, ANALOG_TONE_RINGTONE))
+				ast_log(LOG_WARNING, "Unable to generate call-wait ring-back on channel %s\n", ast->name);
+
+		}
+		n = ast->connected.id.name;
+		l = ast->connected.id.number;
+		if (l)
+			ast_copy_string(p->lastcid_num, l, sizeof(p->lastcid_num));
+		else
+			p->lastcid_num[0] = '\0';
+		if (n)
+			ast_copy_string(p->lastcid_name, n, sizeof(p->lastcid_name));
+		else
+			p->lastcid_name[0] = '\0';
+
+		if (p->use_callerid) {
+			//p->callwaitcas = 0;
+			p->cid.cid_name = p->lastcid_name;
+			p->cid.cid_num = p->lastcid_num;
+		}
+
+
+		ast_setstate(ast, AST_STATE_RINGING);
+		index = analog_get_index(ast, p, 0);
+		if (index > -1) {
+			ast_queue_control(p->subs[index].owner, AST_CONTROL_RINGING);
+		}
+		break;
+	case ANALOG_SIG_FXSLS:
+	case ANALOG_SIG_FXSGS:
+	case ANALOG_SIG_FXSKS:
+	case ANALOG_SIG_EMWINK:
+	case ANALOG_SIG_EM:
+	case ANALOG_SIG_EM_E1:
+	case ANALOG_SIG_FEATD:
+	case ANALOG_SIG_FEATDMF:
+	case ANALOG_SIG_E911:
+	case ANALOG_SIG_FGC_CAMA:
+	case ANALOG_SIG_FGC_CAMAMF:
+	case ANALOG_SIG_FEATB:
+	case ANALOG_SIG_SFWINK:
+	case ANALOG_SIG_SF:
+	case ANALOG_SIG_SF_FEATD:
+	case ANALOG_SIG_SF_FEATDMF:
+	case ANALOG_SIG_FEATDMF_TA:
+	case ANALOG_SIG_SF_FEATB:
+		c = strchr(dest, '/');
+		if (c)
+			c++;
+		else
+			c = "";
+		if (strlen(c) < p->stripmsd) {
+			ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd);
+			return -1;
+		}
+		res = analog_start(p);
+		if (res < 0) {
+			if (errno != EINPROGRESS) {
+				return -1;
+			}
+		}
+		ast_log(LOG_DEBUG, "Dialing '%s'\n", c);
+		p->dop.op = ANALOG_DIAL_OP_REPLACE;
+
+		c += p->stripmsd;
+
+		switch (mysig) {
+		case ANALOG_SIG_FEATD:
+			l = ast->connected.id.number;
+			if (l) 
+				snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T*%s*%s*", l, c);
+			else
+				snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T**%s*", c);
+			break;
+		case ANALOG_SIG_FEATDMF:
+			l = ast->connected.id.number;
+			if (l) 
+				snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*00%s#*%s#", l, c);
+			else
+				snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*02#*%s#", c);
+			break;
+		case ANALOG_SIG_FEATDMF_TA:
+		{
+			const char *cic = "", *ozz = "";
+
+			/* If you have to go through a Tandem Access point you need to use this */
+#ifndef STANDALONE
+			ozz = pbx_builtin_getvar_helper(p->owner, "FEATDMF_OZZ");
+			if (!ozz)
+				ozz = analog_defaultozz;
+			cic = pbx_builtin_getvar_helper(p->owner, "FEATDMF_CIC");
+			if (!cic)
+				cic = analog_defaultcic;
+#endif
+			if (!ozz || !cic) {
+				ast_log(LOG_WARNING, "Unable to dial channel of type feature group D MF tandem access without CIC or OZZ set\n");
+				return -1;
+			}
+			snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%s%s#", ozz, cic);
+			snprintf(p->finaldial, sizeof(p->finaldial), "M*%s#", c);
+			p->whichwink = 0;
+		}
+			break;
+		case ANALOG_SIG_E911:
+			ast_copy_string(p->dop.dialstr, "M*911#", sizeof(p->dop.dialstr));
+			break;
+		case ANALOG_SIG_FGC_CAMA:
+			snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "P%s", c);
+			break;
+		case ANALOG_SIG_FGC_CAMAMF:
+		case ANALOG_SIG_FEATB:
+			snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%s#", c);
+			break;
+		default:
+			if (p->pulse)
+				snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "P%sw", c);
+			else
+				snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T%sw", c);
+			break;
+		}
+
+		if (p->echotraining && (strlen(p->dop.dialstr) > 4)) {
+			memset(p->echorest, 'w', sizeof(p->echorest) - 1);
+			strcpy(p->echorest + (p->echotraining / 400) + 1, p->dop.dialstr + strlen(p->dop.dialstr) - 2);
+			p->echorest[sizeof(p->echorest) - 1] = '\0';
+			p->echobreak = 1;
+			p->dop.dialstr[strlen(p->dop.dialstr)-2] = '\0';
+		} else
+			p->echobreak = 0;
+		if (!res) {
+			if (analog_dial_digits(p, ANALOG_SUB_REAL, &p->dop)) {
+				int saveerr = errno;
+
+				analog_on_hook(p);
+				ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(saveerr));
+				return -1;
+			}
+		} else
+			ast_debug(1, "Deferring dialing...\n");
+		p->dialing = 1;
+		if (ast_strlen_zero(c))
+			p->dialednone = 1;
+		ast_setstate(ast, AST_STATE_DIALING);
+		break;
+	default:
+		ast_debug(1, "not yet implemented\n");
+		return -1;
+	}
+	return 0;
+}
+
+int analog_hangup(struct analog_pvt *p, struct ast_channel *ast)
+{
+	int res;
+	int index, x;
+
+	ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
+	if (!ast->tech_pvt) {
+		ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+		return 0;
+	}
+
+	index = analog_get_index(ast, p, 1);
+
+	x = 0;
+	if (p->origcid_num) {
+		ast_copy_string(p->cid_num, p->origcid_num, sizeof(p->cid_num));
+		free(p->origcid_num);
+		p->origcid_num = NULL;
+	}
+	if (p->origcid_name) {
+		ast_copy_string(p->cid_name, p->origcid_name, sizeof(p->cid_name));
+		free(p->origcid_name);
+		p->origcid_name = NULL;
+	}
+
+	analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_DTMF);
+
+	ast_debug(1, "Hangup: channel: %d index = %d, normal = %d, callwait = %d, thirdcall = %d\n",
+		p->channel, index, p->subs[ANALOG_SUB_REAL].allocd, p->subs[ANALOG_SUB_CALLWAIT].allocd, p->subs[ANALOG_SUB_THREEWAY].allocd);
+	if (index > -1) {
+		/* Real channel, do some fixup */
+		p->subs[index].owner = NULL;
+		p->subs[index].needcallerid = 0;
+		p->polarity = POLARITY_IDLE;
+		if (index == ANALOG_SUB_REAL) {
+			if (p->subs[ANALOG_SUB_CALLWAIT].allocd && p->subs[ANALOG_SUB_THREEWAY].allocd) {
+				ast_debug(1, "Normal call hung up with both three way call and a call waiting call in place?\n");
+				if (p->subs[ANALOG_SUB_CALLWAIT].inthreeway) {
+					/* We had flipped over to answer a callwait and now it's gone */
+					ast_debug(1, "We were flipped over to the callwait, moving back and unowning.\n");
+					/* Move to the call-wait, but un-own us until they flip back. */
+					analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_REAL);
+					analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT);
+					p->owner = NULL;
+				} else {
+					/* The three way hung up, but we still have a call wait */
+					ast_debug(1, "We were in the threeway and have a callwait still.  Ditching the threeway.\n");
+					analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
+					analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
+					if (p->subs[ANALOG_SUB_REAL].inthreeway) {
+						/* This was part of a three way call.  Immediately make way for
+						   another call */
+						ast_debug(1, "Call was complete, setting owner to former third call\n");
+						p->owner = p->subs[ANALOG_SUB_REAL].owner;
+					} else {
+						/* This call hasn't been completed yet...  Set owner to NULL */
+						ast_debug(1, "Call was incomplete, setting owner to NULL\n");
+						p->owner = NULL;
+					}
+					p->subs[ANALOG_SUB_REAL].inthreeway = 0;
+				}
+			} else if (p->subs[ANALOG_SUB_CALLWAIT].allocd) {
+				/* Move to the call-wait and switch back to them. */
+				analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_REAL);
+				analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT);
+				p->owner = p->subs[ANALOG_SUB_REAL].owner;
+				if (p->owner->_state != AST_STATE_UP)
+					ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_ANSWER);
+				if (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner))
+					ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD);
+			} else if (p->subs[ANALOG_SUB_THREEWAY].allocd) {
+				analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
+				analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
+				if (p->subs[ANALOG_SUB_REAL].inthreeway) {
+					/* This was part of a three way call.  Immediately make way for
+					   another call */
+					ast_debug(1, "Call was complete, setting owner to former third call\n");
+					p->owner = p->subs[ANALOG_SUB_REAL].owner;
+				} else {
+					/* This call hasn't been completed yet...  Set owner to NULL */
+					ast_debug(1, "Call was incomplete, setting owner to NULL\n");
+					p->owner = NULL;
+				}
+				p->subs[ANALOG_SUB_REAL].inthreeway = 0;
+			}
+		} else if (index == ANALOG_SUB_CALLWAIT) {
+			/* Ditch the holding callwait call, and immediately make it availabe */
+			if (p->subs[ANALOG_SUB_CALLWAIT].inthreeway) {
+				/* This is actually part of a three way, placed on hold.  Place the third part
+				   on music on hold now */
+				if (p->subs[ANALOG_SUB_THREEWAY].owner && ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) {
+					ast_queue_control_data(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CONTROL_HOLD, 
+						S_OR(p->mohsuggest, NULL),
+						!ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+				}
+				p->subs[ANALOG_SUB_THREEWAY].inthreeway = 0;
+				/* Make it the call wait now */
+				analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_THREEWAY);
+				analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
+			} else
+				analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT);
+		} else if (index == ANALOG_SUB_THREEWAY) {
+			if (p->subs[ANALOG_SUB_CALLWAIT].inthreeway) {
+				/* The other party of the three way call is currently in a call-wait state.
+				   Start music on hold for them, and take the main guy out of the third call */
+				if (p->subs[ANALOG_SUB_CALLWAIT].owner && ast_bridged_channel(p->subs[ANALOG_SUB_CALLWAIT].owner)) {
+					ast_queue_control_data(p->subs[ANALOG_SUB_CALLWAIT].owner, AST_CONTROL_HOLD, 
+						S_OR(p->mohsuggest, NULL),
+						!ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+				}
+				p->subs[ANALOG_SUB_CALLWAIT].inthreeway = 0;
+			}
+			p->subs[ANALOG_SUB_REAL].inthreeway = 0;
+			/* If this was part of a three way call index, let us make
+			   another three way call */
+			analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
+		} else {
+			/* This wasn't any sort of call, but how are we an index? */
+			ast_log(LOG_WARNING, "Index found but not any type of call?\n");
+		}
+	}
+
+	if (!p->subs[ANALOG_SUB_REAL].owner && !p->subs[ANALOG_SUB_CALLWAIT].owner && !p->subs[ANALOG_SUB_THREEWAY].owner) {
+		p->owner = NULL;
+#if 0
+		p->ringt = 0;
+#endif
+#if 0 /* Since we set it in _call */
+		p->cidrings = 1;
+#endif
+		p->outgoing = 0;
+
+		/* Perform low level hangup if no owner left */
+		res = analog_on_hook(p);
+		if (res < 0) {
+			ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name);
+		}
+		switch (p->sig) {
+		case ANALOG_SIG_FXOGS:
+		case ANALOG_SIG_FXOLS:
+		case ANALOG_SIG_FXOKS:
+			/* If they're off hook, try playing congestion */
+			if (analog_is_off_hook(p))
+				analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION);
+			else
+				analog_play_tone(p, ANALOG_SUB_REAL, -1);
+			break;
+		case ANALOG_SIG_FXSGS:
+		case ANALOG_SIG_FXSLS:
+		case ANALOG_SIG_FXSKS:
+			/* Make sure we're not made available for at least two seconds assuming
+			   we were actually used for an inbound or outbound call. */
+			if (ast->_state != AST_STATE_RESERVED) {
+				time(&p->guardtime);
+				p->guardtime += 2;
+			}
+			break;
+		default:
+			analog_play_tone(p, ANALOG_SUB_REAL, -1);
+		}
+
+
+		analog_set_echocanceller(p, 0);
+
+		x = 0;
+		ast_channel_setoption(ast,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
+		ast_channel_setoption(ast,AST_OPTION_TDD,&x,sizeof(char),0);
+		p->callwaitcas = 0;
+		p->callwaiting = p->permcallwaiting;
+		p->hidecallerid = p->permhidecallerid;
+		p->dialing = 0;
+		analog_update_conf(p);
+		analog_all_subchannels_hungup(p);
+	}
+
+	analog_stop_callwait(p);
+	ast->tech_pvt = NULL;
+
+	if (option_verbose > 2) {
+		ast_verbose(VERBOSE_PREFIX_3 "Hanging up on '%s'\n", ast->name);
+	}
+
+	return 0;
+}
+
+int analog_answer(struct analog_pvt *p, struct ast_channel *ast)
+{
+	int res = 0;
+	int index;
+	int oldstate = ast->_state;
+	ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
+	ast_setstate(ast, AST_STATE_UP);
+	index = analog_get_index(ast, p, 1);
+	if (index < 0)
+		index = ANALOG_SUB_REAL;
+	switch (p->sig) {
+	case ANALOG_SIG_FXSLS:
+	case ANALOG_SIG_FXSGS:
+	case ANALOG_SIG_FXSKS:
+#if 0
+		p->ringt = 0;
+#endif
+		/* Fall through */
+	case ANALOG_SIG_EM:
+	case ANALOG_SIG_EM_E1:
+	case ANALOG_SIG_EMWINK:
+	case ANALOG_SIG_FEATD:
+	case ANALOG_SIG_FEATDMF:
+	case ANALOG_SIG_FEATDMF_TA:
+	case ANALOG_SIG_E911:
+	case ANALOG_SIG_FGC_CAMA:
+	case ANALOG_SIG_FGC_CAMAMF:
+	case ANALOG_SIG_FEATB:
+	case ANALOG_SIG_SF:
+	case ANALOG_SIG_SFWINK:
+	case ANALOG_SIG_SF_FEATD:
+	case ANALOG_SIG_SF_FEATDMF:
+	case ANALOG_SIG_SF_FEATB:
+	case ANALOG_SIG_FXOLS:
+	case ANALOG_SIG_FXOGS:
+	case ANALOG_SIG_FXOKS:
+		/* Pick up the line */
+		ast_debug(1, "Took %s off hook\n", ast->name);
+		if (p->hanguponpolarityswitch) {
+			gettimeofday(&p->polaritydelaytv, NULL);
+		}
+		res = analog_off_hook(p);
+		analog_play_tone(p, index, -1);
+		p->dialing = 0;
+		if ((index == ANALOG_SUB_REAL) && p->subs[ANALOG_SUB_THREEWAY].inthreeway) {
+			if (oldstate == AST_STATE_RINGING) {
+				ast_debug(1, "Finally swapping real and threeway\n");
+				analog_play_tone(p, ANALOG_SUB_THREEWAY, -1);
+				analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
+				p->owner = p->subs[ANALOG_SUB_REAL].owner;
+			}
+		}
+		if ((p->sig == ANALOG_SIG_FXSLS) || (p->sig == ANALOG_SIG_FXSKS) || (p->sig == ANALOG_SIG_FXSGS)) {
+			analog_set_echocanceller(p, 1);
+			analog_train_echocanceller(p);
+		}
+		break;
+	default:
+		ast_log(LOG_WARNING, "Don't know how to answer signalling %d (channel %d)\n", p->sig, p->channel);
+		res = -1;
+	}
+	ast_setstate(ast, AST_STATE_UP);
+	return res;
+}
+
+static int analog_handles_digit(struct ast_frame *f)
+{
+	char subclass = toupper(f->subclass);
+
+	switch (subclass) {
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '9':
+	case 'A':
+	case 'B':
+	case 'C':
+	case 'D':
+	case 'E':
+	case 'F':
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+void analog_handle_dtmfup(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub index, struct ast_frame **dest)
+{
+	struct ast_frame *f = *dest;
+
+	if (p->callwaitcas) {
+		if ((f->subclass == 'A') || (f->subclass == 'D')) {
+			ast_log(LOG_ERROR, "Got some DTMF, but it's for the CAS\n");
+			p->cid.cid_name = p->callwait_name;
+			p->cid.cid_num = p->callwait_num;
+			analog_send_callerid(p, 1, &p->cid);
+		}
+		if (analog_handles_digit(f))
+			p->callwaitcas = 0;
+		p->subs[index].f.frametype = AST_FRAME_NULL;
+		p->subs[index].f.subclass = 0;
+		*dest = &p->subs[index].f;
+	} else {
+		analog_cb_handle_dtmfup(p, ast, index, dest);
+	}
+}
+
+static int analog_my_getsigstr(struct ast_channel *chan, char *str, const char *term, int ms)
+{
+	char c;
+
+	*str = 0; /* start with empty output buffer */
+	for (;;)
+	{
+		/* Wait for the first digit (up to specified ms). */
+		c = ast_waitfordigit(chan, ms);
+		/* if timeout, hangup or error, return as such */
+		if (c < 1)
+			return c;
+		*str++ = c;
+		*str = 0;
+		if (strchr(term, c))
+			return 1;
+	}
+}
+
+static int analog_handle_notify_message(struct ast_channel *chan, struct analog_pvt *p, int cid_flags, int neon_mwievent)
+{
+	if (p->calls->handle_notify_message) {
+		p->calls->handle_notify_message(chan, p->chan_pvt, cid_flags, neon_mwievent);
+		return 0;
+	}
+	else
+		return -1;
+}
+
+static int analog_increase_ss_count(struct analog_pvt *p)
+{
+	if (p->calls->increase_ss_count) {
+		p->calls->increase_ss_count();
+		return 0;
+	} else
+		return -1;
+}
+
+static int analog_decrease_ss_count(struct analog_pvt *p)
+{
+	if (p->calls->decrease_ss_count) {
+		p->calls->decrease_ss_count();
+		return 0;
+	} else
+		return -1;
+}
+
+#define ANALOG_NEED_MFDETECT(p) (((p)->sig == ANALOG_SIG_FEATDMF) || ((p)->sig == ANALOG_SIG_FEATDMF_TA) || ((p)->sig == ANALOG_SIG_E911) || ((p)->sig == ANALOG_SIG_FGC_CAMA) || ((p)->sig == ANALOG_SIG_FGC_CAMAMF) || ((p)->sig == ANALOG_SIG_FEATB))
+
+/* Note by jpeeler: This function has a rather large section of code ifdefed
+ * away. I'd like to leave the code there until more testing is done and I
+ * know for sure that nothing got left out. The plan is at the latest for this
+ * comment and code below to be removed shortly after the merging of sig_pri.
+ */
+static void *__analog_ss_thread(void *data)
+{
+	struct analog_pvt *p = data;
+	struct ast_channel *chan = p->ss_astchan;
+	char exten[AST_MAX_EXTENSION] = "";
+	char exten2[AST_MAX_EXTENSION] = "";
+	char dtmfcid[300];
+	char dtmfbuf[300];
+	char namebuf[ANALOG_MAX_CID];
+	char numbuf[ANALOG_MAX_CID];
+	struct callerid_state *cs = NULL;
+	char *name = NULL, *number = NULL;
+	int flags;
+#if 0
+	unsigned char buf[256];
+	int distMatches;
+	int curRingData[3];
+	int receivedRingT;
+	int samples = 0;
+	int counter1;
+	int counter;
+	int i;
+#endif
+	int timeout;
+	int getforward = 0;
+	char *s1, *s2;
+	int len = 0;
+	int res;
+	int index;
+
+	analog_increase_ss_count(p);
+
+	ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
+
+	/* in the bizarre case where the channel has become a zombie before we
+	   even get started here, abort safely
+	*/
+	if (!p) {
+		ast_log(LOG_WARNING, "Channel became a zombie before simple switch could be started (%s)\n", chan->name);
+		ast_hangup(chan);
+		goto quit;
+	}
+
+	ast_verb(3, "Starting simple switch on '%s'\n", chan->name);
+	index = analog_get_index(chan, p, 1);
+	if (index < 0) {
+		ast_log(LOG_WARNING, "Huh?\n");
+		ast_hangup(chan);
+		goto quit;
+	}
+	analog_dsp_reset_and_flush_digits(p);
+	switch (p->sig) {
+	case ANALOG_SIG_FEATD:
+	case ANALOG_SIG_FEATDMF:
+	case ANALOG_SIG_FEATDMF_TA:
+	case ANALOG_SIG_E911:
+	case ANALOG_SIG_FGC_CAMAMF:
+	case ANALOG_SIG_FEATB:
+	case ANALOG_SIG_EMWINK:
+	case ANALOG_SIG_SF_FEATD:
+	case ANALOG_SIG_SF_FEATDMF:
+	case ANALOG_SIG_SF_FEATB:
+	case ANALOG_SIG_SFWINK:
+		if (analog_wink(p, index))
+			goto quit;
+		/* Fall through */
+	case ANALOG_SIG_EM:
+	case ANALOG_SIG_EM_E1:
+	case ANALOG_SIG_SF:
+	case ANALOG_SIG_FGC_CAMA:
+		res = analog_play_tone(p, index, -1);
+
+		analog_dsp_reset_and_flush_digits(p);
+
+		if (ANALOG_NEED_MFDETECT(p)) {
+			analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_MF);
+		} else
+			analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_DTMF);
+
+		memset(dtmfbuf, 0, sizeof(dtmfbuf));
+		/* Wait for the first digit only if immediate=no */
+		if (!p->immediate)
+			/* Wait for the first digit (up to 5 seconds). */

[... 2279 lines stripped ...]



More information about the svn-commits mailing list