]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
sig_analog: Add Call Waiting Deluxe support.
authorNaveen Albert <asterisk@phreaknet.org>
Thu, 24 Aug 2023 14:07:06 +0000 (14:07 +0000)
committerGeorge Joseph <gjoseph@sangoma.com>
Thu, 26 Jun 2025 18:15:04 +0000 (12:15 -0600)
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.

(cherry picked from commit 876c25a9538fdffad37c889e8bff0e8e5cfc8aaf)

apps/app_dial.c
channels/chan_dahdi.c
channels/chan_dahdi.h
channels/chan_iax2.c
channels/sig_analog.c
channels/sig_analog.h
configs/samples/chan_dahdi.conf.sample
funcs/func_frame_trace.c
include/asterisk/frame.h
main/channel.c

index c66be66dfb59d2ae39923476d72d87643d749807..93d269defb5dd70779731ef505d9e0f7020af570 100644 (file)
@@ -1728,6 +1728,15 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                case AST_CONTROL_PVT_CAUSE_CODE:
                                        ast_indicate_data(in, AST_CONTROL_PVT_CAUSE_CODE, f->data.ptr, f->datalen);
                                        break;
+                               case AST_CONTROL_PLAYBACK_BEGIN:
+                                       if (!f->data.ptr) {
+                                               ast_log(LOG_WARNING, "Got playback begin directive without filename on %s\n", ast_channel_name(c));
+                                       } else {
+                                               const char *filename = f->data.ptr;
+                                               ast_verb(3, "Playing audio file %s on %s\n", filename, ast_channel_name(in));
+                                               ast_streamfile(in, filename, ast_channel_language(in));
+                                       }
+                                       break;
                                case -1:
                                        if (single && !caller_entertained) {
                                                ast_verb(3, "%s stopped sounds\n", ast_channel_name(c));
index d96683413c518dffe4c9e3f3d1bd42bb5b30d1c1..2b0b14f471e20f4f840941573f7abbaba4f07463 100644 (file)
@@ -9368,6 +9368,26 @@ static int dahdi_write(struct ast_channel *ast, struct ast_frame *frame)
                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",
@@ -13133,6 +13153,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 
                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 */
@@ -13473,6 +13494,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                                        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);
@@ -18757,6 +18779,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                        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")) {
index 72bd234f848ea682248b418f04ab149fa8d3e0b2..43c89d93b485f93abbdcbaf226db192ddfedf241 100644 (file)
@@ -332,6 +332,10 @@ struct dahdi_pvt {
         * \note Set from the "callwaiting" value read in from chan_dahdi.conf
         */
        unsigned int permcallwaiting:1;
+       /*!
+        * \brief TRUE if Call Waiting Deluxe options should be available
+        */
+       unsigned int callwaitingdeluxe:1;
        /*!
         * \brief TRUE if the outgoing caller ID is blocked/restricted/hidden.
         * \note Set from the "hidecallerid" value read in from chan_dahdi.conf
index 1ffcffce5a24dfe64dc9b2ff26c576278f18fbdd..465f9805ff3a395f955825bcdc40b77fcf8b61a1 100644 (file)
@@ -1512,6 +1512,7 @@ static int iax2_is_control_frame_allowed(int subtype)
        case AST_CONTROL_STREAM_RESTART:
        case AST_CONTROL_STREAM_REVERSE:
        case AST_CONTROL_STREAM_FORWARD:
+       case AST_CONTROL_PLAYBACK_BEGIN: /* Only supported by app_dial currently */
                /* None of these playback stream control frames should go across the link. */
        case AST_CONTROL_RECORD_CANCEL:
        case AST_CONTROL_RECORD_STOP:
index c21b8cae124eae16c8f00713eef8aa60114e6758..c859a0c2624f023713ddc3564cd8b450d743daa8 100644 (file)
@@ -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;
                        }
index 023170cf7f274abe1130d6da95d2922656e5a399..ae909ebba61c4e3845163b7e5992ea559b6a8d22 100644 (file)
@@ -299,6 +299,7 @@ struct analog_pvt {
        unsigned int immediatering:1;                   /*!< TRUE if ringing should be provided for immediate execution */
        unsigned int lastnumredial:1;                   /*!< TRUE if last number redial allowed */
        unsigned int permcallwaiting:1;                 /*!< TRUE if call waiting is enabled. (Configured option) */
+       unsigned int callwaitingdeluxe:1;               /*!< TRUE if Call Waiting Deluxe options are available */
        unsigned int permhidecallerid:1;                /*!< Whether to hide our outgoing caller ID or not */
        unsigned int pulse:1;
        unsigned int threewaycalling:1;
@@ -347,6 +348,12 @@ struct analog_pvt {
         * gives a positive reply.
         */
        unsigned int callwaitcas:1;
+
+       /*!
+        * \brief TRUE if a Call Waiting Deluxe action is currently pending.
+        */
+       unsigned int callwaitingdeluxepending:1;
+
        unsigned int call_qualifier:1;  /*!< Call qualifier delivery */
 
        char callwait_num[AST_MAX_EXTENSION];
@@ -399,6 +406,8 @@ void *analog_handle_init_event(struct analog_pvt *i, int event);
 
 int analog_config_complete(struct analog_pvt *p);
 
+int analog_callwaiting_deluxe(struct analog_pvt *p, int option);
+
 void analog_handle_dtmf(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub index, struct ast_frame **dest);
 
 enum analog_cid_start analog_str_to_cidstart(const char *value);
index 21c7954e1f43a03e03ef434fb69f95b11eb6a061..a574ab41f8f97699d2643a3fdad3f85f2fb64552 100644 (file)
@@ -684,6 +684,31 @@ usecallerid=yes
 ;
 callwaiting=yes
 ;
+; Whether or not to allow for Call Waiting Deluxe options to be used.
+; The Call Waiting Deluxe options are:
+;  3: Conference - conference the call waiting call with the existing call
+;  7: Drop - drop current call and switch to new (same as hanging up and answering)
+;  6/8: Announcement - play announcement to call waiting caller telling them to hold
+;  9: Forward - forward the call waiting call to a preconfigured extension
+; Doing nothing after 1 second triggers default behavior of holding the current call
+;  and switching to the new one.
+;
+; Some CPE (Caller ID units, screenphones, etc.) have dedicated buttons for utilizing
+; the Call Waiting Deluxe features, but these can also be used from any phone manually
+; by simply hook flashing as usual and then quickly dialing the appropriate DTMF digit.
+; If no digit is received within 1 second, the default action is assumed,
+; as if Call Waiting Deluxe was not used.
+;
+; The following extensions are also available in the dialplan to utilize this functionality:
+; - The CALLWAITDELUXEANNOUNCEMENT variable can be set on the incoming channel to control the
+;   announcement prompt played to the call waiting caller. Default is "one-moment-please".
+; - If the forward option is used, the call waiting channel will be forwarded to the 'f' extension
+;   in the channel's configured context. You can then use any dialplan mechanism to route the call.
+;
+; Default is 'no'.
+;
+;callwaitingdeluxe=yes
+;
 ; Configure the number of outstanding call waiting calls for internal ISDN
 ; endpoints before bouncing the calls as busy.  This option is equivalent to
 ; the callwaiting option for analog ports.
index f94ef5c08f13d1b8ccdcf38e17346834d5a98273..cdb747311e0e562ca592397cd43e41c832bf24f9 100644 (file)
@@ -371,6 +371,9 @@ static void print_frame(struct ast_frame *frame)
                case AST_CONTROL_STREAM_FORWARD:
                        ast_verbose("SubClass: STREAM_FORWARD\n");
                        break;
+               case AST_CONTROL_PLAYBACK_BEGIN:
+                       ast_verbose("SubClass: PLAYBACK_BEGIN\n");
+                       break;
                case AST_CONTROL_RECORD_CANCEL:
                        ast_verbose("SubClass: RECORD_CANCEL\n");
                        break;
index fc2c9d513dc9c17901fc3a258a7fefe9d0ec24ca..a81ff92024b9f2f655a695c144f6bb9fc2356fc9 100644 (file)
@@ -335,6 +335,7 @@ enum ast_control_frame_type {
        AST_CONTROL_STREAM_RESTART = 1002,      /*!< Indicate to a channel in playback to restart the stream */
        AST_CONTROL_STREAM_REVERSE = 1003,      /*!< Indicate to a channel in playback to rewind */
        AST_CONTROL_STREAM_FORWARD = 1004,      /*!< Indicate to a channel in playback to fast forward */
+       AST_CONTROL_PLAYBACK_BEGIN = 1005,      /*!< Indicate to a dialing interface that playback of an audio file should begin on the dialing channel. Currently only supported by app_dial. */
        /* Control frames to manipulate recording on a channel. */
        AST_CONTROL_RECORD_CANCEL = 1100,       /*!< Indicated to a channel in record to stop recording and discard the file */
        AST_CONTROL_RECORD_STOP = 1101, /*!< Indicated to a channel in record to stop recording */
index fbaede59e352382fd2819ee3a0f303698ac1e656..4980a6c84502087110fc52640ab19509ea293fee 100644 (file)
@@ -4275,6 +4275,7 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con
        case AST_CONTROL_STREAM_REVERSE:
        case AST_CONTROL_STREAM_FORWARD:
        case AST_CONTROL_STREAM_RESTART:
+       case AST_CONTROL_PLAYBACK_BEGIN:
        case AST_CONTROL_RECORD_CANCEL:
        case AST_CONTROL_RECORD_STOP:
        case AST_CONTROL_RECORD_SUSPEND:
@@ -4576,6 +4577,7 @@ static int indicate_data_internal(struct ast_channel *chan, int _condition, cons
        case AST_CONTROL_STREAM_REVERSE:
        case AST_CONTROL_STREAM_FORWARD:
        case AST_CONTROL_STREAM_RESTART:
+       case AST_CONTROL_PLAYBACK_BEGIN:
        case AST_CONTROL_RECORD_CANCEL:
        case AST_CONTROL_RECORD_STOP:
        case AST_CONTROL_RECORD_SUSPEND: