Skip to content
Snippets Groups Projects
sig_analog.c 124 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		if ((p->sig == ANALOG_SIG_FEATDMF) || (p->sig == ANALOG_SIG_FEATDMF_TA)) {
    			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 + 2, NULL, s1 + 2);
    
    					ast_copy_string(exten, s2 + 1, sizeof(exten));
    
    					ast_copy_string(exten, s1 + 2, sizeof(exten));
    
    				ast_log(LOG_WARNING, "Got a non-Feature Group D input on channel %d.  Assuming E&M Wink instead\n", p->channel);
    
    		}
    		if ((p->sig == ANALOG_SIG_E911) || (p->sig == ANALOG_SIG_FGC_CAMAMF)) {
    			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 && (*(s2 + 1) == '0')) {
    
    						ast_set_callerid(chan, s2 + 2, NULL, s2 + 2);
    
    				if (s1) {
    					ast_copy_string(exten, s1, sizeof(exten));
    				} else {
    					ast_copy_string(exten, "911", sizeof(exten));
    				}
    			} else {
    
    				ast_log(LOG_WARNING, "Got a non-E911/FGC CAMA input on channel %d.  Assuming E&M Wink instead\n", p->channel);
    
    		}
    		if (p->sig == ANALOG_SIG_FEATB) {
    			if (exten[0] == '*') {
    				char *stringp=NULL;
    				ast_copy_string(exten2, exten, sizeof(exten2));
    				/* Parse out extension and callerid */
    				stringp=exten2 +1;
    				s1 = strsep(&stringp, "#");
    				ast_copy_string(exten, exten2 + 1, sizeof(exten));
    
    				ast_log(LOG_WARNING, "Got a non-Feature Group B input on channel %d.  Assuming E&M Wink instead\n", p->channel);
    
    		}
    		if ((p->sig == ANALOG_SIG_FEATDMF) || (p->sig == ANALOG_SIG_FEATDMF_TA)) {
    
    			analog_wink(p, idx);
    
    			/*
    			 * Some switches require a minimum guard time between the last
    			 * FGD wink and something that answers immediately.  This
    			 * ensures it.
    			 */
    			if (ast_safe_sleep(chan, 100)) {
    				ast_hangup(chan);
    
    		}
    		analog_set_echocanceller(p, 1);
    
    		analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_DTMF);
    
    
    		if (ast_exists_extension(chan, ast_channel_context(chan), exten, 1,
    
    			ast_channel_caller(chan)->id.number.valid ? ast_channel_caller(chan)->id.number.str : NULL)) {
    
    			ast_channel_exten_set(chan, exten);
    
    			analog_dsp_reset_and_flush_digits(p);
    			res = ast_pbx_run(chan);
    			if (res) {
    				ast_log(LOG_WARNING, "PBX exited non-zero\n");
    
    				res = analog_play_tone(p, idx, ANALOG_TONE_CONGESTION);
    
    			ast_verb(3, "Unknown extension '%s' in context '%s' requested\n", exten, ast_channel_context(chan));
    
    			res = analog_play_tone(p, idx, ANALOG_TONE_INFO);
    
    				ast_log(LOG_WARNING, "Unable to start special tone on %d\n", p->channel);
    
    			res = ast_streamfile(chan, "ss-noservice", ast_channel_language(chan));
    
    			res = analog_play_tone(p, idx, ANALOG_TONE_CONGESTION);
    
    			ast_hangup(chan);
    			goto quit;
    		}
    		break;
    	case ANALOG_SIG_FXOLS:
    	case ANALOG_SIG_FXOGS:
    	case ANALOG_SIG_FXOKS:
    		/* Read the first digit */
    		timeout = analog_firstdigittimeout;
    		/* If starting a threeway call, never timeout on the first digit so someone
    		   can use flash-hook as a "hold" feature */
    
    		if (p->subs[ANALOG_SUB_THREEWAY].owner) {
    
    		while (len < AST_MAX_EXTENSION-1) {
    			/* Read digit unless it's supposed to be immediate, in which case the
    			   only answer is 's' */
    
    				res = ast_waitfordigit(chan, timeout);
    
    			timeout = 0;
    			if (res < 0) {
    				ast_debug(1, "waitfordigit returned < 0...\n");
    
    				res = analog_play_tone(p, idx, -1);
    
    				ast_debug(1,"waitfordigit returned '%c' (%d), timeout = %d\n", res, res, timeout);
    
    				exten[len++]=res;
    				exten[len] = '\0';
    			}
    
    			if (!ast_ignore_pattern(ast_channel_context(chan), exten)) {
    
    				analog_play_tone(p, idx, -1);
    
    				analog_play_tone(p, idx, ANALOG_TONE_DIALTONE);
    
    			if (ast_exists_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num) && !ast_parking_ext_valid(exten, chan, ast_channel_context(chan))) {
    				if (!res || !ast_matchmore_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num)) {
    
    					if (getforward) {
    						/* Record this as the forwarding extension */
    
    						ast_copy_string(p->call_forward, exten, sizeof(p->call_forward));
    
    						ast_verb(3, "Setting call forward to '%s' on channel %d\n", p->call_forward, p->channel);
    
    						res = analog_play_tone(p, idx, ANALOG_TONE_DIALRECALL);
    
    						res = analog_play_tone(p, idx, -1);
    
    						sleep(1);
    						memset(exten, 0, sizeof(exten));
    
    						res = analog_play_tone(p, idx, ANALOG_TONE_DIALTONE);
    
    						res = analog_play_tone(p, idx, -1);
    
    						ast_channel_exten_set(chan, exten);
    
    						if (!ast_strlen_zero(p->cid_num)) {
    
    								ast_set_callerid(chan, p->cid_num, NULL, p->cid_num);
    
    								ast_set_callerid(chan, NULL, NULL, p->cid_num);
    
    						}
    						if (!ast_strlen_zero(p->cid_name)) {
    
    								ast_set_callerid(chan, NULL, p->cid_name, NULL);
    
    						}
    						ast_setstate(chan, AST_STATE_RING);
    						analog_set_echocanceller(p, 1);
    						res = ast_pbx_run(chan);
    						if (res) {
    							ast_log(LOG_WARNING, "PBX exited non-zero\n");
    
    							res = analog_play_tone(p, idx, ANALOG_TONE_CONGESTION);
    
    						}
    						goto quit;
    					}
    				} else {
    					/* It's a match, but they just typed a digit, and there is an ambiguous match,
    					   so just set the timeout to analog_matchdigittimeout and wait some more */
    					timeout = analog_matchdigittimeout;
    				}
    			} else if (res == 0) {
    				ast_debug(1, "not enough digits (and no ambiguous match)...\n");
    
    				res = analog_play_tone(p, idx, ANALOG_TONE_CONGESTION);
    
    				analog_wait_event(p);
    				ast_hangup(chan);
    				goto quit;
    			} else if (p->callwaiting && !strcmp(exten, "*70")) {
    
    				ast_verb(3, "Disabling call waiting on %s\n", ast_channel_name(chan));
    
    				/* Disable call waiting if enabled */
    
    				analog_set_callwaiting(p, 0);
    
    				res = analog_play_tone(p, idx, ANALOG_TONE_DIALRECALL);
    
    					ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n",
    
    						ast_channel_name(chan), strerror(errno));
    
    				}
    				len = 0;
    				memset(exten, 0, sizeof(exten));
    				timeout = analog_firstdigittimeout;
    
    			} else if (!strcmp(exten,ast_pickup_ext())) {
    				/* Scan all channels and see if there are any
    				 * ringing channels that have call groups
    				 * that equal this channels pickup group
    				 */
    
    				if (idx == ANALOG_SUB_REAL) {
    
    					/* Switch us from Third call to Call Wait */
    					if (p->subs[ANALOG_SUB_THREEWAY].owner) {
    						/* If you make a threeway call and the *8# a call, it should actually
    						   look like a callwait */
    						analog_alloc_sub(p, ANALOG_SUB_CALLWAIT);
    						analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_THREEWAY);
    						analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
    					}
    					analog_set_echocanceller(p, 1);
    					if (ast_pickup_call(chan)) {
    						ast_debug(1, "No call pickup possible...\n");
    
    						res = analog_play_tone(p, idx, ANALOG_TONE_CONGESTION);
    
    						analog_wait_event(p);
    					}
    					ast_hangup(chan);
    					goto quit;
    				} else {
    					ast_log(LOG_WARNING, "Huh?  Got *8# on call not on real\n");
    					ast_hangup(chan);
    					goto quit;
    				}
    
    			} else if (!p->hidecallerid && !strcmp(exten, "*67")) {
    
    				ast_verb(3, "Disabling Caller*ID on %s\n", ast_channel_name(chan));
    
    				/* Disable Caller*ID if enabled */
    				p->hidecallerid = 1;
    
    				ast_party_number_free(&ast_channel_caller(chan)->id.number);
    				ast_party_number_init(&ast_channel_caller(chan)->id.number);
    				ast_party_name_free(&ast_channel_caller(chan)->id.name);
    				ast_party_name_init(&ast_channel_caller(chan)->id.name);
    
    				res = analog_play_tone(p, idx, ANALOG_TONE_DIALRECALL);
    
    				if (res) {
    					ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n",
    
    						ast_channel_name(chan), strerror(errno));
    
    				}
    				len = 0;
    				memset(exten, 0, sizeof(exten));
    				timeout = analog_firstdigittimeout;
    			} else if (p->callreturn && !strcmp(exten, "*69")) {
    				res = 0;
    				if (!ast_strlen_zero(p->lastcid_num)) {
    
    					res = ast_say_digit_str(chan, p->lastcid_num, "", ast_channel_language(chan));
    
    					res = analog_play_tone(p, idx, ANALOG_TONE_DIALRECALL);
    
    				break;
    			} else if (!strcmp(exten, "*78")) {
    
    				/* Do not disturb enabled */
    				analog_dnd(p, 1);
    				res = analog_play_tone(p, idx, ANALOG_TONE_DIALRECALL);
    
    				getforward = 0;
    				memset(exten, 0, sizeof(exten));
    				len = 0;
    			} else if (!strcmp(exten, "*79")) {
    
    				/* Do not disturb disabled */
    				analog_dnd(p, 0);
    				res = analog_play_tone(p, idx, ANALOG_TONE_DIALRECALL);
    
    				getforward = 0;
    				memset(exten, 0, sizeof(exten));
    				len = 0;
    			} else if (p->cancallforward && !strcmp(exten, "*72")) {
    
    				res = analog_play_tone(p, idx, ANALOG_TONE_DIALRECALL);
    
    				getforward = 1;
    				memset(exten, 0, sizeof(exten));
    				len = 0;
    			} else if (p->cancallforward && !strcmp(exten, "*73")) {
    
    				ast_verb(3, "Cancelling call forwarding on channel %d\n", p->channel);
    
    				res = analog_play_tone(p, idx, ANALOG_TONE_DIALRECALL);
    
    				memset(p->call_forward, 0, sizeof(p->call_forward));
    				getforward = 0;
    				memset(exten, 0, sizeof(exten));
    				len = 0;
    
    			} else if ((p->transfer || p->canpark) && ast_parking_ext_valid(exten, chan, ast_channel_context(chan)) &&
    
    						p->subs[ANALOG_SUB_THREEWAY].owner &&
    						ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) {
    				/* This is a three way call, the main call being a real channel,
    					and we're parking the first call. */
    
    Richard Mudgett's avatar
    Richard Mudgett committed
    				ast_masq_park_call_exten(
    					ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner), chan, exten,
    
    					ast_channel_context(chan), 0, NULL);
    
    				ast_verb(3, "Parking call to '%s'\n", ast_channel_name(chan));
    
    				break;
    			} else if (!ast_strlen_zero(p->lastcid_num) && !strcmp(exten, "*60")) {
    
    				ast_verb(3, "Blacklisting number %s\n", p->lastcid_num);
    
    				res = ast_db_put("blacklist", p->lastcid_num, "1");
    				if (!res) {
    
    					res = analog_play_tone(p, idx, ANALOG_TONE_DIALRECALL);
    
    					memset(exten, 0, sizeof(exten));
    					len = 0;
    				}
    			} else if (p->hidecallerid && !strcmp(exten, "*82")) {
    
    				ast_verb(3, "Enabling Caller*ID on %s\n", ast_channel_name(chan));
    
    				/* Enable Caller*ID if enabled */
    				p->hidecallerid = 0;
    				ast_set_callerid(chan, p->cid_num, p->cid_name, NULL);
    
    				res = analog_play_tone(p, idx, ANALOG_TONE_DIALRECALL);
    
    				if (res) {
    					ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n",
    
    						ast_channel_name(chan), strerror(errno));
    
    				}
    				len = 0;
    				memset(exten, 0, sizeof(exten));
    				timeout = analog_firstdigittimeout;
    			} else if (!strcmp(exten, "*0")) {
    				struct ast_channel *nbridge = p->subs[ANALOG_SUB_THREEWAY].owner;
    
    				struct analog_pvt *pbridge = NULL;
    
    				/* set up the private struct of the bridged one, if any */
    
    					pbridge = analog_get_bridged_channel(nbridge);
    
    				if (pbridge && ISTRUNK(pbridge)) {
    
    					/* Clear out the dial buffer */
    					p->dop.dialstr[0] = '\0';
    					/* flash hookswitch */
    
    					if ((analog_flash(pbridge) == -1) && (errno != EINPROGRESS)) {
    						ast_log(LOG_WARNING,
    							"Unable to flash-hook bridged trunk from channel %s: %s\n",
    
    							ast_channel_name(nbridge), strerror(errno));
    
    					}
    					analog_swap_subs(p, ANALOG_SUB_REAL, ANALOG_SUB_THREEWAY);
    					analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
    
    					analog_set_new_owner(p, p->subs[ANALOG_SUB_REAL].owner);
    
    					if (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)) {
    
    						ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD);
    
    					ast_hangup(chan);
    					goto quit;
    				} else {
    
    					analog_play_tone(p, idx, ANALOG_TONE_CONGESTION);
    
    					analog_play_tone(p, idx, -1);
    
    					analog_swap_subs(p, ANALOG_SUB_REAL, ANALOG_SUB_THREEWAY);
    					analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
    
    					analog_set_new_owner(p, p->subs[ANALOG_SUB_REAL].owner);
    
    			} else if (!ast_canmatch_extension(chan, ast_channel_context(chan), exten, 1,
    
    				ast_channel_caller(chan)->id.number.valid ? ast_channel_caller(chan)->id.number.str : NULL)
    
    				&& !analog_canmatch_featurecode(exten)) {
    
    				ast_debug(1, "Can't match %s from '%s' in context %s\n", exten,
    
    					ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str
    						? ast_channel_caller(chan)->id.number.str : "<Unknown Caller>",
    
    					ast_channel_context(chan));
    
    				timeout = analog_gendigittimeout;
    
    			if (len && !ast_ignore_pattern(ast_channel_context(chan), exten)) {
    
    				analog_play_tone(p, idx, -1);
    
    		}
    		break;
    	case ANALOG_SIG_FXSLS:
    	case ANALOG_SIG_FXSGS:
    	case ANALOG_SIG_FXSKS:
    
    		/* check for SMDI messages */
    		if (p->use_smdi && p->smdi_iface) {
    			smdi_msg = ast_smdi_md_message_wait(p->smdi_iface, ANALOG_SMDI_MD_WAIT_TIMEOUT);
    			if (smdi_msg != NULL) {
    
    				ast_channel_exten_set(chan, smdi_msg->fwd_st);
    
    
    				if (smdi_msg->type == 'B')
    					pbx_builtin_setvar_helper(chan, "_SMDI_VM_TYPE", "b");
    				else if (smdi_msg->type == 'N')
    					pbx_builtin_setvar_helper(chan, "_SMDI_VM_TYPE", "u");
    
    
    				ast_debug(1, "Received SMDI message on %s\n", ast_channel_name(chan));
    
    			} else {
    				ast_log(LOG_WARNING, "SMDI enabled but no SMDI message present\n");
    			}
    		}
    
    		if (p->use_callerid && (p->cid_signalling == CID_SIG_SMDI && smdi_msg)) {
    			number = smdi_msg->calling_st;
    
    
    		/* If we want caller id, we're in a prering state due to a polarity reversal
    		 * and we're set to use a polarity reversal to trigger the start of caller id,
    		 * grab the caller id and wait for ringing to start... */
    
    		} else if (p->use_callerid && (ast_channel_state(chan) == AST_STATE_PRERING
    
    			&& (p->cid_start == ANALOG_CID_START_POLARITY
    				|| p->cid_start == ANALOG_CID_START_POLARITY_IN
    				|| p->cid_start == ANALOG_CID_START_DTMF_NOALERT))) {
    
    			/* If set to use DTMF CID signalling, listen for DTMF */
    			if (p->cid_signalling == CID_SIG_DTMF) {
    
    				int timeout_ms;
    				int ms;
    				struct timeval start = ast_tvnow();
    
    				ast_debug(1, "Receiving DTMF cid on channel %s\n", ast_channel_name(chan));
    
    				oldlinearity = analog_set_linear_mode(p, idx, 0);
    
    				/*
    				 * We are the only party interested in the Rx stream since
    				 * we have not answered yet.  We don't need or even want DTMF
    				 * emulation.  The DTMF digits can come so fast that emulation
    				 * can drop some of them.
    				 */
    
    				ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
    
    				timeout_ms = 4000;/* This is a typical OFF time between rings. */
    
    
    					ms = ast_remaining_ms(start, timeout_ms);
    					res = ast_waitfor(chan, ms);
    
    						/*
    						 * We do not need to restore the analog_set_linear_mode()
    						 * or AST_FLAG_END_DTMF_ONLY flag settings since we
    						 * are hanging up the channel.
    						 */
    
    						ast_log(LOG_WARNING, "DTMFCID timed out waiting for ring. "
    							"Exiting simple switch\n");
    						ast_hangup(chan);
    						goto quit;
    					}
    
    					f = ast_read(chan);
    					if (!f) {
    
    					if (f->frametype == AST_FRAME_DTMF) {
    
    						if (k < ARRAY_LEN(dtmfbuf) - 1) {
    							dtmfbuf[k++] = f->subclass.integer;
    
    						ast_debug(1, "CID got digit '%c'\n", f->subclass.integer);
    
    						start = ast_tvnow();
    
    					if (ast_channel_state(chan) == AST_STATE_RING ||
    						ast_channel_state(chan) == AST_STATE_RINGING) {
    
    				ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
    
    				dtmfbuf[k] = '\0';
    
    				analog_set_linear_mode(p, idx, oldlinearity);
    
    				/* Got cid and ring. */
    				ast_debug(1, "CID got string '%s'\n", dtmfbuf);
    				callerid_get_dtmf(dtmfbuf, dtmfcid, &flags);
    
    				ast_debug(1, "CID is '%s', flags %d\n", dtmfcid, flags);
    
    				/* If first byte is NULL, we have no cid */
    
    				if (!ast_strlen_zero(dtmfcid)) {
    
    			/* If set to use V23 Signalling, launch our FSK gubbins and listen for it */
    			} else if ((p->cid_signalling == CID_SIG_V23) || (p->cid_signalling == CID_SIG_V23_JP)) {
    
    				int timeout = 10000;  /* Ten seconds */
    				struct timeval start = ast_tvnow();
    				enum analog_event ev;
    
    				namebuf[0] = 0;
    				numbuf[0] = 0;
    
    				if (!analog_start_cid_detect(p, p->cid_signalling)) {
    
    					int off_ms;
    					int ms;
    					struct timeval off_start;
    
    					while (1) {
    						res = analog_get_callerid(p, namebuf, numbuf, &ev, timeout - ast_tvdiff_ms(ast_tvnow(), start));
    
    							if (ev == ANALOG_EVENT_NOALARM) {
    
    							if (p->cid_signalling == CID_SIG_V23_JP) {
    
    								if (ev == ANALOG_EVENT_RINGBEGIN) {
    									analog_off_hook(p);
    
    								ev = ANALOG_EVENT_NONE;
    
    						if (ast_tvdiff_ms(ast_tvnow(), start) > timeout)
    							break;
    
    					name = namebuf;
    					number = numbuf;
    
    					analog_stop_cid_detect(p);
    
    
    					if (p->cid_signalling == CID_SIG_V23_JP) {
    						res = analog_on_hook(p);
    						usleep(1);
    					}
    
    
    					/* Finished with Caller*ID, now wait for a ring to make sure there really is a call coming */
    
    					off_start = ast_tvnow();
    					off_ms = 4000;/* This is a typical OFF time between rings. */
    					while ((ms = ast_remaining_ms(off_start, off_ms))) {
    
    
    						res = ast_waitfor(chan, ms);
    
    						if (res <= 0) {
    							ast_log(LOG_WARNING, "CID timed out waiting for ring. "
    								"Exiting simple switch\n");
    							ast_hangup(chan);
    							goto quit;
    
    						if (!(f = ast_read(chan))) {
    							ast_log(LOG_WARNING, "Hangup received waiting for ring. Exiting simple switch\n");
    							ast_hangup(chan);
    							goto quit;
    						}
    						ast_frfree(f);
    
    						if (ast_channel_state(chan) == AST_STATE_RING ||
    							ast_channel_state(chan) == AST_STATE_RINGING)
    
    					if (analog_distinctive_ring(chan, p, idx, NULL)) {
    
    						ast_log(LOG_WARNING, "CallerID returned with error on channel '%s'\n", ast_channel_name(chan));
    
    					ast_log(LOG_WARNING, "Unable to get caller ID space\n");
    
    			} else {
    				ast_log(LOG_WARNING, "Channel %s in prering "
    					"state, but I have nothing to do. "
    					"Terminating simple switch, should be "
    
    					"restarted by the actual ring.\n",
    
    				ast_hangup(chan);
    				goto quit;
    			}
    		} else if (p->use_callerid && p->cid_start == ANALOG_CID_START_RING) {
    			int timeout = 10000;  /* Ten seconds */
    			struct timeval start = ast_tvnow();
    			enum analog_event ev;
    
    			int curRingData[RING_PATTERNS] = { 0 };
    
    			int receivedRingT = 0;
    
    
    			namebuf[0] = 0;
    			numbuf[0] = 0;
    
    			if (!analog_start_cid_detect(p, p->cid_signalling)) {
    				while (1) {
    					res = analog_get_callerid(p, namebuf, numbuf, &ev, timeout - ast_tvdiff_ms(ast_tvnow(), start));
    
    					if (res == 0) {
    						break;
    					}
    
    
    					if (res == 1 || res == 2) {
    
    						if (ev == ANALOG_EVENT_NOALARM) {
    
    						} else if (ev == ANALOG_EVENT_POLARITY && p->hanguponpolarityswitch && p->polarity == POLARITY_REV) {
    
    							ast_debug(1, "Hanging up due to polarity reversal on channel %d while detecting callerid\n", p->channel);
    							p->polarity = POLARITY_IDLE;
    							ast_hangup(chan);
    							goto quit;
    
    						} else if (ev != ANALOG_EVENT_NONE && ev != ANALOG_EVENT_RINGBEGIN && ev != ANALOG_EVENT_RINGOFFHOOK) {
    
    						if (res != 2) {
    							/* Let us detect callerid when the telco uses distinctive ring */
    							curRingData[receivedRingT] = p->ringt;
    
    							if (p->ringt < p->ringt_base/2) {
    								break;
    							}
    							/* Increment the ringT counter so we can match it against
    							   values in chan_dahdi.conf for distinctive ring */
    
    							if (++receivedRingT == RING_PATTERNS) {
    
    					if (ast_tvdiff_ms(ast_tvnow(), start) > timeout) {
    
    
    				}
    				name = namebuf;
    				number = numbuf;
    
    				analog_stop_cid_detect(p);
    
    
    				if (analog_distinctive_ring(chan, p, idx, curRingData)) {
    
    					ast_log(LOG_WARNING, "CallerID returned with error on channel '%s'\n", ast_channel_name(chan));
    
    				ast_log(LOG_WARNING, "Unable to get caller ID space\n");
    
    			ast_shrink_phone_number(number);
    
    		ast_set_callerid(chan, number, name, number);
    
    
    
    		analog_handle_notify_message(chan, p, flags, -1);
    
    		ast_setstate(chan, AST_STATE_RING);
    
    		ast_channel_rings_set(chan, 1);
    
    		analog_set_ringtimeout(p, p->ringt_base);
    
    		res = ast_pbx_run(chan);
    		if (res) {
    			ast_hangup(chan);
    			ast_log(LOG_WARNING, "PBX exited non-zero\n");
    		}
    		goto quit;
    	default:
    		ast_log(LOG_WARNING, "Don't know how to handle simple switch with signalling %s on channel %d\n", analog_sigtype_to_str(p->sig), p->channel);
    
    	res = analog_play_tone(p, idx, ANALOG_TONE_CONGESTION);
    
    		ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", p->channel);
    
    	if (smdi_msg) {
    		ASTOBJ_UNREF(smdi_msg, ast_smdi_md_message_destroy);
    	}
    
    	analog_decrease_ss_count();
    
    	return NULL;
    }
    
    int analog_ss_thread_start(struct analog_pvt *p, struct ast_channel *chan)
    {
    	pthread_t threadid;
    
    
    	return ast_pthread_create_detached(&threadid, NULL, __analog_ss_thread, p);
    
    }
    
    static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_channel *ast)
    {
    	int res, x;
    	int mysig;
    
    	enum analog_sub idx;
    
    	char *c;
    	pthread_t threadid;
    	struct ast_channel *chan;
    	struct ast_frame *f;
    
    	struct ast_control_pvt_cause_code *cause_code = NULL;
    
    	int data_size = sizeof(*cause_code);
    	char *subclass = NULL;
    
    	ast_debug(1, "%s %d\n", __FUNCTION__, p->channel);
    
    	idx = analog_get_index(ast, p, 0);
    	if (idx < 0) {
    
    		return &ast_null_frame;
    	}
    
    	if (idx != ANALOG_SUB_REAL) {
    
    		ast_log(LOG_ERROR, "We got an event on a non real sub.  Fix it!\n");
    	}
    
    
    	p->subs[idx].f.frametype = AST_FRAME_NULL;
    	p->subs[idx].f.subclass.integer = 0;
    	p->subs[idx].f.datalen = 0;
    	p->subs[idx].f.samples = 0;
    	p->subs[idx].f.mallocd = 0;
    	p->subs[idx].f.offset = 0;
    	p->subs[idx].f.src = "dahdi_handle_event";
    	p->subs[idx].f.data.ptr = NULL;
    	f = &p->subs[idx].f;
    
    	ast_debug(1, "Got event %s(%d) on channel %d (index %d)\n", analog_event2str(res), res, p->channel, idx);
    
    	if (res & (ANALOG_EVENT_PULSEDIGIT | ANALOG_EVENT_DTMFUP)) {
    
    		analog_set_pulsedial(p, (res & ANALOG_EVENT_PULSEDIGIT) ? 1 : 0);
    
    		ast_debug(1, "Detected %sdigit '%c'\n", (res & ANALOG_EVENT_PULSEDIGIT) ? "pulse ": "", res & 0xff);
    		analog_confmute(p, 0);
    
    		p->subs[idx].f.frametype = AST_FRAME_DTMF_END;
    		p->subs[idx].f.subclass.integer = res & 0xff;
    
    		analog_handle_dtmf(p, ast, idx, &f);
    
    		return f;
    	}
    
    	if (res & ANALOG_EVENT_DTMFDOWN) {
    		ast_debug(1, "DTMF Down '%c'\n", res & 0xff);
    		/* Mute conference */
    		analog_confmute(p, 1);
    
    		p->subs[idx].f.frametype = AST_FRAME_DTMF_BEGIN;
    		p->subs[idx].f.subclass.integer = res & 0xff;
    
    		analog_handle_dtmf(p, ast, idx, &f);
    
    	switch (res) {
    	case ANALOG_EVENT_ALARM:
    	case ANALOG_EVENT_POLARITY:
    	case ANALOG_EVENT_ONHOOK:
    		/* add length of "ANALOG " */
    		data_size += 7;
    		subclass = analog_event2str(res);
    		data_size += strlen(subclass);
    
    		cause_code = ast_alloca(data_size);
    
    		cause_code->ast_cause = AST_CAUSE_NORMAL_CLEARING;
    
    		ast_copy_string(cause_code->chan_name, ast_channel_name(ast), AST_CHANNEL_NAME);
    		snprintf(cause_code->code, data_size - sizeof(*cause_code) + 1, "ANALOG %s", subclass);
    		break;
    	default:
    		break;
    	}
    
    	switch (res) {
    	case ANALOG_EVENT_EC_DISABLED:
    
    		ast_verb(3, "Channel %d echo canceler disabled due to CED detection\n", p->channel);
    
    		analog_set_echocanceller(p, 0);
    		break;
    #ifdef HAVE_DAHDI_ECHOCANCEL_FAX_MODE
    	case ANALOG_EVENT_TX_CED_DETECTED:
    		ast_verb(3, "Channel %d detected a CED tone towards the network.\n", p->channel);
    		break;
    	case ANALOG_EVENT_RX_CED_DETECTED:
    		ast_verb(3, "Channel %d detected a CED tone from the network.\n", p->channel);
    		break;
    	case ANALOG_EVENT_EC_NLP_DISABLED:
    		ast_verb(3, "Channel %d echo canceler disabled its NLP.\n", p->channel);
    		break;
    	case ANALOG_EVENT_EC_NLP_ENABLED:
    		ast_verb(3, "Channel %d echo canceler enabled its NLP.\n", p->channel);
    
    		break;
    #endif
    	case ANALOG_EVENT_PULSE_START:
    		/* Stop tone if there's a pulse start and the PBX isn't started */
    
    		if (!ast_channel_pbx(ast))
    
    			analog_play_tone(p, ANALOG_SUB_REAL, -1);
    		break;
    	case ANALOG_EVENT_DIALCOMPLETE:
    
    		x = analog_is_dialing(p, idx);
    
    		if (!x) { /* if not still dialing in driver */
    			analog_set_echocanceller(p, 1);
    			if (p->echobreak) {
    				analog_train_echocanceller(p);
    				ast_copy_string(p->dop.dialstr, p->echorest, sizeof(p->dop.dialstr));
    				p->dop.op = ANALOG_DIAL_OP_REPLACE;
    
    				if (analog_dial_digits(p, ANALOG_SUB_REAL, &p->dop)) {
    					int dial_err = errno;
    					ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(dial_err));
    				}
    
    				analog_set_dialing(p, 0);
    
    				if ((mysig == ANALOG_SIG_E911) || (mysig == ANALOG_SIG_FGC_CAMA) || (mysig == ANALOG_SIG_FGC_CAMAMF)) {
    					/* if thru with dialing after offhook */
    
    					if (ast_channel_state(ast) == AST_STATE_DIALING_OFFHOOK) {
    
    						ast_setstate(ast, AST_STATE_UP);
    
    						p->subs[idx].f.frametype = AST_FRAME_CONTROL;
    						p->subs[idx].f.subclass.integer = AST_CONTROL_ANSWER;
    
    						break;
    					} else { /* if to state wait for offhook to dial rest */
    						/* we now wait for off hook */
    						ast_setstate(ast,AST_STATE_DIALING_OFFHOOK);
    					}
    				}
    
    				if (ast_channel_state(ast) == AST_STATE_DIALING) {
    
    					if (analog_have_progressdetect(p)) {
    						ast_debug(1, "Done dialing, but waiting for progress detection before doing more...\n");
    					} else if (analog_check_confirmanswer(p) || (!p->dialednone
    
    						&& ((mysig == ANALOG_SIG_EM) || (mysig == ANALOG_SIG_EM_E1)
    							|| (mysig == ANALOG_SIG_EMWINK) || (mysig == ANALOG_SIG_FEATD)
    							|| (mysig == ANALOG_SIG_FEATDMF_TA) || (mysig == ANALOG_SIG_FEATDMF)
    							|| (mysig == ANALOG_SIG_E911) || (mysig == ANALOG_SIG_FGC_CAMA)
    							|| (mysig == ANALOG_SIG_FGC_CAMAMF) || (mysig == ANALOG_SIG_FEATB)
    							|| (mysig == ANALOG_SIG_SF) || (mysig == ANALOG_SIG_SFWINK)
    							|| (mysig == ANALOG_SIG_SF_FEATD) || (mysig == ANALOG_SIG_SF_FEATDMF)
    							|| (mysig == ANALOG_SIG_SF_FEATB)))) {
    
    						ast_setstate(ast, AST_STATE_RINGING);
    					} else if (!p->answeronpolarityswitch) {
    						ast_setstate(ast, AST_STATE_UP);
    
    						p->subs[idx].f.frametype = AST_FRAME_CONTROL;
    						p->subs[idx].f.subclass.integer = AST_CONTROL_ANSWER;
    
    						/* If aops=0 and hops=1, this is necessary */
    						p->polarity = POLARITY_REV;
    					} else {
    						/* Start clean, so we can catch the change to REV polarity when party answers */
    						p->polarity = POLARITY_IDLE;
    					}
    				}
    			}
    		}
    		break;
    	case ANALOG_EVENT_ALARM:
    
    		analog_get_and_handle_alarms(p);
    
    		cause_code->ast_cause = AST_CAUSE_NETWORK_OUT_OF_ORDER;
    
    		ast_queue_control_data(ast, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size);
    
    		ast_channel_hangupcause_hash_set(ast, cause_code, data_size);
    
    		switch (p->sig) {
    		case ANALOG_SIG_FXOLS:
    		case ANALOG_SIG_FXOGS:
    		case ANALOG_SIG_FXOKS:
    
    			p->fxsoffhookstate = 0;
    			p->onhooktime = time(NULL);
    			p->msgstate = -1;
    
    			/* Check for some special conditions regarding call waiting */
    
    			if (idx == ANALOG_SUB_REAL) {
    
    				/* The normal line was hung up */
    				if (p->subs[ANALOG_SUB_CALLWAIT].owner) {
    
    					/* 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.
    						 * This is now a normal hangup.
    						 */
    						analog_set_echocanceller(p, 0);
    						return NULL;
    					}
    
    
    					/* There's a call waiting call, so ring the phone, but make it unowned in the mean time */
    					analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_REAL);
    
    					ast_verb(3, "Channel %d still has (callwait) call, ringing phone\n", p->channel);
    
    					analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT);
    					analog_stop_callwait(p);
    
    					analog_set_new_owner(p, NULL);
    
    					/* Don't start streaming audio yet if the incoming call isn't up yet */
    
    					if (ast_channel_state(p->subs[ANALOG_SUB_REAL].owner) != AST_STATE_UP) {
    
    						analog_set_dialing(p, 1);
    
    					/* Unlock the call-waiting call that we swapped to real-call. */
    					ast_channel_unlock(p->subs[ANALOG_SUB_REAL].owner);
    
    					analog_ring(p);
    				} else if (p->subs[ANALOG_SUB_THREEWAY].owner) {
    					unsigned int mssinceflash;
    
    
    					/* Need to hold the lock for real-call, private, and 3-way call */
    					analog_lock_sub_owner(p, ANALOG_SUB_THREEWAY);
    
    					if (!p->subs[ANALOG_SUB_THREEWAY].owner) {
    						ast_log(LOG_NOTICE, "Whoa, threeway disappeared kinda randomly.\n");
    
    						/* Just hangup */
    						return NULL;
    					}
    					if (p->owner != ast) {
    						ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
    						ast_log(LOG_WARNING, "This isn't good...\n");
    						/* Just hangup */
    
    					mssinceflash = ast_tvdiff_ms(ast_tvnow(), p->flashtime);
    					ast_debug(1, "Last flash was %d ms ago\n", mssinceflash);
    					if (mssinceflash < MIN_MS_SINCE_FLASH) {
    						/* It hasn't been long enough since the last flashook.  This is probably a bounce on
    						   hanging up.  Hangup both channels now */
    						ast_debug(1, "Looks like a bounced flash, hanging up both calls on %d\n", p->channel);
    
    						ast_queue_hangup_with_cause(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CAUSE_NO_ANSWER);
    						ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
    
    						ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
    
    					} else if ((ast_channel_pbx(ast)) || (ast_channel_state(ast) == AST_STATE_UP)) {
    
    							int inthreeway;
    
    							inthreeway = p->subs[ANALOG_SUB_THREEWAY].inthreeway;
    
    							/* In any case this isn't a threeway call anymore */
    							analog_set_inthreeway(p, ANALOG_SUB_REAL, 0);
    							analog_set_inthreeway(p, ANALOG_SUB_THREEWAY, 0);
    
    
    							/* Only attempt transfer if the phone is ringing; why transfer to busy tone eh? */
    
    							if (!p->transfertobusy && ast_channel_state(ast) == AST_STATE_BUSY) {
    
    								/* Swap subs and dis-own channel */
    								analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
    
    								/* Unlock the 3-way call that we swapped to real-call. */
    								ast_channel_unlock(p->subs[ANALOG_SUB_REAL].owner);
    
    								analog_set_new_owner(p, NULL);
    
    								/* Ring the phone */
    								analog_ring(p);
    							} else {
    
    								res = analog_attempt_transfer(p, inthreeway);
    
    								if (res < 0) {
    									/* Transfer attempt failed. */
    
    									ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
    
    									ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
    
    								} else if (res) {
    									/* Don't actually hang up at this point */
    									break;
    								}
    							}
    						} else {
    							ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
    
    							ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
    
    						}
    					} else {
    						/* Swap subs and dis-own channel */
    						analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
    
    						/* Unlock the 3-way call that we swapped to real-call. */
    						ast_channel_unlock(p->subs[ANALOG_SUB_REAL].owner);
    
    						analog_set_new_owner(p, NULL);
    
    						/* Ring the phone */
    						analog_ring(p);
    					}
    				}
    			} else {
    
    				ast_log(LOG_WARNING, "Got a hangup and my index is %d?\n", idx);
    
    			}
    			/* Fall through */
    		default:
    			analog_set_echocanceller(p, 0);
    			return NULL;
    		}
    		break;
    	case ANALOG_EVENT_RINGOFFHOOK:
    
    		/* for E911, its supposed to wait for offhook then dial
    		   the second half of the dial string */
    
    		if (((mysig == ANALOG_SIG_E911) || (mysig == ANALOG_SIG_FGC_CAMA) || (mysig == ANALOG_SIG_FGC_CAMAMF)) && (ast_channel_state(ast) == AST_STATE_DIALING_OFFHOOK)) {
    
    			c = strchr(p->dialdest, '/');
    
    			}
    			if (*c) {
    				snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*0%s#", c);
    			} else {
    				ast_copy_string(p->dop.dialstr,"M*2#", sizeof(p->dop.dialstr));
    			}
    
    			if (strlen(p->dop.dialstr) > 4) {
    				memset(p->echorest, 'w', sizeof(p->echorest) - 1);
    				strcpy(p->echorest + (p->echotraining / 401) + 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';
    
    			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 NULL;
    			}
    
    			analog_set_dialing(p, 1);
    
    			return &p->subs[idx].f;
    
    		}
    		switch (p->sig) {
    		case ANALOG_SIG_FXOLS:
    		case ANALOG_SIG_FXOGS:
    		case ANALOG_SIG_FXOKS:
    
    			p->fxsoffhookstate = 1;
    
    			switch (ast_channel_state(ast)) {
    
    			case AST_STATE_RINGING:
    				analog_set_echocanceller(p, 1);
    				analog_train_echocanceller(p);
    
    				p->subs[idx].f.frametype = AST_FRAME_CONTROL;
    				p->subs[idx].f.subclass.integer = AST_CONTROL_ANSWER;
    
    				/* Make sure it stops ringing */
    
    				analog_set_needringing(p, 0);
    
    				analog_off_hook(p);
    				ast_debug(1, "channel %d answered\n", p->channel);
    
    
    				/* Cancel any running CallerID spill */