Newer
Older
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);
} else {
if (*(s1 + 2)) {
ast_set_callerid(chan, s1 + 2, NULL, s1 + 2);
}
}
ast_copy_string(exten, s2 + 1, sizeof(exten));
} else {
ast_copy_string(exten, s1 + 2, sizeof(exten));
}
} else {
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')) {
if (*(s2 + 2)) {
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));
} else {
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)) {
/*
* 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);
goto quit;
}
}
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);
}
goto quit;
} else {
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);
if (res < 0) {
ast_log(LOG_WARNING, "Unable to start special tone on %d\n", p->channel);
} else {
res = ast_streamfile(chan, "ss-noservice", ast_channel_language(chan));
if (res >= 0) {
ast_waitstream(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) {
timeout = 999999;
while (len < AST_MAX_EXTENSION-1) {
/* Read digit unless it's supposed to be immediate, in which case the
only answer is 's' */
if (p->immediate) {
res = 's';
} else {
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_hangup(chan);
goto quit;
} else if (res) {
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);
} else {
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);
if (res) {
usleep(500000);
res = analog_play_tone(p, idx, -1);
sleep(1);
memset(exten, 0, sizeof(exten));
res = analog_play_tone(p, idx, ANALOG_TONE_DIALTONE);
len = 0;
getforward = 0;
} else {
res = analog_play_tone(p, idx, -1);
ast_channel_exten_set(chan, exten);
if (!ast_strlen_zero(p->cid_num)) {
if (!p->hidecallerid) {
ast_set_callerid(chan, p->cid_num, NULL, p->cid_num);
} else {
ast_set_callerid(chan, NULL, NULL, p->cid_num);
}
if (!ast_strlen_zero(p->cid_name)) {
if (!p->hidecallerid) {
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);
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,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));
if (!res) {
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. */
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 */
if (nbridge) {
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_wait_event(p);
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);
ast_hangup(chan);
goto quit;
}
} 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>",
break;
}
if (!timeout) {
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 oldlinearity;
int timeout_ms;
int ms;
struct timeval start = ast_tvnow();
cs = NULL;
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. */
for (;;) {
struct ast_frame *f;
ms = ast_remaining_ms(start, timeout_ms);
res = ast_waitfor(chan, ms);
if (res <= 0) {
/*
* 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);
}
ast_frfree(f);
if (ast_channel_state(chan) == AST_STATE_RING ||
ast_channel_state(chan) == AST_STATE_RINGING) {
break; /* Got ring */
ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
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)) {
number = dtmfcid;
} else {
number = NULL;
/* 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 (res == 0) {
break;
if (ev == ANALOG_EVENT_NOALARM) {
Richard Mudgett
committed
analog_set_alarm(p, 0);
if (p->cid_signalling == CID_SIG_V23_JP) {
if (ev == ANALOG_EVENT_RINGBEGIN) {
analog_off_hook(p);
usleep(1);
ev = ANALOG_EVENT_NONE;
break;
}
}
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))) {
struct ast_frame *f;
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)
break; /* Got ring */
}
if (analog_distinctive_ring(chan, p, idx, NULL)) {
ast_log(LOG_WARNING, "CallerID returned with error on channel '%s'\n", ast_channel_name(chan));
} else {
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 "
ast_channel_name(chan));
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 };
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) {
Richard Mudgett
committed
analog_set_alarm(p, 0);
} 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) {
break;
}
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)) {
if (res < 0) {
ast_log(LOG_WARNING, "CallerID returned with error on channel '%s'\n", ast_channel_name(chan));
} else {
ast_log(LOG_WARNING, "Unable to get caller ID space\n");
}
} else {
cs = NULL;
if (number) {
ast_shrink_phone_number(number);
ast_set_callerid(chan, number, name, number);
if (cs) {
callerid_free(cs);
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);
if (res < 0) {
ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", p->channel);
ast_hangup(chan);
quit:
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;
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");
}
mysig = p->sig;
if (p->outsigmod > -1) {
mysig = p->outsigmod;
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;
res = analog_get_event(p);
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:
if (p->inalarm) {
break;
}
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));
}
p->echobreak = 0;
} else {
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:
Richard Mudgett
committed
analog_set_alarm(p, 1);
analog_get_and_handle_alarms(p);
cause_code->ast_cause = AST_CAUSE_NETWORK_OUT_OF_ORDER;
case ANALOG_EVENT_ONHOOK:
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:
analog_start_polarityswitch(p);
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 */
return NULL;
}
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)) {
if (p->transfer) {
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:
if (p->inalarm) {
break;
}
/* 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) {
} else {
c = 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';
} else {
p->echobreak = 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);
}
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_off_hook(p);
ast_debug(1, "channel %d answered\n", p->channel);
/* Cancel any running CallerID spill */