Skip to content
Snippets Groups Projects
sig_analog.c 124 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		/* Return provides old linear_mode setting or error indication */
    
    		return analog_callbacks.set_linear_mode(p->chan_pvt, sub, linear_mode);
    
    static void analog_set_inthreeway(struct analog_pvt *p, enum analog_sub sub, int inthreeway)
    {
    	p->subs[sub].inthreeway = inthreeway;
    
    	if (analog_callbacks.set_inthreeway) {
    		analog_callbacks.set_inthreeway(p->chan_pvt, sub, inthreeway);
    
    int analog_call(struct analog_pvt *p, struct ast_channel *ast, const char *rdest, int timeout)
    
    	int res, idx, mysig;
    
    	char *c, *n, *l;
    	char dest[256]; /* must be same length as p->dialdest */
    
    
    	ast_debug(1, "CALLING CID_NAME: %s CID_NUM:: %s\n",
    
    		S_COR(ast_channel_connected(ast)->id.name.valid, ast_channel_connected(ast)->id.name.str, ""),
    		S_COR(ast_channel_connected(ast)->id.number.valid, ast_channel_connected(ast)->id.number.str, ""));
    
    
    	ast_copy_string(dest, rdest, sizeof(dest));
    	ast_copy_string(p->dialdest, rdest, sizeof(p->dialdest));
    
    
    	if ((ast_channel_state(ast) == AST_STATE_BUSY)) {
    
    		ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_BUSY);
    		return 0;
    	}
    
    
    	if ((ast_channel_state(ast) != AST_STATE_DOWN) && (ast_channel_state(ast) != AST_STATE_RESERVED)) {
    
    		ast_log(LOG_WARNING, "analog_call called on %s, neither down nor reserved\n", ast_channel_name(ast));
    
    	analog_set_outgoing(p, 1);
    
    
    	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 */
    
    			analog_set_dialing(p, 1);
    
    			analog_set_cadence(p, ast); /* and set p->cidrings */
    
    
    			/* nick@dccinc.com 4/3/03 mods to allow for deferred dialing */
    			c = strchr(dest, '/');
    
    			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;
    			}
    
    			analog_set_dialing(p, 1);
    
    			/* Call waiting call */
    
    			if (ast_channel_connected(ast)->id.number.valid && ast_channel_connected(ast)->id.number.str) {
    				ast_copy_string(p->callwait_num, ast_channel_connected(ast)->id.number.str, sizeof(p->callwait_num));
    
    			if (ast_channel_connected(ast)->id.name.valid && ast_channel_connected(ast)->id.name.str) {
    				ast_copy_string(p->callwait_name, ast_channel_connected(ast)->id.name.str, sizeof(p->callwait_name));
    
    				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_channel_name(ast));
    
    		n = ast_channel_connected(ast)->id.name.valid ? ast_channel_connected(ast)->id.name.str : NULL;
    		l = ast_channel_connected(ast)->id.number.valid ? ast_channel_connected(ast)->id.number.str : NULL;
    
    			ast_copy_string(p->lastcid_num, l, sizeof(p->lastcid_num));
    
    			ast_copy_string(p->lastcid_name, n, sizeof(p->lastcid_name));
    
    			p->caller.id.name.str = p->lastcid_name;
    			p->caller.id.number.str = p->lastcid_num;
    
    		}
    
    		ast_setstate(ast, AST_STATE_RINGING);
    
    		idx = analog_get_index(ast, p, 0);
    		if (idx > -1) {
    
    			struct ast_cc_config_params *cc_params;
    
    			/* This is where the initial ringing frame is queued for an analog call.
    			 * As such, this is a great time to offer CCNR to the caller if it's available.
    			 */
    
    			cc_params = ast_channel_get_cc_config_params(p->subs[idx].owner);
    
    			if (cc_params) {
    				switch (ast_get_cc_monitor_policy(cc_params)) {
    				case AST_CC_MONITOR_NEVER:
    					break;
    				case AST_CC_MONITOR_NATIVE:
    				case AST_CC_MONITOR_ALWAYS:
    				case AST_CC_MONITOR_GENERIC:
    
    					ast_queue_cc_frame(p->subs[idx].owner, AST_CC_GENERIC_MONITOR_TYPE,
    
    						analog_get_orig_dialstring(p), AST_CC_CCNR, NULL);
    					break;
    				}
    			}
    
    			ast_queue_control(p->subs[idx].owner, AST_CONTROL_RINGING);
    
    		}
    		break;
    	case ANALOG_SIG_FXSLS:
    	case ANALOG_SIG_FXSGS:
    	case ANALOG_SIG_FXSKS:
    
    		if (p->answeronpolarityswitch || p->hanguponpolarityswitch) {
    			ast_debug(1, "Ignore possible polarity reversal on line seizure\n");
    			p->polaritydelaytv = ast_tvnow();
    		}
    		/* fall through */
    
    	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 (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_debug(1, "Dialing '%s'\n", c);
    
    		p->dop.op = ANALOG_DIAL_OP_REPLACE;
    
    		c += p->stripmsd;
    
    		switch (mysig) {
    		case ANALOG_SIG_FEATD:
    
    			l = ast_channel_connected(ast)->id.number.valid ? ast_channel_connected(ast)->id.number.str : NULL;
    
    				snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T*%s*%s*", l, c);
    
    				snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T**%s*", c);
    
    			l = ast_channel_connected(ast)->id.number.valid ? ast_channel_connected(ast)->id.number.str : NULL;
    
    				snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*00%s#*%s#", l, c);
    
    				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");
    
    			cic = pbx_builtin_getvar_helper(p->owner, "FEATDMF_CIC");
    
    #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:
    
    				snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "P%sw", c);
    
    				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';
    
    		analog_set_waitingfordt(p, ast);
    
    		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;
    			}
    
    			ast_debug(1, "Deferring dialing...\n");
    
    		analog_set_dialing(p, 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;
    
    	ast_debug(1, "%s %d\n", __FUNCTION__, p->channel);
    
    	if (!ast_channel_tech_pvt(ast)) {
    
    		ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
    		return 0;
    	}
    
    
    	idx = 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));
    
    		ast_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));
    
    		ast_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, idx, p->subs[ANALOG_SUB_REAL].allocd, p->subs[ANALOG_SUB_CALLWAIT].allocd, p->subs[ANALOG_SUB_THREEWAY].allocd);
    	if (idx > -1) {
    
    		/* Real channel, do some fixup */
    
    		p->subs[idx].owner = NULL;
    
    		p->polarity = POLARITY_IDLE;
    
    		analog_set_linear_mode(p, idx, 0);
    		switch (idx) {
    
    		case 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);
    
    					analog_set_new_owner(p, 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");
    
    						analog_set_inthreeway(p, ANALOG_SUB_REAL, 0);
    
    						analog_set_new_owner(p, 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");
    
    						analog_set_new_owner(p, NULL);
    
    					}
    				}
    			} else if (p->subs[ANALOG_SUB_CALLWAIT].allocd) {
    
    				/* Need to hold the lock for real-call, private, and call-waiting call */
    				analog_lock_sub_owner(p, ANALOG_SUB_CALLWAIT);
    				if (!p->subs[ANALOG_SUB_CALLWAIT].owner) {
    					/* The call waiting call dissappeared. */
    
    					analog_set_new_owner(p, NULL);
    
    				/* 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);
    
    				analog_set_new_owner(p, p->subs[ANALOG_SUB_REAL].owner);
    
    				if (ast_channel_state(p->owner) != 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);
    
    				/* Unlock the call-waiting call that we swapped to real-call. */
    				ast_channel_unlock(p->subs[ANALOG_SUB_REAL].owner);
    
    			} 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");
    
    					analog_set_inthreeway(p, ANALOG_SUB_REAL, 0);
    
    					analog_set_new_owner(p, 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");
    
    					analog_set_new_owner(p, NULL);
    
    			break;
    		case ANALOG_SUB_CALLWAIT:
    			/* Ditch the holding callwait call, and immediately make it available */
    
    			if (p->subs[ANALOG_SUB_CALLWAIT].inthreeway) {
    
    				/* Need to hold the lock for call-waiting call, private, and 3-way call */
    				analog_lock_sub_owner(p, ANALOG_SUB_THREEWAY);
    
    
    				/* 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);
    				}
    
    				analog_set_inthreeway(p, ANALOG_SUB_THREEWAY, 0);
    
    				/* Make it the call wait now */
    				analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_THREEWAY);
    				analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
    
    				if (p->subs[ANALOG_SUB_CALLWAIT].owner) {
    					/* Unlock the 3-way call that we swapped to call-waiting call. */
    					ast_channel_unlock(p->subs[ANALOG_SUB_CALLWAIT].owner);
    				}
    
    				analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT);
    
    			break;
    		case ANALOG_SUB_THREEWAY:
    			/* Need to hold the lock for 3-way call, private, and call-waiting call */
    			analog_lock_sub_owner(p, ANALOG_SUB_CALLWAIT);
    
    			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 */
    
    				analog_set_inthreeway(p, ANALOG_SUB_CALLWAIT, 0);
    
    				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);
    				}
    
    			}
    			if (p->subs[ANALOG_SUB_CALLWAIT].owner) {
    				ast_channel_unlock(p->subs[ANALOG_SUB_CALLWAIT].owner);
    
    			analog_set_inthreeway(p, ANALOG_SUB_REAL, 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);
    
    			break;
    		default:
    			/*
    			 * Should never happen.
    			 * This wasn't any sort of call, so how are we an index?
    			 */
    			ast_log(LOG_ERROR, "Index found but not any type of call?\n");
    			break;
    
    		}
    	}
    
    	if (!p->subs[ANALOG_SUB_REAL].owner && !p->subs[ANALOG_SUB_CALLWAIT].owner && !p->subs[ANALOG_SUB_THREEWAY].owner) {
    
    		analog_set_new_owner(p, NULL);
    
    		analog_set_ringtimeout(p, 0);
    
    		analog_set_confirmanswer(p, 0);
    
    		analog_set_pulsedial(p, 0);
    
    		analog_set_outgoing(p, 0);
    
    		p->onhooktime = time(NULL);
    		p->cidrings = 1;
    
    
    		/* 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_channel_name(ast));
    
    		}
    		switch (p->sig) {
    		case ANALOG_SIG_FXOGS:
    		case ANALOG_SIG_FXOLS:
    		case ANALOG_SIG_FXOKS:
    			/* If they're off hook, try playing congestion */
    
    				analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION);
    
    				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_channel_state(ast) != 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;
    
    		analog_set_callwaiting(p, p->permcallwaiting);
    
    		p->hidecallerid = p->permhidecallerid;
    
    		analog_set_dialing(p, 0);
    
    		analog_update_conf(p);
    		analog_all_subchannels_hungup(p);
    	}
    
    	analog_stop_callwait(p);
    
    
    	ast_verb(3, "Hanging up on '%s'\n", ast_channel_name(ast));
    
    
    	return 0;
    }
    
    int analog_answer(struct analog_pvt *p, struct ast_channel *ast)
    {
    	int res = 0;
    
    	int oldstate = ast_channel_state(ast);
    
    
    	ast_debug(1, "%s %d\n", __FUNCTION__, p->channel);
    
    	ast_setstate(ast, AST_STATE_UP);
    
    	idx = analog_get_index(ast, p, 1);
    	if (idx < 0) {
    		idx = ANALOG_SUB_REAL;
    
    	switch (p->sig) {
    	case ANALOG_SIG_FXSLS:
    	case ANALOG_SIG_FXSGS:
    	case ANALOG_SIG_FXSKS:
    
    		analog_set_ringtimeout(p, 0);
    
    		/* 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_channel_name(ast));
    
    		if (p->hanguponpolarityswitch) {
    			gettimeofday(&p->polaritydelaytv, NULL);
    		}
    		res = analog_off_hook(p);
    
    		analog_play_tone(p, idx, -1);
    
    		analog_set_dialing(p, 0);
    
    		if ((idx == 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);
    
    				analog_set_new_owner(p, p->subs[ANALOG_SUB_REAL].owner);
    
    
    		switch (p->sig) {
    		case ANALOG_SIG_FXSLS:
    		case ANALOG_SIG_FXSKS:
    		case ANALOG_SIG_FXSGS:
    
    			analog_set_echocanceller(p, 1);
    			analog_train_echocanceller(p);
    
    			break;
    		case ANALOG_SIG_FXOLS:
    		case ANALOG_SIG_FXOKS:
    		case ANALOG_SIG_FXOGS:
    			analog_answer_polarityswitch(p);
    			break;
    		default:
    			break;
    
    		}
    		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.integer);
    
    
    	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_dtmf(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub idx, struct ast_frame **dest)
    
    	ast_debug(1, "%s DTMF digit: 0x%02X '%c' on %s\n",
    		f->frametype == AST_FRAME_DTMF_BEGIN ? "Begin" : "End",
    
    		f->subclass.integer, f->subclass.integer, ast_channel_name(ast));
    
    	if (analog_check_confirmanswer(p)) {
    
    		if (f->frametype == AST_FRAME_DTMF_END) {
    
    			ast_debug(1, "Confirm answer on %s!\n", ast_channel_name(ast));
    
    			/* Upon receiving a DTMF digit, consider this an answer confirmation instead
    			of a DTMF digit */
    			p->subs[idx].f.frametype = AST_FRAME_CONTROL;
    			p->subs[idx].f.subclass.integer = AST_CONTROL_ANSWER;
    			/* Reset confirmanswer so DTMF's will behave properly for the duration of the call */
    			analog_set_confirmanswer(p, 0);
    		} else {
    			p->subs[idx].f.frametype = AST_FRAME_NULL;
    			p->subs[idx].f.subclass.integer = 0;
    		}
    
    		*dest = &p->subs[idx].f;
    	} else if (p->callwaitcas) {
    
    		if (f->frametype == AST_FRAME_DTMF_END) {
    			if ((f->subclass.integer == 'A') || (f->subclass.integer == 'D')) {
    				ast_debug(1, "Got some DTMF, but it's for the CAS\n");
    				p->caller.id.name.str = p->callwait_name;
    				p->caller.id.number.str = p->callwait_num;
    				analog_send_callerid(p, 1, &p->caller);
    			}
    			if (analog_handles_digit(f)) {
    				p->callwaitcas = 0;
    			}
    
    		p->subs[idx].f.frametype = AST_FRAME_NULL;
    		p->subs[idx].f.subclass.integer = 0;
    		*dest = &p->subs[idx].f;
    
    		analog_cb_handle_dtmf(p, ast, idx, 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 */
    
    		/* Wait for the first digit (up to specified ms). */
    		c = ast_waitfordigit(chan, ms);
    		/* if timeout, hangup or error, return as such */
    
    	}
    }
    
    static int analog_handle_notify_message(struct ast_channel *chan, struct analog_pvt *p, int cid_flags, int neon_mwievent)
    {
    
    	if (analog_callbacks.handle_notify_message) {
    		analog_callbacks.handle_notify_message(chan, p->chan_pvt, cid_flags, neon_mwievent);
    
    static void analog_increase_ss_count(void)
    
    	if (analog_callbacks.increase_ss_count) {
    		analog_callbacks.increase_ss_count();
    
    static void analog_decrease_ss_count(void)
    
    	if (analog_callbacks.decrease_ss_count) {
    		analog_callbacks.decrease_ss_count();
    
    static int analog_distinctive_ring(struct ast_channel *chan, struct analog_pvt *p, int idx, int *ringdata)
    {
    
    	if (analog_callbacks.distinctive_ring) {
    		return analog_callbacks.distinctive_ring(chan, p->chan_pvt, idx, ringdata);
    
    
    }
    
    static void analog_get_and_handle_alarms(struct analog_pvt *p)
    {
    
    	if (analog_callbacks.get_and_handle_alarms) {
    		analog_callbacks.get_and_handle_alarms(p->chan_pvt);
    
    static void *analog_get_bridged_channel(struct ast_channel *chan)
    
    	if (analog_callbacks.get_sigpvt_bridged_channel) {
    		return analog_callbacks.get_sigpvt_bridged_channel(chan);
    
    }
    
    static int analog_get_sub_fd(struct analog_pvt *p, enum analog_sub sub)
    {
    
    	if (analog_callbacks.get_sub_fd) {
    		return analog_callbacks.get_sub_fd(p->chan_pvt, sub);
    
    #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))
    
    
    static int analog_canmatch_featurecode(const char *exten)
    {
    	int extlen = strlen(exten);
    	const char *pickup_ext;
    	if (!extlen) {
    		return 1;
    	}
    	pickup_ext = ast_pickup_ext();
    	if (extlen < strlen(pickup_ext) && !strncmp(pickup_ext, exten, extlen)) {
    		return 1;
    	}
    	/* hardcoded features are *60, *67, *69, *70, *72, *73, *78, *79, *82, *0 */
    	if (exten[0] == '*' && extlen < 3) {
    		if (extlen == 1) {
    			return 1;
    		}
    		/* "*0" should be processed before it gets here */
    		switch (exten[1]) {
    		case '6':
    		case '7':
    		case '8':
    			return 1;
    		}
    	}
    	return 0;
    }
    
    
    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;
    
    	struct ast_smdi_md_message *smdi_msg = NULL;
    
    	int timeout;
    	int getforward = 0;
    	char *s1, *s2;
    	int len = 0;
    	int res;
    
    	struct ast_callid *callid;
    
    	analog_increase_ss_count();
    
    	ast_debug(1, "%s %d\n", __FUNCTION__, p->channel);
    
    	if (!chan) {
    		/* What happened to the channel? */
    		goto quit;
    	}
    
    
    	if ((callid = ast_channel_callid(chan))) {
    		ast_callid_threadassoc_add(callid);
    		ast_callid_unref(callid);
    	}
    
    
    	/* in the bizarre case where the channel has become a zombie before we
    	   even get started here, abort safely
    	*/
    
    	if (!ast_channel_tech_pvt(chan)) {
    
    		ast_log(LOG_WARNING, "Channel became a zombie before simple switch could be started (%s)\n", ast_channel_name(chan));
    
    	ast_verb(3, "Starting simple switch on '%s'\n", ast_channel_name(chan));
    
    	idx = analog_get_index(chan, p, 0);
    	if (idx < 0) {
    
    		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, idx))
    
    			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, idx, -1);
    
    
    		analog_dsp_reset_and_flush_digits(p);
    
    
    		/* set digit mode appropriately */
    
    		if (ANALOG_NEED_MFDETECT(p)) {
    			analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_MF);
    
    			analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_DTMF);
    
    
    		memset(dtmfbuf, 0, sizeof(dtmfbuf));
    		/* Wait for the first digit only if immediate=no */
    
    			/* Wait for the first digit (up to 5 seconds). */
    			res = ast_waitfordigit(chan, 5000);
    
    		if (res > 0) {
    			/* save first char */
    			dtmfbuf[0] = res;
    			switch (p->sig) {
    			case ANALOG_SIG_FEATD:
    			case ANALOG_SIG_SF_FEATD:
    				res = analog_my_getsigstr(chan, dtmfbuf + 1, "*", 3000);
    
    					res = analog_my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "*", 3000);
    
    					analog_dsp_reset_and_flush_digits(p);
    
    				break;
    			case ANALOG_SIG_FEATDMF_TA:
    				res = analog_my_getsigstr(chan, dtmfbuf + 1, "#", 3000);
    
    					analog_dsp_reset_and_flush_digits(p);
    
    				if (analog_wink(p, idx)) {
    
    				dtmfbuf[0] = 0;
    				/* Wait for the first digit (up to 5 seconds). */
    				res = ast_waitfordigit(chan, 5000);
    
    				dtmfbuf[0] = res;
    				/* fall through intentionally */
    			case ANALOG_SIG_FEATDMF:
    			case ANALOG_SIG_E911:
    			case ANALOG_SIG_FGC_CAMAMF:
    			case ANALOG_SIG_SF_FEATDMF:
    				res = analog_my_getsigstr(chan, dtmfbuf + 1, "#", 3000);
    				/* if international caca, do it again to get real ANO */
    
    				if ((p->sig == ANALOG_SIG_FEATDMF) && (dtmfbuf[1] != '0') 
    					&& (strlen(dtmfbuf) != 14)) {
    
    					if (analog_wink(p, idx)) {
    
    					dtmfbuf[0] = 0;
    					/* Wait for the first digit (up to 5 seconds). */
    					res = ast_waitfordigit(chan, 5000);
    
    					dtmfbuf[0] = res;
    					res = analog_my_getsigstr(chan, dtmfbuf + 1, "#", 3000);
    				}
    				if (res > 0) {
    					/* if E911, take off hook */
    
    					if (p->sig == ANALOG_SIG_E911) {
    
    					res = analog_my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "#", 3000);
    				}
    
    					analog_dsp_reset_and_flush_digits(p);
    
    				break;
    			case ANALOG_SIG_FEATB:
    			case ANALOG_SIG_SF_FEATB:
    				res = analog_my_getsigstr(chan, dtmfbuf + 1, "#", 3000);
    
    					analog_dsp_reset_and_flush_digits(p);
    
    				break;
    			case ANALOG_SIG_EMWINK:
    				/* if we received a '*', we are actually receiving Feature Group D
    				   dial syntax, so use that mode; otherwise, fall through to normal
    				   mode
    				*/
    				if (res == '*') {
    					res = analog_my_getsigstr(chan, dtmfbuf + 1, "*", 3000);
    
    						res = analog_my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "*", 3000);
    
    						analog_dsp_reset_and_flush_digits(p);
    
    					break;
    				}
    			default:
    				/* If we got the first digit, get the rest */
    				len = 1;
    				dtmfbuf[len] = '\0';
    
    				while ((len < AST_MAX_EXTENSION-1) && ast_matchmore_extension(chan, ast_channel_context(chan), dtmfbuf, 1, p->cid_num)) {
    					if (ast_exists_extension(chan, ast_channel_context(chan), dtmfbuf, 1, p->cid_num)) {
    
    						timeout = analog_matchdigittimeout;
    					} else {
    						timeout = analog_gendigittimeout;
    					}
    					res = ast_waitfordigit(chan, timeout);
    					if (res < 0) {
    						ast_debug(1, "waitfordigit returned < 0...\n");
    						ast_hangup(chan);
    						goto quit;
    					} else if (res) {
    						dtmfbuf[len++] = res;
    						dtmfbuf[len] = '\0';
    					} else {
    						break;
    					}
    				}
    				break;
    			}
    		}
    		if (res == -1) {
    			ast_log(LOG_WARNING, "getdtmf on channel %d: %s\n", p->channel, strerror(errno));
    			ast_hangup(chan);
    			goto quit;
    		} else if (res < 0) {
    			ast_debug(1, "Got hung up before digits finished\n");
    			ast_hangup(chan);
    			goto quit;
    		}
    
    		if (p->sig == ANALOG_SIG_FGC_CAMA) {
    			char anibuf[100];
    
    			if (ast_safe_sleep(chan,1000) == -1) {
    
    				ast_hangup(chan);
    				goto quit;
    
    			}
    			analog_off_hook(p);
    			analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_MF);
    
    			res = analog_my_getsigstr(chan, anibuf, "#", 10000);
    			if ((res > 0) && (strlen(anibuf) > 2)) {
    
    				if (anibuf[strlen(anibuf) - 1] == '#') {
    
    					anibuf[strlen(anibuf) - 1] = 0;
    
    				ast_set_callerid(chan, anibuf + 2, NULL, anibuf + 2);
    			}
    			analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_DTMF);
    		}
    
    		ast_copy_string(exten, dtmfbuf, sizeof(exten));
    
    		if (ast_strlen_zero(exten)) {
    
    			ast_copy_string(exten, "s", sizeof(exten));
    
    		if (p->sig == ANALOG_SIG_FEATD || p->sig == ANALOG_SIG_EMWINK) {
    			/* Look for Feature Group D on all E&M Wink and Feature Group D trunks */
    			if (exten[0] == '*') {
    				char *stringp=NULL;
    				ast_copy_string(exten2, exten, sizeof(exten2));
    				/* Parse out extension and callerid */
    				stringp=exten2 +1;
    				s1 = strsep(&stringp, "*");
    				s2 = strsep(&stringp, "*");
    				if (s2) {
    
    					if (!ast_strlen_zero(p->cid_num)) {
    
    						ast_set_callerid(chan, p->cid_num, NULL, p->cid_num);
    
    						ast_set_callerid(chan, s1, NULL, s1);
    
    					ast_copy_string(exten, s2, sizeof(exten));
    
    					ast_copy_string(exten, s1, sizeof(exten));
    
    				}
    			} else if (p->sig == ANALOG_SIG_FEATD) {
    
    				ast_log(LOG_WARNING, "Got a non-Feature Group D input on channel %d.  Assuming E&M Wink instead\n", p->channel);