mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-03 11:11:05 +00:00
sig_analog: Add Call Waiting Deluxe support.
Adds support for Call Waiting Deluxe options to enhance the current call waiting feature. As part of this change, a mechanism is also added that allows a channel driver to queue an audio file for Dial() to play, which is necessary for the announcement function. ASTERISK-30373 #close Resolves: #271 UserNote: Call Waiting Deluxe can now be enabled for FXS channels by enabling its corresponding option.
This commit is contained in:
committed by
George Joseph
parent
9c58dec9b0
commit
6002b6fdcb
@@ -1590,6 +1590,161 @@ static int analog_handles_digit(struct ast_frame *f)
|
||||
}
|
||||
}
|
||||
|
||||
enum callwaiting_deluxe_option {
|
||||
CWD_CONFERENCE = '3',
|
||||
CWD_HOLD = '6',
|
||||
CWD_DROP = '7',
|
||||
CWD_ANNOUNCEMENT = '8',
|
||||
CWD_FORWARD = '9',
|
||||
};
|
||||
|
||||
static const char *callwaiting_deluxe_optname(int option)
|
||||
{
|
||||
switch (option) {
|
||||
case CWD_CONFERENCE:
|
||||
return "CONFERENCE";
|
||||
case CWD_HOLD:
|
||||
return "HOLD";
|
||||
case CWD_DROP:
|
||||
return "DROP";
|
||||
case CWD_ANNOUNCEMENT:
|
||||
return "ANNOUNCEMENT";
|
||||
case CWD_FORWARD:
|
||||
return "FORWARD";
|
||||
default:
|
||||
return "DEFAULT";
|
||||
}
|
||||
}
|
||||
|
||||
int analog_callwaiting_deluxe(struct analog_pvt *p, int option)
|
||||
{
|
||||
const char *announce_var;
|
||||
char announcement[PATH_MAX];
|
||||
|
||||
ast_debug(1, "Handling Call Waiting on channel %d with option %c: treatment %s\n", p->channel, option, callwaiting_deluxe_optname(option));
|
||||
|
||||
if (!p->subs[ANALOG_SUB_CALLWAIT].owner) {
|
||||
/* This can happen if the caller hook flashes and the call waiting hangs up before the CWD timer expires (1 second) */
|
||||
ast_debug(1, "Call waiting call disappeared before it could be handled?\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
analog_lock_sub_owner(p, ANALOG_SUB_CALLWAIT);
|
||||
if (!p->subs[ANALOG_SUB_CALLWAIT].owner) {
|
||||
ast_log(LOG_WARNING, "Whoa, the call-waiting call disappeared.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Note that when p->callwaitingdeluxepending, dahdi_write will drop incoming frames to the channel,
|
||||
* since the user shouldn't hear anything after flashing until either a DTMF has been received
|
||||
* or it's been a second and the decision is made automatically. */
|
||||
|
||||
switch (option) {
|
||||
case CWD_CONFERENCE:
|
||||
/* We should never have a call waiting if we have a 3-way anyways, but check just in case,
|
||||
* there better be no existing SUB_THREEWAY since we're going to make one (and then swap the call wait to it) */
|
||||
if (p->subs[ANALOG_SUB_THREEWAY].owner) {
|
||||
ast_channel_unlock(p->subs[ANALOG_SUB_CALLWAIT].owner);
|
||||
ast_log(LOG_ERROR, "Already have a 3-way call on channel %d, can't conference!\n", p->channel);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* To conference the incoming call, swap it from SUB_CALLWAIT to SUB_THREEWAY,
|
||||
* and then the existing 3-way logic will ensure that flashing again will drop the call waiting */
|
||||
analog_alloc_sub(p, ANALOG_SUB_THREEWAY);
|
||||
analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_CALLWAIT);
|
||||
analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT);
|
||||
|
||||
ast_verb(3, "Building conference call with %s and %s\n", ast_channel_name(p->subs[ANALOG_SUB_THREEWAY].owner), ast_channel_name(p->subs[ANALOG_SUB_REAL].owner));
|
||||
analog_set_inthreeway(p, ANALOG_SUB_THREEWAY, 1);
|
||||
analog_set_inthreeway(p, ANALOG_SUB_REAL, 1);
|
||||
|
||||
if (ast_channel_state(p->subs[ANALOG_SUB_THREEWAY].owner) == AST_STATE_RINGING) {
|
||||
ast_setstate(p->subs[ANALOG_SUB_THREEWAY].owner, AST_STATE_UP);
|
||||
ast_queue_control(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CONTROL_ANSWER);
|
||||
/* Stop the ringing on the call wait channel (yeah, apparently this is how it's done) */
|
||||
ast_queue_hold(p->subs[ANALOG_SUB_THREEWAY].owner, p->mohsuggest);
|
||||
ast_queue_unhold(p->subs[ANALOG_SUB_THREEWAY].owner);
|
||||
}
|
||||
analog_stop_callwait(p);
|
||||
|
||||
ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); /* Unlock what was originally SUB_CALLWAIT */
|
||||
break;
|
||||
case CWD_HOLD: /* The CI-7112 Visual Director sends "HOLD" for "Play hold message" rather than "ANNOUNCEMENT". For default behavior, nothing is actually sent. */
|
||||
case CWD_ANNOUNCEMENT:
|
||||
/* We can't just call ast_streamfile here, this thread isn't responsible for media on the call waiting channel.
|
||||
* Indicate to the dialing channel in app_dial that it needs to play media.
|
||||
*
|
||||
* This is a lot easier than other ways of trying to send early media to the channel
|
||||
* (such as every call from the core to dahdi_read, sending the channel one frame of the audio file, etc.)
|
||||
*/
|
||||
|
||||
/* There's not a particularly good stock audio prompt to use here. The Pat Fleet library has some better
|
||||
* ones but we want one that is also in the default Allison Smith library. "One moment please" works okay.
|
||||
* Check if a variable containing the prompt to use was specified on the call waiting channel, and
|
||||
* fall back to a reasonable default if not. */
|
||||
|
||||
/* The SUB_CALLWAIT channel is already locked here, no need to lock and unlock to get the variable. */
|
||||
announce_var = pbx_builtin_getvar_helper(p->subs[ANALOG_SUB_CALLWAIT].owner, "CALLWAITDELUXEANNOUNCEMENT");
|
||||
ast_copy_string(announcement, S_OR(announce_var, "one-moment-please"), sizeof(announcement));
|
||||
ast_debug(2, "Call Waiting Deluxe announcement for %s: %s\n", ast_channel_name(p->subs[ANALOG_SUB_CALLWAIT].owner), announcement);
|
||||
ast_channel_unlock(p->subs[ANALOG_SUB_CALLWAIT].owner);
|
||||
/* Tell app_dial what file to play. */
|
||||
ast_queue_control_data(p->subs[ANALOG_SUB_CALLWAIT].owner, AST_CONTROL_PLAYBACK_BEGIN, announcement, strlen(announcement) + 1);
|
||||
/* Unlike all the other options, the call waiting is still active with this option,
|
||||
* so we don't call analog_stop_callwait(p)
|
||||
* The call waiting will continue to be here, and at some later point the user can flash again and choose a finalizing option
|
||||
* (or even queue the announcement again... and again... and again...)
|
||||
*/
|
||||
break;
|
||||
case CWD_FORWARD:
|
||||
/* Go away, call waiting, call again some other day... */
|
||||
analog_stop_callwait(p);
|
||||
/* Can't use p->call_forward exten because that's for *72 forwarding, and sig_analog doesn't
|
||||
* have a Busy/Don't Answer call forwarding exten internally, so let the dialplan deal with it.
|
||||
* by sending the call to the 'f' extension.
|
||||
*/
|
||||
ast_channel_call_forward_set(p->subs[ANALOG_SUB_CALLWAIT].owner, "f");
|
||||
ast_channel_unlock(p->subs[ANALOG_SUB_CALLWAIT].owner);
|
||||
/* app_dial already has a verbose message for forwarding, so we don't really need one here also since that does the job */
|
||||
break;
|
||||
case CWD_DROP:
|
||||
/* Fall through: logic is identical to hold, except we drop the original call right after we swap. */
|
||||
default:
|
||||
/* Swap to call-wait, same as with the non-deluxe call waiting handling. */
|
||||
analog_swap_subs(p, ANALOG_SUB_REAL, ANALOG_SUB_CALLWAIT);
|
||||
analog_play_tone(p, ANALOG_SUB_REAL, -1);
|
||||
analog_set_new_owner(p, p->subs[ANALOG_SUB_REAL].owner);
|
||||
ast_debug(1, "Making %s the new owner\n", ast_channel_name(p->owner));
|
||||
if (ast_channel_state(p->subs[ANALOG_SUB_REAL].owner) == AST_STATE_RINGING) {
|
||||
ast_setstate(p->subs[ANALOG_SUB_REAL].owner, AST_STATE_UP);
|
||||
ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_ANSWER);
|
||||
}
|
||||
analog_stop_callwait(p);
|
||||
|
||||
if (option == CWD_DROP) {
|
||||
/* Disconnect the previous call (the original call is now the SUB_CALLWAIT since we swapped above) */
|
||||
ast_queue_hangup(p->subs[ANALOG_SUB_CALLWAIT].owner);
|
||||
ast_verb(3, "Dropping original call and swapping to call waiting on %s\n", ast_channel_name(p->subs[ANALOG_SUB_REAL].owner));
|
||||
} else {
|
||||
/* Start music on hold if appropriate */
|
||||
if (!p->subs[ANALOG_SUB_CALLWAIT].inthreeway) {
|
||||
ast_queue_hold(p->subs[ANALOG_SUB_CALLWAIT].owner, p->mohsuggest);
|
||||
}
|
||||
ast_verb(3, "Holding original call and swapping to call waiting on %s\n", ast_channel_name(p->subs[ANALOG_SUB_REAL].owner));
|
||||
}
|
||||
|
||||
/* Stop ringing on the incoming call */
|
||||
ast_queue_hold(p->subs[ANALOG_SUB_REAL].owner, p->mohsuggest);
|
||||
ast_queue_unhold(p->subs[ANALOG_SUB_REAL].owner);
|
||||
|
||||
/* Unlock the call-waiting call that we swapped to real-call. */
|
||||
ast_channel_unlock(p->subs[ANALOG_SUB_REAL].owner);
|
||||
}
|
||||
analog_update_conf(p);
|
||||
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;
|
||||
@@ -1627,6 +1782,50 @@ void analog_handle_dtmf(struct analog_pvt *p, struct ast_channel *ast, enum anal
|
||||
p->subs[idx].f.frametype = AST_FRAME_NULL;
|
||||
p->subs[idx].f.subclass.integer = 0;
|
||||
*dest = &p->subs[idx].f;
|
||||
} else if (p->callwaitingdeluxepending) {
|
||||
if (f->frametype == AST_FRAME_DTMF_END) {
|
||||
unsigned int mssinceflash = ast_tvdiff_ms(ast_tvnow(), p->flashtime);
|
||||
p->callwaitingdeluxepending = 0;
|
||||
|
||||
/* This is the case where a user explicitly took action (made a decision)
|
||||
* for Call Waiting Deluxe.
|
||||
* Because we already handled the hook flash, if the user doesn't do
|
||||
* anything within a second, then we still need to eventually take
|
||||
* the default action (swap) for the call waiting.
|
||||
*
|
||||
* dahdi_write will also drop audio if callwaitingdeluxepending is set HIGH,
|
||||
* and also check if flashtime hits 1000, in which case it will set the flag LOW and then take the
|
||||
* default action, e.g. analog_callwaiting_deluxe(p, 0);
|
||||
*/
|
||||
|
||||
/* Slightly less than 1000, so there's no chance of a race condition
|
||||
* between do_monitor when it sees flashtime hitting 1000 and us. */
|
||||
if (mssinceflash > 990) {
|
||||
/* This was more than a second ago, clear the flag and process normally. */
|
||||
/* Because another thread has to monitor channels with pending CWDs,
|
||||
* in theory, we shouldn't need to check this here. */
|
||||
ast_debug(1, "It's been %u ms since the last flash, this is not a Call Waiting Deluxe DTMF\n", mssinceflash);
|
||||
analog_cb_handle_dtmf(p, ast, idx, dest);
|
||||
return;
|
||||
}
|
||||
/* Okay, actually do something now. */
|
||||
switch (f->subclass.integer) {
|
||||
case CWD_CONFERENCE:
|
||||
case CWD_HOLD:
|
||||
case CWD_DROP:
|
||||
case CWD_ANNOUNCEMENT:
|
||||
case CWD_FORWARD:
|
||||
ast_debug(1, "Got some DTMF, but it's for Call Waiting Deluxe: %c\n", f->subclass.integer);
|
||||
analog_callwaiting_deluxe(p, f->subclass.integer);
|
||||
break;
|
||||
default:
|
||||
ast_log(LOG_WARNING, "Invalid Call Waiting Deluxe option (%c), using default\n", f->subclass.integer);
|
||||
analog_callwaiting_deluxe(p, 0);
|
||||
}
|
||||
}
|
||||
p->subs[idx].f.frametype = AST_FRAME_NULL;
|
||||
p->subs[idx].f.subclass.integer = 0;
|
||||
*dest = &p->subs[idx].f;
|
||||
} else {
|
||||
analog_cb_handle_dtmf(p, ast, idx, dest);
|
||||
}
|
||||
@@ -2991,6 +3190,7 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
|
||||
break;
|
||||
}
|
||||
}
|
||||
p->callwaitingdeluxepending = 0;
|
||||
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) {
|
||||
@@ -3306,6 +3506,7 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
|
||||
}
|
||||
/* Remember last time we got a flash-hook */
|
||||
gettimeofday(&p->flashtime, NULL);
|
||||
p->callwaitingdeluxepending = 0;
|
||||
switch (mysig) {
|
||||
case ANALOG_SIG_FXOLS:
|
||||
case ANALOG_SIG_FXOGS:
|
||||
@@ -3334,6 +3535,20 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
|
||||
goto winkflashdone;
|
||||
}
|
||||
|
||||
/* If line has Call Waiting Deluxe, see what the user wants to do.
|
||||
* Only do this if this is an as yet unanswered call waiting, not an existing, answered SUB_CALLWAIT. */
|
||||
if (ast_channel_state(p->subs[ANALOG_SUB_CALLWAIT].owner) == AST_STATE_RINGING) {
|
||||
if (p->callwaitingdeluxe) {
|
||||
/* This thread cannot block, so just set the flag that we need
|
||||
* to wait for a Call Waiting Deluxe option (or let it time out),
|
||||
* and then we're done for now. */
|
||||
ast_channel_unlock(p->subs[ANALOG_SUB_CALLWAIT].owner);
|
||||
p->callwaitingdeluxepending = 1;
|
||||
ast_debug(1, "Deferring call waiting manipulation, waiting for Call Waiting Deluxe option from user\n");
|
||||
goto winkflashdone;
|
||||
}
|
||||
}
|
||||
|
||||
/* Swap to call-wait */
|
||||
analog_swap_subs(p, ANALOG_SUB_REAL, ANALOG_SUB_CALLWAIT);
|
||||
analog_play_tone(p, ANALOG_SUB_REAL, -1);
|
||||
@@ -3868,6 +4083,7 @@ void *analog_handle_init_event(struct analog_pvt *i, int event)
|
||||
res = analog_off_hook(i);
|
||||
i->fxsoffhookstate = 1;
|
||||
i->cshactive = 0;
|
||||
i->callwaitingdeluxepending = 0;
|
||||
if (res && (errno == EBUSY)) {
|
||||
break;
|
||||
}
|
||||
|
Reference in New Issue
Block a user