Newer
Older
/* Return provides old linear_mode setting or error indication */
return analog_callbacks.set_linear_mode(p->chan_pvt, sub, linear_mode);
}
return -1;
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)
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));
return -1;
}
p->dialednone = 0;
analog_set_outgoing(p, 1);
mysig = p->sig;
if (p->outsigmod > -1) {
mysig = p->outsigmod;
switch (mysig) {
case ANALOG_SIG_FXOLS:
case ANALOG_SIG_FXOGS:
case ANALOG_SIG_FXOKS:
if (p->owner == ast) {
/* Normal ring, on hook */
/* Don't send audio while on hook, until the call is answered */
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) {
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);
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));
} else {
p->callwait_num[0] = '\0';
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));
} else {
p->callwait_name[0] = '\0';
/* Call waiting tone instead */
if (analog_callwait(p)) {
return -1;
}
/* Make ring-back */
if (analog_play_tone(p, ANALOG_SUB_CALLWAIT, ANALOG_TONE_RINGTONE)) {
ast_log(LOG_WARNING, "Unable to generate call-wait ring-back on channel %s\n", ast_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;
if (l) {
ast_copy_string(p->lastcid_num, l, sizeof(p->lastcid_num));
} else {
p->lastcid_num[0] = '\0';
}
if (n) {
ast_copy_string(p->lastcid_name, n, sizeof(p->lastcid_name));
} else {
p->lastcid_name[0] = '\0';
if (p->use_callerid) {
p->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:
Jeff Peeler
committed
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 (c) {
} else {
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;
if (l) {
snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T*%s*%s*", l, c);
} else {
snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T**%s*", c);
break;
case ANALOG_SIG_FEATDMF:
l = ast_channel_connected(ast)->id.number.valid ? ast_channel_connected(ast)->id.number.str : NULL;
if (l) {
snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*00%s#*%s#", l, c);
} else {
snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*02#*%s#", c);
break;
case ANALOG_SIG_FEATDMF_TA:
{
const char *cic = "", *ozz = "";
/* If you have to go through a Tandem Access point you need to use this */
#ifndef STANDALONE
ozz = pbx_builtin_getvar_helper(p->owner, "FEATDMF_OZZ");
if (!ozz) {
ozz = analog_defaultozz;
cic = pbx_builtin_getvar_helper(p->owner, "FEATDMF_CIC");
if (!cic) {
cic = analog_defaultcic;
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
#endif
if (!ozz || !cic) {
ast_log(LOG_WARNING, "Unable to dial channel of type feature group D MF tandem access without CIC or OZZ set\n");
return -1;
}
snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%s%s#", ozz, cic);
snprintf(p->finaldial, sizeof(p->finaldial), "M*%s#", c);
p->whichwink = 0;
}
break;
case ANALOG_SIG_E911:
ast_copy_string(p->dop.dialstr, "M*911#", sizeof(p->dop.dialstr));
break;
case ANALOG_SIG_FGC_CAMA:
snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "P%s", c);
break;
case ANALOG_SIG_FGC_CAMAMF:
case ANALOG_SIG_FEATB:
snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%s#", c);
break;
default:
if (p->pulse) {
snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "P%sw", c);
} else {
snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T%sw", c);
break;
}
if (p->echotraining && (strlen(p->dop.dialstr) > 4)) {
memset(p->echorest, 'w', sizeof(p->echorest) - 1);
strcpy(p->echorest + (p->echotraining / 400) + 1, p->dop.dialstr + strlen(p->dop.dialstr) - 2);
p->echorest[sizeof(p->echorest) - 1] = '\0';
p->echobreak = 1;
p->dop.dialstr[strlen(p->dop.dialstr)-2] = '\0';
} else {
p->echobreak = 0;
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;
}
} else {
ast_debug(1, "Deferring dialing...\n");
analog_set_dialing(p, 1);
if (ast_strlen_zero(c)) {
p->dialednone = 1;
ast_setstate(ast, AST_STATE_DIALING);
break;
default:
ast_debug(1, "not yet implemented\n");
return -1;
}
return 0;
}
int analog_hangup(struct analog_pvt *p, struct ast_channel *ast)
{
int res;
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));
p->origcid_num = NULL;
}
if (p->origcid_name) {
ast_copy_string(p->cid_name, p->origcid_name, sizeof(p->cid_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->polarity = POLARITY_IDLE;
analog_set_linear_mode(p, idx, 0);
switch (idx) {
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);
}
} else {
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_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 */
if (analog_is_off_hook(p)) {
analog_hangup_polarityswitch(p);
analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION);
} else {
analog_play_tone(p, ANALOG_SUB_REAL, -1);
break;
case ANALOG_SIG_FXSGS:
case ANALOG_SIG_FXSLS:
case ANALOG_SIG_FXSKS:
/* Make sure we're not made available for at least two seconds assuming
we were actually used for an inbound or outbound call. */
if (ast_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:
/* 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);
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
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)
{
struct ast_frame *f = *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 */
for (;;) {
/* Wait for the first digit (up to specified ms). */
c = ast_waitfordigit(chan, ms);
/* if timeout, hangup or error, return as such */
if (c < 1) {
*str++ = c;
*str = 0;
if (strchr(term, c)) {
}
}
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);
return 0;
}
return -1;
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);
}
return -1;
}
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);
}
return NULL;
}
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);
}
return -1;
#define ANALOG_NEED_MFDETECT(p) (((p)->sig == ANALOG_SIG_FEATDMF) || ((p)->sig == ANALOG_SIG_FEATDMF_TA) || ((p)->sig == ANALOG_SIG_E911) || ((p)->sig == ANALOG_SIG_FGC_CAMA) || ((p)->sig == ANALOG_SIG_FGC_CAMAMF) || ((p)->sig == ANALOG_SIG_FEATB))
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
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;
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_hangup(chan);
goto quit;
}
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:
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);
} else {
analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_DTMF);
memset(dtmfbuf, 0, sizeof(dtmfbuf));
/* Wait for the first digit only if immediate=no */
if (!p->immediate) {
/* Wait for the first digit (up to 5 seconds). */
res = ast_waitfordigit(chan, 5000);
} else {
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);
if (res > 0) {
res = analog_my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "*", 3000);
}
if (res < 1) {
analog_dsp_reset_and_flush_digits(p);
break;
case ANALOG_SIG_FEATDMF_TA:
res = analog_my_getsigstr(chan, dtmfbuf + 1, "#", 3000);
if (res < 1) {
analog_dsp_reset_and_flush_digits(p);
goto quit;
}
dtmfbuf[0] = 0;
/* Wait for the first digit (up to 5 seconds). */
res = ast_waitfordigit(chan, 5000);
if (res <= 0) {
break;
}
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)) {
goto quit;
}
dtmfbuf[0] = 0;
/* Wait for the first digit (up to 5 seconds). */
res = ast_waitfordigit(chan, 5000);
if (res <= 0) {
break;
}
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) {
analog_off_hook(p);
res = analog_my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "#", 3000);
}
if (res < 1) {
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);
if (res < 1) {
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);
if (res > 0) {
res = analog_my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "*", 3000);
}
if (res < 1) {
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)) {
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
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);
} else {
ast_set_callerid(chan, s1, NULL, s1);
ast_copy_string(exten, s2, sizeof(exten));
} else {
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);