return -1;
}
+ if (p->sig == SIG_FXOLS || p->sig == SIG_FXOKS || p->sig == SIG_FXOGS) {
+ struct analog_pvt *analog_p = p->sig_pvt;
+ if (analog_p->callwaitingdeluxepending) {
+ unsigned int mssinceflash = ast_tvdiff_ms(ast_tvnow(), analog_p->flashtime);
+ if (mssinceflash >= 1000) {
+ /* Timer expired: the user hasn't yet selected an option. Take the default action and get on with it. */
+ /* Note: If in the future Advanced Call Waiting Deluxe (*76) is supported, then as part of the
+ * dialing code, we'll need to automatically invoke the preselected behavior about 2-3 seconds after
+ * the call waiting begins (this allows for the SAS, CAS, and CWCID spill to be sent first).
+ */
+ analog_p->callwaitingdeluxepending = 0;
+ analog_callwaiting_deluxe(analog_p, 0);
+ }
+ ast_mutex_unlock(&p->lock);
+ /* The user shouldn't hear anything after hook flashing, until a decision is made, by the user or when the timer expires. */
+ ast_debug(5, "Dropping frame since Call Waiting Deluxe pending on %s\n", ast_channel_name(ast));
+ return 0;
+ }
+ }
+
if (p->dialing) {
ast_mutex_unlock(&p->lock);
ast_debug(5, "Dropping frame since I'm still dialing on %s...\n",
tmp->usedistinctiveringdetection = usedistinctiveringdetection;
tmp->callwaitingcallerid = conf->chan.callwaitingcallerid;
+ tmp->callwaitingdeluxe = conf->chan.callwaitingdeluxe; /* Not used in DAHDI pvt, only analog pvt */
tmp->threewaycalling = conf->chan.threewaycalling;
tmp->threewaysilenthold = conf->chan.threewaysilenthold;
tmp->calledsubscriberheld = conf->chan.calledsubscriberheld; /* Not used in chan_dahdi.c, just analog pvt, but must exist on the DAHDI pvt anyways */
break;
}
analog_p->callwaitingcallerid = conf->chan.callwaitingcallerid;
+ analog_p->callwaitingdeluxe = conf->chan.callwaitingdeluxe;
analog_p->ringt = conf->chan.ringt;
analog_p->ringt_base = ringt_base;
analog_p->onhooktime = time(NULL);
confp->chan.callwaiting = ast_true(v->value);
} else if (!strcasecmp(v->name, "callwaitingcallerid")) {
confp->chan.callwaitingcallerid = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "callwaitingdeluxe")) {
+ confp->chan.callwaitingdeluxe = ast_true(v->value);
} else if (!strcasecmp(v->name, "context")) {
ast_copy_string(confp->chan.context, v->value, sizeof(confp->chan.context));
} else if (!strcasecmp(v->name, "language")) {
}
}
+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;
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);
}
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) {
}
/* 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:
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);
res = analog_off_hook(i);
i->fxsoffhookstate = 1;
i->cshactive = 0;
+ i->callwaitingdeluxepending = 0;
if (res && (errno == EBUSY)) {
break;
}