Add ETSI Call Waiting support.
authorRichard Mudgett <rmudgett@digium.com>
Wed, 2 Jun 2010 21:05:32 +0000 (21:05 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Wed, 2 Jun 2010 21:05:32 +0000 (21:05 +0000)
Add the ability to announce a call to an endpoint when there are no B
channels available.  A call waiting call is a SETUP message with no B
channel selected.

Relevant specification: EN 300 056, EN 300 057, EN 300 058

For DAHDI/ISDN channels, the CHANNEL() dialplan function now supports the
"no_media_path" option.
* Returns "0" if there is a B channel associated with the call.
* Returns "1" if no B channel is associated with the call.  The call is
either on hold or is a call waiting call.

If you are going to allow incoming call waiting calls then you need to use
CHANNEL(no_media_path) do determine if you must drop a call to accept the
new call.

Review: https://reviewboard.asterisk.org/r/568/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@267261 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
channels/chan_dahdi.c
channels/sig_pri.c
channels/sig_pri.h
configs/chan_dahdi.conf.sample
configure
configure.ac
include/asterisk/autoconfig.h.in

diff --git a/CHANGES b/CHANGES
index 08d526d..91df249 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -205,6 +205,11 @@ Dialplan Functions
    prefixing the name of the hash at assignment with the appropriate number of
    underscores, just like variables.
  * GROUP_MATCH_COUNT has been improved to allow regex matching on category
+ * For DAHDI/ISDN channels, the CHANNEL() dialplan function now supports the
+   "no_media_path" option.
+   Returns "0" if there is a B channel associated with the call.
+   Returns "1" if no B channel is associated with the call.  The call is either
+   on hold or is a call waiting call.
 
 Dialplan Variables
 ------------------
@@ -339,6 +344,8 @@ libpri channel driver (chan_dahdi) DAHDI changes
    back into the same interface.  Tromboned calls happen because of call routing,
    call deflection, call forwarding, and call transfer.
  * Added the ability to send and receive ETSI Advice-Of-Charge messages. 
+ * Added the ability to support call waiting calls.  (The SETUP has no B channel
+   assigned.)
 
 Asterisk Manager Interface
 --------------------------
index 926daff..17de19d 100644 (file)
@@ -800,7 +800,10 @@ struct dahdi_pvt {
        unsigned int didtdd:1;                          /*!< flag to say its done it once */
        /*! \brief TRUE if analog type line dialed no digits in Dial() */
        unsigned int dialednone:1;
-       /*! \brief TRUE if in the process of dialing digits or sending something. */
+       /*!
+        * \brief TRUE if in the process of dialing digits or sending something.
+        * \note This is used as a receive squelch for ISDN until connected.
+        */
        unsigned int dialing:1;
        /*! \brief TRUE if the transfer capability of the call is digital. */
        unsigned int digital:1;
@@ -1075,6 +1078,8 @@ struct dahdi_pvt {
         * \note The "group" bitmapped group string read in from chan_dahdi.conf
         */
        ast_group_t group;
+       /*! \brief Default call PCM encoding format: DAHDI_LAW_ALAW or DAHDI_LAW_MULAW. */
+       int law_default;
        /*! \brief Active PCM encoding format: DAHDI_LAW_ALAW or DAHDI_LAW_MULAW */
        int law;
        int confno;                                     /*!< Our conference */
@@ -2232,7 +2237,8 @@ static void my_all_subchannels_hungup(void *pvt)
                p->dsp = NULL;
        }
 
-       law = DAHDI_LAW_DEFAULT;
+       p->law = p->law_default;
+       law = p->law_default;
        res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETLAW, &law);
        if (res < 0)
                ast_log(LOG_WARNING, "Unable to set law on channel %d to default: %s\n", p->channel, strerror(errno));
@@ -2391,10 +2397,22 @@ static struct ast_channel *my_new_pri_ast_channel(void *pvt, int state, int star
        int audio;
        int newlaw = -1;
 
-       /* Set to audio mode at this point */
-       audio = 1;
-       if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_AUDIOMODE, &audio) == -1)
-               ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d: %s\n", p->channel, audio, strerror(errno));
+       switch (p->sig) {
+       case SIG_PRI_LIB_HANDLE_CASES:
+               if (((struct sig_pri_chan *) p->sig_pvt)->no_b_channel) {
+                       /* PRI nobch pseudo channel.  Does not handle ioctl(DAHDI_AUDIOMODE) */
+                       break;
+               }
+               /* Fall through */
+       default:
+               /* Set to audio mode at this point */
+               audio = 1;
+               if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_AUDIOMODE, &audio) == -1) {
+                       ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d: %s\n",
+                               p->channel, audio, strerror(errno));
+               }
+               break;
+       }
 
        if (law != SIG_PRI_DEFLAW) {
                dahdi_setlaw(p->subs[SUB_REAL].dfd, (law == SIG_PRI_ULAW) ? DAHDI_LAW_MULAW : DAHDI_LAW_ALAW);
@@ -2417,6 +2435,54 @@ static struct ast_channel *my_new_pri_ast_channel(void *pvt, int state, int star
 }
 #endif /* defined(HAVE_PRI) */
 
+static int set_actual_gain(int fd, float rxgain, float txgain, float rxdrc, float txdrc, int law);
+
+#if defined(HAVE_PRI)
+/*!
+ * \internal
+ * \brief Open the PRI channel media path.
+ * \since 1.8
+ *
+ * \param p Channel private control structure.
+ *
+ * \return Nothing
+ */
+static void my_pri_open_media(void *p)
+{
+       struct dahdi_pvt *pvt = p;
+       int res;
+       int dfd;
+       int set_val;
+
+       dfd = pvt->subs[SUB_REAL].dfd;
+
+       /* Open the media path. */
+       set_val = 1;
+       res = ioctl(dfd, DAHDI_AUDIOMODE, &set_val);
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Unable to enable audio mode on channel %d (%s)\n",
+                       pvt->channel, strerror(errno));
+       }
+
+       /* Set correct companding law for this call. */
+       res = dahdi_setlaw(dfd, pvt->law);
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Unable to set law on channel %d\n", pvt->channel);
+       }
+
+       /* Set correct gain for this call. */
+       if (pvt->digital) {
+               res = set_actual_gain(dfd, 0, 0, pvt->rxdrc, pvt->txdrc, pvt->law);
+       } else {
+               res = set_actual_gain(dfd, pvt->rxgain, pvt->txgain, pvt->rxdrc, pvt->txdrc,
+                       pvt->law);
+       }
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", pvt->channel);
+       }
+}
+#endif /* defined(HAVE_PRI) */
+
 static int unalloc_sub(struct dahdi_pvt *p, int x);
 
 static int my_unallocate_sub(void *pvt, enum analog_sub analogsub)
@@ -2734,6 +2800,17 @@ static void my_pri_fixup_chans(void *chan_old, void *chan_new)
        new_chan->dsp_features = old_chan->dsp_features;
        old_chan->dsp = NULL;
        old_chan->dsp_features = 0;
+
+       /* Transfer flags from the old channel. */
+       new_chan->dialing = old_chan->dialing;
+       new_chan->digital = old_chan->digital;
+       new_chan->outgoing = old_chan->outgoing;
+       old_chan->dialing = 0;
+       old_chan->digital = 0;
+       old_chan->outgoing = 0;
+
+       /* More stuff to transfer to the new channel. */
+       new_chan->law = old_chan->law;
 }
 
 static int sig_pri_tone_to_dahditone(enum sig_pri_tone tone)
@@ -2997,6 +3074,9 @@ static void my_module_unref(void)
        ast_module_unref(ast_module_info->self);
 }
 
+#if defined(HAVE_PRI_CALL_WAITING)
+static void my_pri_init_config(void *priv, struct sig_pri_pri *pri);
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
 static int dahdi_new_pri_nobch_channel(struct sig_pri_pri *pri);
 
 static struct sig_pri_callback dahdi_pri_callbacks =
@@ -3016,11 +3096,15 @@ static struct sig_pri_callback dahdi_pri_callbacks =
        .set_dnid = my_pri_set_dnid,
        .set_rdnis = my_pri_set_rdnis,
        .new_nobch_intf = dahdi_new_pri_nobch_channel,
+#if defined(HAVE_PRI_CALL_WAITING)
+       .init_config = my_pri_init_config,
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
        .get_orig_dialstring = my_get_orig_dialstring,
        .make_cc_dialstring = my_pri_make_cc_dialstring,
        .update_span_devstate = dahdi_pri_update_span_devstate,
        .module_ref = my_module_ref,
        .module_unref = my_module_unref,
+       .open_media = my_pri_open_media,
 };
 #endif /* defined(HAVE_PRI) */
 
@@ -3155,6 +3239,7 @@ static struct analog_callback dahdi_analog_callbacks =
        .set_needringing = my_set_needringing,
 };
 
+/*! Round robin search locations. */
 static struct dahdi_pvt *round_robin[32];
 
 #if defined(HAVE_SS7)
@@ -4179,7 +4264,7 @@ static int conf_add(struct dahdi_pvt *p, struct dahdi_subchannel *c, int idx, in
        if (slavechannel < 1) {
                p->confno = zi.confno;
        }
-       memcpy(&c->curconf, &zi, sizeof(c->curconf));
+       c->curconf = zi;
        ast_debug(1, "Added %d to conference %d/%d\n", c->dfd, c->curconf.confmode, c->curconf.confno);
        return 0;
 }
@@ -4850,13 +4935,8 @@ static int dahdi_call(struct ast_channel *ast, char *rdest, int timeout)
 
 #ifdef HAVE_PRI
        if (dahdi_sig_pri_lib_handles(p->sig)) {
-               struct dahdi_params ps;
-
-               memset(&ps, 0, sizeof(ps));
-               if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &ps)) {
-                       ast_log(LOG_ERROR, "Could not get params\n");
-               }
-               res = sig_pri_call(p->sig_pvt, ast, rdest, timeout, (ps.curlaw == DAHDI_LAW_MULAW) ? PRI_LAYER_1_ULAW : PRI_LAYER_1_ALAW);
+               res = sig_pri_call(p->sig_pvt, ast, rdest, timeout,
+                       (p->law == DAHDI_LAW_ALAW) ? PRI_LAYER_1_ALAW : PRI_LAYER_1_ULAW);
                ast_mutex_unlock(&p->lock);
                return res;
        }
@@ -5736,7 +5816,8 @@ static int dahdi_hangup(struct ast_channel *ast)
                }
                revert_fax_buffers(p, ast);
                dahdi_setlinear(p->subs[SUB_REAL].dfd, 0);
-               law = DAHDI_LAW_DEFAULT;
+               p->law = p->law_default;
+               law = p->law_default;
                res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETLAW, &law);
                dahdi_disable_ec(p);
                update_conf(p);
@@ -5907,7 +5988,8 @@ static int dahdi_hangup(struct ast_channel *ast)
 
                revert_fax_buffers(p, ast);
 
-               law = DAHDI_LAW_DEFAULT;
+               p->law = p->law_default;
+               law = p->law_default;
                res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETLAW, &law);
                if (res < 0)
                        ast_log(LOG_WARNING, "Unable to set law on channel %d to default: %s\n", p->channel, strerror(errno));
@@ -6464,6 +6546,22 @@ static int dahdi_func_read(struct ast_channel *chan, const char *function, char
                }
                ast_mutex_unlock(&p->lock);
 #endif /* defined(HAVE_PRI_SETUP_KEYPAD) */
+       } else if (!strcasecmp(data, "no_media_path")) {
+               ast_mutex_lock(&p->lock);
+               switch (p->sig) {
+               case SIG_PRI_LIB_HANDLE_CASES:
+                       /*
+                        * TRUE if the call is on hold or is call waiting because
+                        * there is no media path available.
+                        */
+                       snprintf(buf, len, "%d", ((struct sig_pri_chan *) p->sig_pvt)->no_b_channel);
+                       break;
+               default:
+                       *buf = '\0';
+                       res = -1;
+                       break;
+               }
+               ast_mutex_unlock(&p->lock);
 #endif /* defined(HAVE_PRI) */
        } else {
                *buf = '\0';
@@ -8871,12 +8969,10 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
 {
        struct ast_channel *tmp;
        format_t deflaw;
-       int res;
        int x;
        int features;
        struct ast_str *chan_name;
        struct ast_variable *v;
-       struct dahdi_params ps;
 
        if (i->subs[idx].owner) {
                ast_log(LOG_WARNING, "Channel %d already has a %s call\n", i->channel,subnames[idx]);
@@ -8907,21 +9003,29 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
        }
 #endif /* defined(HAVE_PRI) */
        ast_channel_cc_params_init(tmp, i->cc_params);
-       memset(&ps, 0, sizeof(ps));
-       res = ioctl(i->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &ps);
-       if (res) {
-               ast_log(LOG_WARNING, "Unable to get parameters, assuming MULAW: %s\n", strerror(errno));
-               ps.curlaw = DAHDI_LAW_MULAW;
-       }
-       if (ps.curlaw == DAHDI_LAW_ALAW)
-               deflaw = AST_FORMAT_ALAW;
-       else
-               deflaw = AST_FORMAT_ULAW;
        if (law) {
-               if (law == DAHDI_LAW_ALAW)
+               i->law = law;
+               if (law == DAHDI_LAW_ALAW) {
                        deflaw = AST_FORMAT_ALAW;
-               else
+               } else {
+                       deflaw = AST_FORMAT_ULAW;
+               }
+       } else {
+               switch (i->sig) {
+               case SIG_PRI_LIB_HANDLE_CASES:
+                       /* Make sure companding law is known. */
+                       i->law = (i->law_default == DAHDI_LAW_ALAW)
+                               ? DAHDI_LAW_ALAW : DAHDI_LAW_MULAW;
+                       break;
+               default:
+                       i->law = i->law_default;
+                       break;
+               }
+               if (i->law_default == DAHDI_LAW_ALAW) {
+                       deflaw = AST_FORMAT_ALAW;
+               } else {
                        deflaw = AST_FORMAT_ULAW;
+               }
        }
        ast_channel_set_fd(tmp, 0, i->subs[idx].dfd);
        tmp->nativeformats = deflaw;
@@ -11538,6 +11642,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                                        destroy_dahdi_pvt(tmp);
                                        return NULL;
                                }
+                               tmp->law_default = p.curlaw;
                                tmp->law = p.curlaw;
                                tmp->span = p.spanno;
                                span = p.spanno - 1;
@@ -11777,6 +11882,12 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                                                pris[span].pri.cc_qsig_signaling_link_rsp =
                                                        conf->pri.pri.cc_qsig_signaling_link_rsp;
 #endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CALL_WAITING)
+                                               pris[span].pri.max_call_waiting_calls =
+                                                       conf->pri.pri.max_call_waiting_calls;
+                                               pris[span].pri.allow_call_waiting_calls =
+                                                       conf->pri.pri.allow_call_waiting_calls;
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
                                                pris[span].pri.transfer = conf->chan.transfer;
                                                pris[span].pri.facilityenable = conf->pri.pri.facilityenable;
 #if defined(HAVE_PRI_AOC_EVENTS)
@@ -11796,6 +11907,20 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                                                for (x = 0; x < PRI_MAX_TIMERS; x++) {
                                                        pris[span].pri.pritimers[x] = conf->pri.pri.pritimers[x];
                                                }
+
+#if defined(HAVE_PRI_CALL_WAITING)
+                                               /* Channel initial config parameters. */
+                                               pris[span].pri.ch_cfg.stripmsd = conf->chan.stripmsd;
+                                               pris[span].pri.ch_cfg.hidecallerid = conf->chan.hidecallerid;
+                                               pris[span].pri.ch_cfg.hidecalleridname = conf->chan.hidecalleridname;
+                                               pris[span].pri.ch_cfg.immediate = conf->chan.immediate;
+                                               pris[span].pri.ch_cfg.priexclusive = conf->chan.priexclusive;
+                                               pris[span].pri.ch_cfg.priindication_oob = conf->chan.priindication_oob;
+                                               pris[span].pri.ch_cfg.use_callerid = conf->chan.use_callerid;
+                                               pris[span].pri.ch_cfg.use_callingpres = conf->chan.use_callingpres;
+                                               ast_copy_string(pris[span].pri.ch_cfg.context, conf->chan.context, sizeof(pris[span].pri.ch_cfg.context));
+                                               ast_copy_string(pris[span].pri.ch_cfg.mohinterpret, conf->chan.mohinterpret, sizeof(pris[span].pri.ch_cfg.mohinterpret));
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
                                        } else {
                                                ast_log(LOG_ERROR, "Channel %d is reserved for D-channel.\n", p.chanpos);
                                                destroy_dahdi_pvt(tmp);
@@ -12266,8 +12391,10 @@ static int is_group_or_channel_match(struct dahdi_pvt *p, int span, ast_group_t
        return 1;
 }
 
-static int available(struct dahdi_pvt *p)
+static int available(struct dahdi_pvt **pvt, int is_specific_channel)
 {
+       struct dahdi_pvt *p = *pvt;
+
        if (p->inalarm)
                return 0;
 
@@ -12277,7 +12404,15 @@ static int available(struct dahdi_pvt *p)
 #ifdef HAVE_PRI
        switch (p->sig) {
        case SIG_PRI_LIB_HANDLE_CASES:
-               return sig_pri_available(p->sig_pvt);
+               {
+                       struct sig_pri_chan *pvt_chan;
+                       int res;
+
+                       pvt_chan = p->sig_pvt;
+                       res = sig_pri_available(&pvt_chan, is_specific_channel);
+                       *pvt = pvt_chan->chan_pvt;
+                       return res;
+               }
        default:
                break;
        }
@@ -12316,6 +12451,38 @@ static int available(struct dahdi_pvt *p)
 }
 
 #if defined(HAVE_PRI)
+#if defined(HAVE_PRI_CALL_WAITING)
+/*!
+ * \internal
+ * \brief Init the private channel configuration using the span controller.
+ * \since 1.8
+ *
+ * \param priv Channel to init the configuration.
+ * \param pri sig_pri PRI control structure.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ *
+ * \return Nothing
+ */
+static void my_pri_init_config(void *priv, struct sig_pri_pri *pri)
+{
+       struct dahdi_pvt *pvt = priv;
+
+       pvt->stripmsd = pri->ch_cfg.stripmsd;
+       pvt->hidecallerid = pri->ch_cfg.hidecallerid;
+       pvt->hidecalleridname = pri->ch_cfg.hidecalleridname;
+       pvt->immediate = pri->ch_cfg.immediate;
+       pvt->priexclusive = pri->ch_cfg.priexclusive;
+       pvt->priindication_oob = pri->ch_cfg.priindication_oob;
+       pvt->use_callerid = pri->ch_cfg.use_callerid;
+       pvt->use_callingpres = pri->ch_cfg.use_callingpres;
+       ast_copy_string(pvt->context, pri->ch_cfg.context, sizeof(pvt->context));
+       ast_copy_string(pvt->mohinterpret, pri->ch_cfg.mohinterpret, sizeof(pvt->mohinterpret));
+}
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
+#endif /* defined(HAVE_PRI) */
+
+#if defined(HAVE_PRI)
 /*!
  * \internal
  * \brief Create a no B channel interface.
@@ -12381,7 +12548,15 @@ static int dahdi_new_pri_nobch_channel(struct sig_pri_pri *pri)
        }
        chan->no_b_channel = 1;
 
+       /*
+        * Pseudo channel companding law.
+        * Needed for outgoing call waiting calls.
+        * XXX May need to make this determined by switchtype or user option.
+        */
+       pvt->law_default = DAHDI_LAW_ALAW;
+
        pvt->sig = pri->sig;
+       pvt->outsigmod = -1;
        pvt->pri = pri;
        pvt->sig_pvt = chan;
        pri->pvts[pvt_idx] = chan;
@@ -12577,13 +12752,18 @@ static struct dahdi_pvt *determine_starting_point(const char *data, struct dahdi
                        } else
                                p = iflist;
                } else {
+                       if (ARRAY_LEN(round_robin) <= x) {
+                               ast_log(LOG_WARNING, "Round robin index %d out of range for data %s\n",
+                                       x, data);
+                               return NULL;
+                       }
                        if (args.group[0] == 'R') {
                                param->backwards = 1;
-                               p = round_robin[x]?round_robin[x]->prev:ifend;
+                               p = round_robin[x] ? round_robin[x]->prev : ifend;
                                if (!p)
                                        p = ifend;
                        } else {
-                               p = round_robin[x]?round_robin[x]->next:iflist;
+                               p = round_robin[x] ? round_robin[x]->next : iflist;
                                if (!p)
                                        p = iflist;
                        }
@@ -12643,7 +12823,7 @@ static struct ast_channel *dahdi_request(const char *type, format_t format, cons
                        round_robin[start.rr_starting_point] = p;
 
                if (is_group_or_channel_match(p, start.span, start.groupmatch, &groupmatched, start.channelmatch, &channelmatched)
-                       && available(p)) {
+                       && available(&p, channelmatched)) {
                        ast_debug(1, "Using channel %d\n", p->channel);
 
                        callwait = (p->owner != NULL);
@@ -12702,6 +12882,20 @@ static struct ast_channel *dahdi_request(const char *type, format_t format, cons
                        }
                        if (!tmp) {
                                p->outgoing = 0;
+#if defined(HAVE_PRI)
+#if defined(HAVE_PRI_CALL_WAITING)
+                               switch (p->sig) {
+                               case SIG_PRI_LIB_HANDLE_CASES:
+                                       if (((struct sig_pri_chan *) p->sig_pvt)->is_call_waiting) {
+                                               ((struct sig_pri_chan *) p->sig_pvt)->is_call_waiting = 0;
+                                               ast_atomic_fetchadd_int(&p->pri->num_call_waiting_calls, -1);
+                                       }
+                                       break;
+                               default:
+                                       break;
+                               }
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
+#endif /* defined(HAVE_PRI) */
                        } else {
                                snprintf(p->dialstring, sizeof(p->dialstring), "DAHDI/%s", (char *) data);
                        }
@@ -15090,7 +15284,7 @@ static char *dahdi_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli
                        ast_cli(a->fd, "TDD: %s\n", tmp->tdd ? "yes" : "no");
                        ast_cli(a->fd, "Relax DTMF: %s\n", tmp->dtmfrelax ? "yes" : "no");
                        ast_cli(a->fd, "Dialing/CallwaitCAS: %d/%d\n", tmp->dialing, tmp->callwaitcas);
-                       ast_cli(a->fd, "Default law: %s\n", tmp->law == DAHDI_LAW_MULAW ? "ulaw" : tmp->law == DAHDI_LAW_ALAW ? "alaw" : "unknown");
+                       ast_cli(a->fd, "Default law: %s\n", tmp->law_default == DAHDI_LAW_MULAW ? "ulaw" : tmp->law_default == DAHDI_LAW_ALAW ? "alaw" : "unknown");
                        ast_cli(a->fd, "Fax Handled: %s\n", tmp->faxhandled ? "yes" : "no");
                        ast_cli(a->fd, "Pulse phone: %s\n", tmp->pulsedial ? "yes" : "no");
                        ast_cli(a->fd, "Gains (RX/TX): %.2f/%.2f\n", tmp->rxgain, tmp->txgain);
@@ -17246,6 +17440,16 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                                        confp->pri.pri.cc_qsig_signaling_link_rsp = 1;/* retain */
                                }
 #endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CALL_WAITING)
+                       } else if (!strcasecmp(v->name, "max_call_waiting_calls")) {
+                               confp->pri.pri.max_call_waiting_calls = atoi(v->value);
+                               if (confp->pri.pri.max_call_waiting_calls < 0) {
+                                       /* Negative values are not allowed. */
+                                       confp->pri.pri.max_call_waiting_calls = 0;
+                               }
+                       } else if (!strcasecmp(v->name, "allow_call_waiting_calls")) {
+                               confp->pri.pri.allow_call_waiting_calls = ast_true(v->value);
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
 #endif /* HAVE_PRI */
 #ifdef HAVE_SS7
                        } else if (!strcasecmp(v->name, "ss7type")) {
index 2fda977..92f4cf9 100644 (file)
@@ -814,6 +814,26 @@ static struct ast_channel *sig_pri_new_ast_channel(struct sig_pri_chan *p, int s
        return c;
 }
 
+/*!
+ * \internal
+ * \brief Open the PRI channel media path.
+ * \since 1.8
+ *
+ * \param p Channel private control structure.
+ *
+ * \return Nothing
+ */
+static void sig_pri_open_media(struct sig_pri_chan *p)
+{
+       if (p->no_b_channel) {
+               return;
+       }
+
+       if (p->calls->open_media) {
+               p->calls->open_media(p->chan_pvt);
+       }
+}
+
 struct ast_channel *sig_pri_request(struct sig_pri_chan *p, enum sig_pri_law law, const struct ast_channel *requestor, int transfercapability)
 {
        struct ast_channel *ast;
@@ -982,15 +1002,17 @@ static int pri_find_principle(struct sig_pri_pri *pri, int channel, q931_call *c
        int x;
        int span;
        int principle;
+       int prioffset;
 
        if (channel < 0) {
                /* Channel is not picked yet. */
                return -1;
        }
 
-       if (channel & PRI_HELD_CALL) {
+       prioffset = PRI_CHANNEL(channel);
+       if (!prioffset || (channel & PRI_HELD_CALL)) {
                if (!call) {
-                       /* Cannot find a held call without a call. */
+                       /* Cannot find a call waiting call or held call without a call. */
                        return -1;
                }
                principle = -1;
@@ -1015,11 +1037,10 @@ static int pri_find_principle(struct sig_pri_pri *pri, int channel, q931_call *c
                span = pri->dchan_logical_span[index];
        }
 
-       channel = PRI_CHANNEL(channel);
        principle = -1;
        for (x = 0; x < pri->numchans; x++) {
                if (pri->pvts[x]
-                       && pri->pvts[x]->prioffset == channel
+                       && pri->pvts[x]->prioffset == prioffset
                        && pri->pvts[x]->logicalspan == span
                        && !pri->pvts[x]->no_b_channel) {
                        principle = x;
@@ -1079,6 +1100,11 @@ static int pri_fixup_principle(struct sig_pri_pri *pri, int principle, q931_call
                old_chan->call = NULL;
 
                /* Transfer flags from the old channel. */
+#if defined(HAVE_PRI_AOC_EVENTS)
+               new_chan->aoc_s_request_invoke_id_valid = old_chan->aoc_s_request_invoke_id_valid;
+               new_chan->waiting_for_aoce = old_chan->waiting_for_aoce;
+               new_chan->holding_aoce = old_chan->holding_aoce;
+#endif /* defined(HAVE_PRI_AOC_EVENTS) */
                new_chan->alerting = old_chan->alerting;
                new_chan->alreadyhungup = old_chan->alreadyhungup;
                new_chan->isidlecall = old_chan->isidlecall;
@@ -1087,17 +1113,14 @@ static int pri_fixup_principle(struct sig_pri_pri *pri, int principle, q931_call
                new_chan->setup_ack = old_chan->setup_ack;
                new_chan->outgoing = old_chan->outgoing;
                new_chan->digital = old_chan->digital;
-#if defined(HAVE_PRI_AOC_EVENTS)
-               new_chan->aoc_s_request_invoke_id = old_chan->aoc_s_request_invoke_id;
-               new_chan->aoc_s_request_invoke_id_valid = old_chan->aoc_s_request_invoke_id_valid;
-               new_chan->holding_aoce = old_chan->holding_aoce;
-               new_chan->waiting_for_aoce = old_chan->waiting_for_aoce;
-               new_chan->aoc_e = old_chan->aoc_e;
+#if defined(HAVE_PRI_CALL_WAITING)
+               new_chan->is_call_waiting = old_chan->is_call_waiting;
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
 
-               old_chan->holding_aoce = 0;
+#if defined(HAVE_PRI_AOC_EVENTS)
                old_chan->aoc_s_request_invoke_id_valid = 0;
                old_chan->waiting_for_aoce = 0;
-               memset(&old_chan->aoc_e, 0, sizeof(&old_chan->aoc_e));
+               old_chan->holding_aoce = 0;
 #endif /* defined(HAVE_PRI_AOC_EVENTS) */
                old_chan->alerting = 0;
                old_chan->alreadyhungup = 0;
@@ -1107,6 +1130,9 @@ static int pri_fixup_principle(struct sig_pri_pri *pri, int principle, q931_call
                old_chan->setup_ack = 0;
                old_chan->outgoing = 0;
                old_chan->digital = 0;
+#if defined(HAVE_PRI_CALL_WAITING)
+               old_chan->is_call_waiting = 0;
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
 
                /* More stuff to transfer to the new channel. */
 #if defined(HAVE_PRI_REVERSE_CHARGE)
@@ -1115,6 +1141,10 @@ static int pri_fixup_principle(struct sig_pri_pri *pri, int principle, q931_call
 #if defined(HAVE_PRI_SETUP_KEYPAD)
                strcpy(new_chan->keypad_digits, old_chan->keypad_digits);
 #endif /* defined(HAVE_PRI_SETUP_KEYPAD) */
+#if defined(HAVE_PRI_AOC_EVENTS)
+               new_chan->aoc_s_request_invoke_id = old_chan->aoc_s_request_invoke_id;
+               new_chan->aoc_e = old_chan->aoc_e;
+#endif /* defined(HAVE_PRI_AOC_EVENTS) */
 
                if (new_chan->no_b_channel) {
                        /* Copy the real channel configuration to the no B channel interface. */
@@ -1223,6 +1253,38 @@ tryanotherpos:
        return 0;
 }
 
+#if defined(HAVE_PRI_CALL_WAITING)
+/*!
+ * \internal
+ * \brief Init the private channel configuration using the span controller.
+ * \since 1.8
+ *
+ * \param pvt Channel to init the configuration.
+ * \param pri sig_pri PRI control structure.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ *
+ * \return Nothing
+ */
+static void sig_pri_init_config(struct sig_pri_chan *pvt, struct sig_pri_pri *pri)
+{
+       pvt->stripmsd = pri->ch_cfg.stripmsd;
+       pvt->hidecallerid = pri->ch_cfg.hidecallerid;
+       pvt->hidecalleridname = pri->ch_cfg.hidecalleridname;
+       pvt->immediate = pri->ch_cfg.immediate;
+       pvt->priexclusive = pri->ch_cfg.priexclusive;
+       pvt->priindication_oob = pri->ch_cfg.priindication_oob;
+       pvt->use_callerid = pri->ch_cfg.use_callerid;
+       pvt->use_callingpres = pri->ch_cfg.use_callingpres;
+       ast_copy_string(pvt->context, pri->ch_cfg.context, sizeof(pvt->context));
+       ast_copy_string(pvt->mohinterpret, pri->ch_cfg.mohinterpret, sizeof(pvt->mohinterpret));
+
+       if (pri->calls->init_config) {
+               pri->calls->init_config(pvt->chan_pvt, pri);
+       }
+}
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
+
 static int pri_find_empty_chan(struct sig_pri_pri *pri, int backwards)
 {
        int x;
@@ -3519,7 +3581,8 @@ static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int eve
                        sig_pri_lock_owner(pri, chanpos);
                        owner = pri->pvts[chanpos]->owner;
                        if (owner) {
-                               sig_pri_aoc_s_from_pri(&subcmd->u.aoc_s, owner, (pri->aoc_passthrough_flag & SIG_PRI_AOC_GRANT_S));
+                               sig_pri_aoc_s_from_pri(&subcmd->u.aoc_s, owner,
+                                       (pri->aoc_passthrough_flag & SIG_PRI_AOC_GRANT_S));
                                ast_channel_unlock(owner);
                        }
                        break;
@@ -3530,7 +3593,8 @@ static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int eve
                        owner = pri->pvts[chanpos]->owner;
                        if (owner) {
                                /* Queue AST_CONTROL_AOC frame on channel */
-                               sig_pri_aoc_d_from_pri(&subcmd->u.aoc_d, owner, (pri->aoc_passthrough_flag & SIG_PRI_AOC_GRANT_D));
+                               sig_pri_aoc_d_from_pri(&subcmd->u.aoc_d, owner,
+                                       (pri->aoc_passthrough_flag & SIG_PRI_AOC_GRANT_D));
                                ast_channel_unlock(owner);
                        }
                        break;
@@ -3540,7 +3604,8 @@ static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int eve
                        sig_pri_lock_owner(pri, chanpos);
                        owner = pri->pvts[chanpos]->owner;
                        /* Queue AST_CONTROL_AOC frame */
-                       sig_pri_aoc_e_from_pri(&subcmd->u.aoc_e, owner, (pri->aoc_passthrough_flag & SIG_PRI_AOC_GRANT_E));
+                       sig_pri_aoc_e_from_pri(&subcmd->u.aoc_e, owner,
+                               (pri->aoc_passthrough_flag & SIG_PRI_AOC_GRANT_E));
                        if (owner) {
                                ast_channel_unlock(owner);
                        }
@@ -3551,20 +3616,25 @@ static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int eve
                        sig_pri_lock_owner(pri, chanpos);
                        owner = pri->pvts[chanpos]->owner;
                        if (owner) {
-                               sig_pri_aoc_request_from_pri(&subcmd->u.aoc_request, pri->pvts[chanpos], call_rsp);
+                               sig_pri_aoc_request_from_pri(&subcmd->u.aoc_request, pri->pvts[chanpos],
+                                       call_rsp);
                                ast_channel_unlock(owner);
                        }
                        break;
 #endif /* defined(HAVE_PRI_AOC_EVENTS) */
 #if defined(HAVE_PRI_AOC_EVENTS)
                case PRI_SUBCMD_AOC_CHARGING_REQ_RSP:
-                       /* An AOC request response may contain an AOC-S rate list.  If this is the case handle this just like we
-                        * would an incoming AOC-S msg */
+                       /*
+                        * An AOC request response may contain an AOC-S rate list.
+                        * If this is the case handle this just like we
+                        * would an incoming AOC-S msg.
+                        */
                        if (subcmd->u.aoc_request_response.valid_aoc_s) {
                                sig_pri_lock_owner(pri, chanpos);
                                owner = pri->pvts[chanpos]->owner;
                                if (owner) {
-                                       sig_pri_aoc_s_from_pri(&subcmd->u.aoc_request_response.aoc_s, owner, (pri->aoc_passthrough_flag & SIG_PRI_AOC_GRANT_S));
+                                       sig_pri_aoc_s_from_pri(&subcmd->u.aoc_request_response.aoc_s, owner,
+                                               (pri->aoc_passthrough_flag & SIG_PRI_AOC_GRANT_S));
                                        ast_channel_unlock(owner);
                                }
                        }
@@ -4175,10 +4245,33 @@ static void *pri_dchannel(void *vpri)
                                                e->ring.call);
                                        break;
                                }
-                               if (e->ring.channel == -1 || PRI_CHANNEL(e->ring.channel) == 0xFF)
+                               if (e->ring.channel == -1 || PRI_CHANNEL(e->ring.channel) == 0xFF) {
+                                       /* Any channel requested. */
                                        chanpos = pri_find_empty_chan(pri, 1);
-                               else
+                               } else if (PRI_CHANNEL(e->ring.channel) == 0x00) {
+                                       /* No channel specified. */
+#if defined(HAVE_PRI_CALL_WAITING)
+                                       if (!pri->allow_call_waiting_calls)
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
+                                       {
+                                               /* We will not accept incoming call waiting calls. */
+                                               pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_INCOMPATIBLE_DESTINATION);
+                                               break;
+                                       }
+#if defined(HAVE_PRI_CALL_WAITING)
+                                       chanpos = pri_find_empty_nobch(pri);
+                                       if (chanpos < 0) {
+                                               /* We could not find/create a call interface. */
+                                               pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION);
+                                               break;
+                                       }
+                                       /* Setup the call interface to use. */
+                                       sig_pri_init_config(pri->pvts[chanpos], pri);
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
+                               } else {
+                                       /* A channel is specified. */
                                        chanpos = pri_find_principle(pri, e->ring.channel, e->ring.call);
+                               }
                                /* if no channel specified find one empty */
                                if (chanpos < 0) {
                                        ast_log(LOG_WARNING, "Ring requested on unconfigured channel %d/%d span %d\n",
@@ -4571,43 +4664,33 @@ static void *pri_dchannel(void *vpri)
                                        sig_pri_lock_private(pri->pvts[chanpos]);
                                        sig_pri_handle_subcmds(pri, chanpos, e->e, e->proceeding.channel,
                                                e->proceeding.subcmds, e->proceeding.call);
-                                       if ((!pri->pvts[chanpos]->progress)
-#ifdef PRI_PROGRESS_MASK
-                                               || (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE)
-#else
-                                               || (e->proceeding.progress == 8)
-#endif
-                                               ) {
-                                               int subclass;
 
-                                               subclass = AST_CONTROL_PROGRESS;
-                                               if (e->proceeding.cause > -1) {
-                                                       ast_verb(3, "PROGRESS with cause code %d received\n", e->proceeding.cause);
+                                       if (e->proceeding.cause > -1) {
+                                               ast_verb(3, "PROGRESS with cause code %d received\n", e->proceeding.cause);
 
-                                                       /* Work around broken, out of spec USER_BUSY cause in a progress message */
-                                                       if (e->proceeding.cause == AST_CAUSE_USER_BUSY) {
-                                                               if (pri->pvts[chanpos]->owner) {
-                                                                       ast_verb(3, "PROGRESS with 'user busy' received, signaling AST_CONTROL_BUSY instead of AST_CONTROL_PROGRESS\n");
+                                               /* Work around broken, out of spec USER_BUSY cause in a progress message */
+                                               if (e->proceeding.cause == AST_CAUSE_USER_BUSY) {
+                                                       if (pri->pvts[chanpos]->owner) {
+                                                               ast_verb(3, "PROGRESS with 'user busy' received, signaling AST_CONTROL_BUSY instead of AST_CONTROL_PROGRESS\n");
 
-                                                                       pri->pvts[chanpos]->owner->hangupcause = e->proceeding.cause;
-                                                                       subclass = AST_CONTROL_BUSY;
-                                                               }
+                                                               pri->pvts[chanpos]->owner->hangupcause = e->proceeding.cause;
+                                                               pri_queue_control(pri, chanpos, AST_CONTROL_BUSY);
                                                        }
                                                }
+                                       }
 
-                                               ast_debug(1, "Queuing frame from PRI_EVENT_PROGRESS on channel %d/%d span %d\n",
-                                                       pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset,pri->span);
-                                               pri_queue_control(pri, chanpos, subclass);
-                                               if (
+                                       if (!pri->pvts[chanpos]->progress
+                                               && !pri->pvts[chanpos]->no_b_channel
 #ifdef PRI_PROGRESS_MASK
-                                                       e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE
+                                               && (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE)
 #else
-                                                       e->proceeding.progress == 8
+                                               && e->proceeding.progress == 8
 #endif
-                                                       ) {
-                                                       /* Bring voice path up */
-                                                       pri_queue_control(pri, chanpos, AST_CONTROL_PROGRESS);
-                                               }
+                                               ) {
+                                               /* Bring voice path up */
+                                               ast_debug(1, "Queuing frame from PRI_EVENT_PROGRESS on channel %d/%d span %d\n",
+                                                       pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset,pri->span);
+                                               pri_queue_control(pri, chanpos, AST_CONTROL_PROGRESS);
                                                pri->pvts[chanpos]->progress = 1;
                                                sig_pri_set_dialing(pri->pvts[chanpos], 0);
                                        }
@@ -4629,17 +4712,19 @@ static void *pri_dchannel(void *vpri)
                                                ast_debug(1, "Queuing frame from PRI_EVENT_PROCEEDING on channel %d/%d span %d\n",
                                                        pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset,pri->span);
                                                pri_queue_control(pri, chanpos, AST_CONTROL_PROCEEDING);
-                                               if (
+                                               pri->pvts[chanpos]->proceeding = 1;
+                                       }
+                                       if (!pri->pvts[chanpos]->progress
+                                               && !pri->pvts[chanpos]->no_b_channel
 #ifdef PRI_PROGRESS_MASK
-                                                       e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE
+                                               && (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE)
 #else
-                                                       e->proceeding.progress == 8
+                                               && e->proceeding.progress == 8
 #endif
-                                                       ) {
-                                                       /* Bring voice path up */
-                                                       pri_queue_control(pri, chanpos, AST_CONTROL_PROGRESS);
-                                               }
-                                               pri->pvts[chanpos]->proceeding = 1;
+                                               ) {
+                                               /* Bring voice path up */
+                                               pri_queue_control(pri, chanpos, AST_CONTROL_PROGRESS);
+                                               pri->pvts[chanpos]->progress = 1;
                                                sig_pri_set_dialing(pri->pvts[chanpos], 0);
                                        }
                                        sig_pri_unlock_private(pri->pvts[chanpos]);
@@ -4689,39 +4774,127 @@ static void *pri_dchannel(void *vpri)
                                if (chanpos < 0) {
                                        ast_log(LOG_WARNING, "Answer on unconfigured channel %d/%d span %d\n",
                                                PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), pri->span);
+                                       break;
+                               }
+                               chanpos = pri_fixup_principle(pri, chanpos, e->answer.call);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "Answer requested on channel %d/%d not in use on span %d\n",
+                                               PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), pri->span);
+                                       break;
+                               }
+#if defined(HAVE_PRI_CALL_WAITING)
+                               if (pri->pvts[chanpos]->is_call_waiting) {
+                                       if (pri->pvts[chanpos]->no_b_channel) {
+                                               int new_chanpos;
+
+                                               /*
+                                                * Need to find a free channel now or
+                                                * kill the call with PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION.
+                                                */
+                                               new_chanpos = pri_find_empty_chan(pri, 1);
+                                               if (new_chanpos < 0) {
+                                                       sig_pri_lock_private(pri->pvts[chanpos]);
+                                                       sig_pri_handle_subcmds(pri, chanpos, e->e, e->answer.channel,
+                                                               e->answer.subcmds, e->answer.call);
+                                                       sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCBS);
+                                                       if (pri->pvts[chanpos]->owner) {
+                                                               pri->pvts[chanpos]->owner->hangupcause = PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION;
+                                                               switch (pri->pvts[chanpos]->owner->_state) {
+                                                               case AST_STATE_BUSY:
+                                                               case AST_STATE_UP:
+                                                                       ast_softhangup_nolock(pri->pvts[chanpos]->owner, AST_SOFTHANGUP_DEV);
+                                                                       break;
+                                                               default:
+                                                                       pri_queue_control(pri, chanpos, AST_CONTROL_CONGESTION);
+                                                                       break;
+                                                               }
+                                                       } else {
+                                                               pri->pvts[chanpos]->is_call_waiting = 0;
+                                                               ast_atomic_fetchadd_int(&pri->num_call_waiting_calls, -1);
+                                                               pri_hangup(pri->pri, e->answer.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION);
+                                                               pri->pvts[chanpos]->call = NULL;
+                                                       }
+                                                       sig_pri_unlock_private(pri->pvts[chanpos]);
+                                                       break;
+                                               }
+                                               chanpos = pri_fixup_principle(pri, new_chanpos, e->answer.call);
+                                               if (chanpos < 0) {
+                                                       ast_log(LOG_WARNING,
+                                                               "Unable to move call waiting call channel on span %d\n",
+                                                               pri->span);
+                                                       break;
+                                               }
+                                       }
+                                       pri_connect_ack(pri->pri, e->answer.call, PVT_TO_CHANNEL(pri->pvts[chanpos]));
                                } else {
-                                       chanpos = pri_fixup_principle(pri, chanpos, e->answer.call);
-                                       if (chanpos < 0) {
-                                               ast_log(LOG_WARNING, "Answer requested on channel %d/%d not in use on span %d\n",
-                                                       PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), pri->span);
-                                       } else {
-                                               sig_pri_lock_private(pri->pvts[chanpos]);
+                                       /* Call is normal so do normal CONNECT_ACKNOWLEDGE. */
+                                       pri_connect_ack(pri->pri, e->answer.call, 0);
+                               }
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
+                               sig_pri_lock_private(pri->pvts[chanpos]);
 
-                                               sig_pri_handle_subcmds(pri, chanpos, e->e, e->answer.channel,
-                                                       e->answer.subcmds, e->answer.call);
-                                               pri_queue_control(pri, chanpos, AST_CONTROL_ANSWER);
-                                               /* Enable echo cancellation if it's not on already */
-                                               sig_pri_set_dialing(pri->pvts[chanpos], 0);
-                                               sig_pri_set_echocanceller(pri->pvts[chanpos], 1);
+#if defined(HAVE_PRI_CALL_WAITING)
+                               if (pri->pvts[chanpos]->is_call_waiting) {
+                                       pri->pvts[chanpos]->is_call_waiting = 0;
+                                       ast_atomic_fetchadd_int(&pri->num_call_waiting_calls, -1);
+                                       sig_pri_span_devstate_changed(pri);
+                               }
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
+                               sig_pri_handle_subcmds(pri, chanpos, e->e, e->answer.channel,
+                                       e->answer.subcmds, e->answer.call);
+                               sig_pri_open_media(pri->pvts[chanpos]);
+                               pri_queue_control(pri, chanpos, AST_CONTROL_ANSWER);
+                               /* Enable echo cancellation if it's not on already */
+                               sig_pri_set_dialing(pri->pvts[chanpos], 0);
+                               sig_pri_set_echocanceller(pri->pvts[chanpos], 1);
 
 #ifdef SUPPORT_USERUSER
-                                               if (!ast_strlen_zero(e->answer.useruserinfo)) {
-                                                       struct ast_channel *owner;
-
-                                                       sig_pri_lock_owner(pri, chanpos);
-                                                       owner = pri->pvts[chanpos]->owner;
-                                                       if (owner) {
-                                                               pbx_builtin_setvar_helper(owner, "USERUSERINFO",
-                                                                       e->answer.useruserinfo);
-                                                               ast_channel_unlock(owner);
-                                                       }
-                                               }
+                               if (!ast_strlen_zero(e->answer.useruserinfo)) {
+                                       struct ast_channel *owner;
+
+                                       sig_pri_lock_owner(pri, chanpos);
+                                       owner = pri->pvts[chanpos]->owner;
+                                       if (owner) {
+                                               pbx_builtin_setvar_helper(owner, "USERUSERINFO",
+                                                       e->answer.useruserinfo);
+                                               ast_channel_unlock(owner);
+                                       }
+                               }
 #endif
 
-                                               sig_pri_unlock_private(pri->pvts[chanpos]);
-                                       }
+                               sig_pri_unlock_private(pri->pvts[chanpos]);
+                               break;
+#if defined(HAVE_PRI_CALL_WAITING)
+                       case PRI_EVENT_CONNECT_ACK:
+                               if (sig_pri_is_cis_call(e->connect_ack.channel)) {
+                                       sig_pri_handle_cis_subcmds(pri, e->e, e->connect_ack.subcmds,
+                                               e->connect_ack.call);
+                                       break;
+                               }
+                               chanpos = pri_find_principle(pri, e->connect_ack.channel,
+                                       e->connect_ack.call);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "Connect ACK on unconfigured channel %d/%d span %d\n",
+                                               PRI_SPAN(e->connect_ack.channel),
+                                               PRI_CHANNEL(e->connect_ack.channel), pri->span);
+                                       break;
                                }
+                               chanpos = pri_fixup_principle(pri, chanpos, e->connect_ack.call);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "Connect ACK requested on channel %d/%d not in use on span %d\n",
+                                               PRI_SPAN(e->connect_ack.channel),
+                                               PRI_CHANNEL(e->connect_ack.channel), pri->span);
+                                       break;
+                               }
+
+                               sig_pri_lock_private(pri->pvts[chanpos]);
+                               sig_pri_span_devstate_changed(pri);
+                               sig_pri_handle_subcmds(pri, chanpos, e->e, e->connect_ack.channel,
+                                       e->connect_ack.subcmds, e->connect_ack.call);
+                               sig_pri_open_media(pri->pvts[chanpos]);
+                               sig_pri_unlock_private(pri->pvts[chanpos]);
                                break;
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
                        case PRI_EVENT_HANGUP:
                                if (sig_pri_is_cis_call(e->hangup.channel)) {
                                        sig_pri_handle_cis_subcmds(pri, e->e, e->hangup.subcmds,
@@ -4880,7 +5053,6 @@ static void *pri_dchannel(void *vpri)
                                                        sig_pri_lock_private(pri->pvts[chanpos]);
                                                }
 #endif /* defined(HAVE_PRI_CALL_HOLD) */
-
                                                switch (e->hangup.cause) {
                                                case PRI_CAUSE_USER_BUSY:
                                                case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION:
@@ -5207,6 +5379,12 @@ int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast)
        p->owner = NULL;
        p->outgoing = 0;
        sig_pri_set_digital(p, 0);      /* push up to parent for EC*/
+#if defined(HAVE_PRI_CALL_WAITING)
+       if (p->is_call_waiting) {
+               p->is_call_waiting = 0;
+               ast_atomic_fetchadd_int(&p->pri->num_call_waiting_calls, -1);
+       }
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
        p->proceeding = 0;
        p->progress = 0;
        p->alerting = 0;
@@ -5248,13 +5426,11 @@ int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast)
                                        if (atoi(cause))
                                                icause = atoi(cause);
                                }
-
 #if defined(HAVE_PRI_AOC_EVENTS)
                                if (p->holding_aoce) {
                                        pri_aoc_e_send(p->pri->pri, p->call, &p->aoc_e);
                                }
 #endif /* defined(HAVE_PRI_AOC_EVENTS) */
-
                                pri_hangup(p->pri->pri, p->call, icause);
                        }
                }
@@ -5271,7 +5447,6 @@ int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast)
        p->holding_aoce = 0;
        p->waiting_for_aoce = 0;
 #endif /* defined(HAVE_PRI_AOC_EVENTS) */
-
        ast->tech_pvt = NULL;
        return res;
 }
@@ -5356,6 +5531,7 @@ enum SIG_PRI_CALL_OPT_FLAGS {
 enum SIG_PRI_CALL_OPT_ARGS {
        OPT_ARG_KEYPAD = 0,
        OPT_ARG_AOC_REQUEST,
+
        /* note: this entry _MUST_ be the last one in the enum */
        OPT_ARG_ARRAY_SIZE,
 };
@@ -5482,14 +5658,25 @@ int sig_pri_call(struct sig_pri_chan *p, struct ast_channel *ast, char *rdest, i
 
        sig_pri_set_digital(p, IS_DIGITAL(ast->transfercapability));    /* push up to parent for EC */
 
-       /* Should the picked channel be used exclusively? */
-       if (p->priexclusive || p->pri->nodetype == PRI_NETWORK) {
-               exclusive = 1;
-       } else {
-               exclusive = 0;
+#if defined(HAVE_PRI_CALL_WAITING)
+       if (p->is_call_waiting) {
+               /*
+                * Indicate that this is a call waiting call.
+                * i.e., Normal call but with no B channel.
+                */
+               pri_sr_set_channel(sr, 0, 0, 1);
+       } else
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
+       {
+               /* Should the picked channel be used exclusively? */
+               if (p->priexclusive || p->pri->nodetype == PRI_NETWORK) {
+                       exclusive = 1;
+               } else {
+                       exclusive = 0;
+               }
+               pri_sr_set_channel(sr, PVT_TO_CHANNEL(p), exclusive, 1);
        }
 
-       pri_sr_set_channel(sr, PVT_TO_CHANNEL(p), exclusive, 1);
        pri_sr_set_bearer(sr, p->digital ? PRI_TRANS_CAP_DIGITAL : ast->transfercapability,
                (p->digital ? -1 : layer1));
 
@@ -5567,9 +5754,9 @@ int sig_pri_call(struct sig_pri_chan *p, struct ast_channel *ast, char *rdest, i
                }
                c++;
        }
-
 #if defined(HAVE_PRI_AOC_EVENTS)
-       if (ast_test_flag(&opts, OPT_AOC_REQUEST) && !ast_strlen_zero(opt_args[OPT_ARG_AOC_REQUEST])) {
+       if (ast_test_flag(&opts, OPT_AOC_REQUEST)
+               && !ast_strlen_zero(opt_args[OPT_ARG_AOC_REQUEST])) {
                if (strchr(opt_args[OPT_ARG_AOC_REQUEST], 's')) {
                        pri_sr_set_aoc_charging_request(sr, PRI_AOC_REQUEST_S);
                }
@@ -5581,7 +5768,6 @@ int sig_pri_call(struct sig_pri_chan *p, struct ast_channel *ast, char *rdest, i
                }
        }
 #endif /* defined(HAVE_PRI_AOC_EVENTS) */
-
 #if defined(HAVE_PRI_SETUP_KEYPAD)
        if (ast_test_flag(&opts, OPT_KEYPAD)
                && !ast_strlen_zero(opt_args[OPT_ARG_KEYPAD])) {
@@ -5762,7 +5948,7 @@ int sig_pri_indicate(struct sig_pri_chan *p, struct ast_channel *chan, int condi
 
        switch (condition) {
        case AST_CONTROL_BUSY:
-               if (p->priindication_oob) {
+               if (p->priindication_oob || p->no_b_channel) {
                        chan->hangupcause = AST_CAUSE_USER_BUSY;
                        chan->_softhangup |= AST_SOFTHANGUP_DEV;
                        res = 0;
@@ -5787,7 +5973,8 @@ int sig_pri_indicate(struct sig_pri_chan *p, struct ast_channel *chan, int condi
                if ((!p->alerting) && p->pri && !p->outgoing && (chan->_state != AST_STATE_UP)) {
                        if (p->pri->pri) {
                                if (!pri_grab(p, p->pri)) {
-                                       pri_acknowledge(p->pri->pri,p->call, PVT_TO_CHANNEL(p), !p->digital);
+                                       pri_acknowledge(p->pri->pri,p->call, PVT_TO_CHANNEL(p),
+                                               p->no_b_channel || p->digital ? 0 : 1);
                                        pri_rel(p->pri);
                                } else {
                                        ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->pri->span);
@@ -5806,14 +5993,17 @@ int sig_pri_indicate(struct sig_pri_chan *p, struct ast_channel *chan, int condi
                if (!p->proceeding && p->pri && !p->outgoing) {
                        if (p->pri->pri) {
                                if (!pri_grab(p, p->pri)) {
-                                       pri_proceeding(p->pri->pri,p->call, PVT_TO_CHANNEL(p), !p->digital);
+                                       pri_proceeding(p->pri->pri,p->call, PVT_TO_CHANNEL(p),
+                                               p->no_b_channel || p->digital ? 0 : 1);
+                                       p->proceeding = 1;
+                                       if (!p->no_b_channel && !p->digital) {
+                                               sig_pri_set_dialing(p, 0);
+                                       }
                                        pri_rel(p->pri);
                                } else {
                                        ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->pri->span);
                                }
                        }
-                       p->proceeding = 1;
-                       sig_pri_set_dialing(p, 0);
                }
                /* don't continue in ast_indicate */
                res = 0;
@@ -5821,7 +6011,7 @@ int sig_pri_indicate(struct sig_pri_chan *p, struct ast_channel *chan, int condi
        case AST_CONTROL_PROGRESS:
                ast_debug(1,"Received AST_CONTROL_PROGRESS on %s\n",chan->name);
                sig_pri_set_digital(p, 0);      /* Digital-only calls isn't allowing any inband progress messages */
-               if (!p->progress && p->pri && !p->outgoing) {
+               if (!p->progress && p->pri && !p->outgoing && !p->no_b_channel) {
                        if (p->pri->pri) {
                                if (!pri_grab(p, p->pri)) {
 #ifdef HAVE_PRI_PROG_W_CAUSE
@@ -5841,7 +6031,7 @@ int sig_pri_indicate(struct sig_pri_chan *p, struct ast_channel *chan, int condi
                break;
        case AST_CONTROL_CONGESTION:
                chan->hangupcause = AST_CAUSE_CONGESTION;
-               if (p->priindication_oob) {
+               if (p->priindication_oob || p->no_b_channel) {
                        chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
                        chan->_softhangup |= AST_SOFTHANGUP_DEV;
                        res = 0;
@@ -5910,7 +6100,8 @@ int sig_pri_indicate(struct sig_pri_chan *p, struct ast_channel *chan, int condi
        case AST_CONTROL_AOC:
 #if defined(HAVE_PRI_AOC_EVENTS)
                {
-                       struct ast_aoc_decoded *decoded = ast_aoc_decode((struct ast_aoc_encoded *) data, datalen, chan);
+                       struct ast_aoc_decoded *decoded
+                               = ast_aoc_decode((struct ast_aoc_encoded *) data, datalen, chan);
                        ast_debug(1, "Received AST_CONTROL_AOC on %s\n", chan->name);
                        if (decoded && p->pri && !pri_grab(p, p->pri)) {
                                switch (ast_aoc_get_msg_type(decoded)) {
@@ -5934,7 +6125,9 @@ int sig_pri_indicate(struct sig_pri_chan *p, struct ast_channel *chan, int condi
                                         * and initiate the softhangup since the delay is no longer necessary */
                                        if (p->waiting_for_aoce) {
                                                p->waiting_for_aoce = 0;
-                                               ast_log(LOG_DEBUG, "Received final AOC-E msg, continue with hangup on %s\n", chan->name);
+                                               ast_log(LOG_DEBUG,
+                                                       "Received final AOC-E msg, continue with hangup on %s\n",
+                                                       chan->name);
                                                ast_softhangup_nolock(chan, AST_SOFTHANGUP_DEV);
                                        }
                                        break;
@@ -5969,12 +6162,14 @@ int sig_pri_answer(struct sig_pri_chan *p, struct ast_channel *ast)
                        /* if AOC-S was requested and the invoke id is still present on answer.  That means
                         * no AOC-S rate list was provided, so send a NULL response which will indicate that
                         * AOC-S is not available */
-                       pri_aoc_s_request_response_send(p->pri->pri, p->call, p->aoc_s_request_invoke_id, NULL);
+                       pri_aoc_s_request_response_send(p->pri->pri, p->call,
+                               p->aoc_s_request_invoke_id, NULL);
                        p->aoc_s_request_invoke_id_valid = 0;
                }
 #endif /* defined(HAVE_PRI_AOC_EVENTS) */
                p->proceeding = 1;
                sig_pri_set_dialing(p, 0);
+               sig_pri_open_media(p);
                res = pri_answer(p->pri->pri, p->call, 0, !p->digital);
                pri_rel(p->pri);
        } else {
@@ -5984,20 +6179,116 @@ int sig_pri_answer(struct sig_pri_chan *p, struct ast_channel *ast)
        return res;
 }
 
-int sig_pri_available(struct sig_pri_chan *p)
+/*!
+ * \internal
+ * \brief Simple check if the channel is available to use.
+ * \since 1.8
+ *
+ * \param pvt Private channel control structure.
+ *
+ * \retval 0 Interface not available.
+ * \retval 1 Interface is available.
+ */
+static int sig_pri_available_check(struct sig_pri_chan *pvt)
 {
-       /* If no owner and interface has a B channel then likely available */
-       if (!p->owner && !p->no_b_channel && p->pri) {
-               if (p->resetting || p->call
+       /*
+        * If no owner, interface has a B channel, not resetting, not already with call,
+        * not in alarm, and in-service then available.
+        */
+       if (!pvt->owner && !pvt->no_b_channel && !pvt->resetting && !pvt->call
+               && !pvt->inalarm) {
 #if defined(HAVE_PRI_SERVICE_MESSAGES)
-                       || p->service_status
-#endif /* defined(HAVE_PRI_SERVICE_MESSAGES) */
-                       ) {
+               if (pvt->service_status) {
                        return 0;
                }
+#endif /* defined(HAVE_PRI_SERVICE_MESSAGES) */
+               return 1;
+       }
+       return 0;
+}
+
+#if defined(HAVE_PRI_CALL_WAITING)
+/*!
+ * \internal
+ * \brief Get an available call waiting interface.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ *
+ * \retval cw Call waiting interface to use.
+ * \retval NULL if no call waiting interface available.
+ */
+static struct sig_pri_chan *sig_pri_cw_available(struct sig_pri_pri *pri)
+{
+       struct sig_pri_chan *cw;
+       int idx;
+
+       cw = NULL;
+       ast_mutex_lock(&pri->lock);
+       if (pri->num_call_waiting_calls < pri->max_call_waiting_calls) {
+               if (!pri->num_call_waiting_calls) {
+                       /*
+                        * There are no outstanding call waiting calls.  Check to see
+                        * if the span is in a congested state for the first call
+                        * waiting call.
+                        */
+                       for (idx = 0; idx < pri->numchans; ++idx) {
+                               if (pri->pvts[idx] && sig_pri_available_check(pri->pvts[idx])) {
+                                       /* There is another channel that is available on this span. */
+                                       ast_mutex_unlock(&pri->lock);
+                                       return cw;
+                               }
+                       }
+               }
+               idx = pri_find_empty_nobch(pri);
+               if (0 <= idx) {
+                       /* Setup the call waiting interface to use. */
+                       cw = pri->pvts[idx];
+                       cw->is_call_waiting = 1;
+                       sig_pri_init_config(cw, pri);
+                       ast_atomic_fetchadd_int(&pri->num_call_waiting_calls, 1);
+               }
+       }
+       ast_mutex_unlock(&pri->lock);
+       return cw;
+}
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
+
+int sig_pri_available(struct sig_pri_chan **pvt, int is_specific_channel)
+{
+       struct sig_pri_chan *p = *pvt;
+
+       if (!p->pri) {
+               /* Something is wrong here.  A PRI channel without the pri pointer? */
+               return 0;
+       }
+
+       if (
+#if defined(HAVE_PRI_CALL_WAITING)
+               /*
+                * Only do call waiting calls if we have any
+                * call waiting call outstanding.  We do not
+                * want new calls to steal a B channel
+                * freed for an earlier call waiting call.
+                */
+               !p->pri->num_call_waiting_calls &&
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
+               sig_pri_available_check(p)) {
                return 1;
        }
 
+#if defined(HAVE_PRI_CALL_WAITING)
+       if (!is_specific_channel) {
+               struct sig_pri_chan *cw;
+
+               cw = sig_pri_cw_available(p->pri);
+               if (cw) {
+                       /* We have a call waiting interface to use instead. */
+                       *pvt = cw;
+                       return 1;
+               }
+       }
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
        return 0;
 }
 
@@ -6025,6 +6316,56 @@ int sig_pri_digit_begin(struct sig_pri_chan *pvt, struct ast_channel *ast, char
        return 1;
 }
 
+/*!
+ * \internal
+ * \brief qsort comparison function.
+ * \since 1.8
+ *
+ * \param left Ptr to sig_pri_chan ptr to compare.
+ * \param right Ptr to sig_pri_chan ptr to compare.
+ *
+ * \retval <0 if left < right.
+ * \retval =0 if left == right.
+ * \retval >0 if left > right.
+ */
+static int sig_pri_cmp_pri_chans(const void *left, const void *right)
+{
+       const struct sig_pri_chan *pvt_left;
+       const struct sig_pri_chan *pvt_right;
+
+       pvt_left = *(struct sig_pri_chan **) left;
+       pvt_right = *(struct sig_pri_chan **) right;
+       if (!pvt_left) {
+               if (!pvt_right) {
+                       return 0;
+               }
+               return 1;
+       }
+       if (!pvt_right) {
+               return -1;
+       }
+
+       return pvt_left->channel - pvt_right->channel;
+}
+
+/*!
+ * \internal
+ * \brief Sort the PRI B channel private pointer array.
+ * \since 1.8
+ *
+ * \param pri PRI Span controlling structure.
+ *
+ * \details
+ * Since the chan_dahdi.conf file can declare channels in any order, we need to sort
+ * the private channel pointer array.
+ *
+ * \return Nothing
+ */
+static void sig_pri_sort_pri_chans(struct sig_pri_pri *pri)
+{
+       qsort(&pri->pvts, pri->numchans, sizeof(pri->pvts[0]), sig_pri_cmp_pri_chans);
+}
+
 int sig_pri_start_pri(struct sig_pri_pri *pri)
 {
        int x;
@@ -6032,6 +6373,8 @@ int sig_pri_start_pri(struct sig_pri_pri *pri)
 
        ast_mutex_init(&pri->lock);
 
+       sig_pri_sort_pri_chans(pri);
+
        for (i = 0; i < SIG_PRI_NUM_DCHANS; i++) {
                if (pri->fds[i] == -1) {
                        break;
@@ -6105,6 +6448,9 @@ int sig_pri_start_pri(struct sig_pri_pri *pri)
 #if defined(HAVE_PRI_AOC_EVENTS)
        pri_aoc_events_enable(pri->pri, 1);
 #endif /* defined(HAVE_PRI_AOC_EVENTS) */
+#if defined(HAVE_PRI_CALL_WAITING)
+       pri_connect_ack_enable(pri->pri, 1);
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
 
        pri->resetpos = -1;
        if (ast_pthread_create_background(&pri->master, NULL, pri_dchannel, pri)) {
index 64c915b..450a36e 100644 (file)
 #include <libpri.h>
 #include <dahdi/user.h>
 
-#define SIG_PRI_AOC_GRANT_S    (1 << 0)
-#define SIG_PRI_AOC_GRANT_D    (1 << 1)
-#define SIG_PRI_AOC_GRANT_E    (1 << 2)
-
 #if defined(HAVE_PRI_CCSS)
 /*! PRI debug message flags when normal PRI debugging is turned on at the command line. */
 #define SIG_PRI_DEBUG_NORMAL   \
 #define SIG_PRI_DEBUG_DEFAULT  0
 #endif
 
+#define SIG_PRI_AOC_GRANT_S    (1 << 0)
+#define SIG_PRI_AOC_GRANT_D    (1 << 1)
+#define SIG_PRI_AOC_GRANT_E    (1 << 2)
+
 enum sig_pri_tone {
        SIG_PRI_TONE_RINGTONE = 0,
        SIG_PRI_TONE_STUTTER,
@@ -115,10 +115,13 @@ struct sig_pri_callback {
        void (* const set_rdnis)(void *pvt, const char *rdnis);
        void (* const queue_control)(void *pvt, int subclass);
        int (* const new_nobch_intf)(struct sig_pri_pri *pri);
+       void (* const init_config)(void *pvt, struct sig_pri_pri *pri);
        const char *(* const get_orig_dialstring)(void *pvt);
        void (* const make_cc_dialstring)(void *pvt, char *buf, size_t buf_size);
        void (* const update_span_devstate)(struct sig_pri_pri *pri);
 
+       void (* const open_media)(void *pvt);
+
        /*! Reference the parent module. */
        void (*module_ref)(void);
        /*! Unreference the parent module. */
@@ -216,6 +219,10 @@ struct sig_pri_chan {
        unsigned int digital:1;
        /*! \brief TRUE if this interface has no B channel.  (call hold and call waiting) */
        unsigned int no_b_channel:1;
+#if defined(HAVE_PRI_CALL_WAITING)
+       /*! \brief TRUE if this is a call waiting call */
+       unsigned int is_call_waiting:1;
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
 
        struct ast_channel *owner;
 
@@ -275,6 +282,10 @@ struct sig_pri_pri {
         * \note Support switch-side transfer (called 2BCT, RLT or other names)
         */
        unsigned int transfer:1;
+#if defined(HAVE_PRI_CALL_WAITING)
+       /*! \brief TRUE if we will allow incoming ISDN call waiting calls. */
+       unsigned int allow_call_waiting_calls:1;
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
        int dialplan;                                                   /*!< Dialing plan */
        int localdialplan;                                              /*!< Local dialing plan */
        char internationalprefix[10];                   /*!< country access code ('00' for european dialplans) */
@@ -298,7 +309,32 @@ struct sig_pri_pri {
        int cc_qsig_signaling_link_req;                 /*!< CC Q.SIG signaling link retention (Party A) release(0), retain(1), do-not-care(2) */
        int cc_qsig_signaling_link_rsp;                 /*!< CC Q.SIG signaling link retention (Party B) release(0), retain(1) */
 #endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CALL_WAITING)
+       /*!
+        * \brief Number of extra outgoing calls to allow on a span before
+        * considering that span congested.
+        */
+       int max_call_waiting_calls;
+       struct {
+               int stripmsd;
+               unsigned int hidecallerid:1;
+               unsigned int hidecalleridname:1;      /*!< Hide just the name not the number for legacy PBX use */
+               unsigned int immediate:1;                       /*!< Answer before getting digits? */
+               unsigned int priexclusive:1;                    /*!< Whether or not to override and use exculsive mode for channel selection */
+               unsigned int priindication_oob:1;
+               unsigned int use_callerid:1;                    /*!< Whether or not to use caller id on this channel */
+               unsigned int use_callingpres:1;                 /*!< Whether to use the callingpres the calling switch sends */
+               char context[AST_MAX_CONTEXT];
+               char mohinterpret[MAX_MUSICCLASS];
+       } ch_cfg;
 
+       /*!
+        * \brief Number of outstanding call waiting calls.
+        * \note Must be zero to allow new calls from asterisk to
+        * immediately allocate a B channel.
+        */
+       int num_call_waiting_calls;
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
        int dchanavail[SIG_PRI_NUM_DCHANS];             /*!< Whether each channel is available */
        int debug;                                                              /*!< set to true if to dump PRI event info (tested but never set) */
        int span;                                                               /*!< span number put into user output messages */
@@ -368,7 +404,7 @@ int sig_pri_indicate(struct sig_pri_chan *p, struct ast_channel *chan, int condi
 
 int sig_pri_answer(struct sig_pri_chan *p, struct ast_channel *ast);
 
-int sig_pri_available(struct sig_pri_chan *p);
+int sig_pri_available(struct sig_pri_chan **pvt, int is_specific_channel);
 
 void sig_pri_init_pri(struct sig_pri_pri *pri);
 
index 57b0fe0..157deca 100644 (file)
@@ -515,6 +515,17 @@ usecallerid=yes
 ;
 callwaiting=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.
+; A call waiting call is a SETUP message with no B channel selected.
+; The default is zero to disable call waiting for ISDN endpoints.
+;max_call_waiting_calls=0
+;
+; Allow incoming ISDN call waiting calls.
+; A call waiting call is a SETUP message with no B channel selected.
+;allow_call_waiting_calls=no
+;
 ; Whether or not restrict outgoing caller ID (will be sent as ANI only, not
 ; available for the user)
 ; Mostly use with FXS ports
index 02af843..3da55a7 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.ac Revision: 266926 .
+# From configure.ac Revision: 267008 .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.63 for asterisk 1.6.
 #
@@ -866,6 +866,10 @@ PBX_PRI_AOC_EVENTS
 PRI_AOC_EVENTS_DIR
 PRI_AOC_EVENTS_INCLUDE
 PRI_AOC_EVENTS_LIB
+PBX_PRI_CALL_WAITING
+PRI_CALL_WAITING_DIR
+PRI_CALL_WAITING_INCLUDE
+PRI_CALL_WAITING_LIB
 PBX_PRI
 PRI_DIR
 PRI_INCLUDE
 
 
 
+PRI_CALL_WAITING_DESCRIP="ISDN PRI call waiting supplementary service"
+PRI_CALL_WAITING_OPTION=pri
+
+for i in ${ac_mandatory_list}; do
+   if test "xPRI" = "x$i"; then
+      ac_mandatory_list="${ac_mandatory_list} PRI_CALL_WAITING"
+      break
+   fi
+done
+
+PBX_PRI_CALL_WAITING=0
+
+
+
+
+
+
+
+
 PRI_AOC_EVENTS_DESCRIP="ISDN PRI advice of charge supplementary service events"
 PRI_AOC_EVENTS_OPTION=pri
 
 
 
 
+if test "x${PBX_PRI_CALL_WAITING}" != "x1" -a "${USE_PRI_CALL_WAITING}" != "no"; then
+   pbxlibdir=""
+   # if --with-PRI_CALL_WAITING=DIR has been specified, use it.
+   if test "x${PRI_CALL_WAITING_DIR}" != "x"; then
+      if test -d ${PRI_CALL_WAITING_DIR}/lib; then
+        pbxlibdir="-L${PRI_CALL_WAITING_DIR}/lib"
+      else
+        pbxlibdir="-L${PRI_CALL_WAITING_DIR}"
+      fi
+   fi
+   pbxfuncname="pri_connect_ack_enable"
+   if test "x${pbxfuncname}" = "x" ; then   # empty lib, assume only headers
+      AST_PRI_CALL_WAITING_FOUND=yes
+   else
+      ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
+      CFLAGS="${CFLAGS} "
+      as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lpri" >&5
+$as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpri ${pbxlibdir}  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  eval "$as_ac_Lib=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Lib=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval 'as_val=${'$as_ac_Lib'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_Lib'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  AST_PRI_CALL_WAITING_FOUND=yes
+else
+  AST_PRI_CALL_WAITING_FOUND=no
+fi
+
+      CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
+   fi
+
+   # now check for the header.
+   if test "${AST_PRI_CALL_WAITING_FOUND}" = "yes"; then
+      PRI_CALL_WAITING_LIB="${pbxlibdir} -lpri "
+      # if --with-PRI_CALL_WAITING=DIR has been specified, use it.
+      if test "x${PRI_CALL_WAITING_DIR}" != "x"; then
+         PRI_CALL_WAITING_INCLUDE="-I${PRI_CALL_WAITING_DIR}/include"
+      fi
+      PRI_CALL_WAITING_INCLUDE="${PRI_CALL_WAITING_INCLUDE} "
+      if test "xlibpri.h" = "x" ; then # no header, assume found
+         PRI_CALL_WAITING_HEADER_FOUND="1"
+      else                             # check for the header
+         ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
+         CPPFLAGS="${CPPFLAGS} ${PRI_CALL_WAITING_INCLUDE}"
+         if test "${ac_cv_header_libpri_h+set}" = set; then
+  { $as_echo "$as_me:$LINENO: checking for libpri.h" >&5
+$as_echo_n "checking for libpri.h... " >&6; }
+if test "${ac_cv_header_libpri_h+set}" = set; then
+  $as_echo_n "(cached) " >&6
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_libpri_h" >&5
+$as_echo "$ac_cv_header_libpri_h" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking libpri.h usability" >&5
+$as_echo_n "checking libpri.h usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <libpri.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking libpri.h presence" >&5
+$as_echo_n "checking libpri.h presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <libpri.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: libpri.h: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: libpri.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: libpri.h: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: libpri.h: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: libpri.h: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: libpri.h: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: libpri.h:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: libpri.h:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: libpri.h: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: libpri.h: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: libpri.h:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: libpri.h:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: libpri.h: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: libpri.h: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: libpri.h: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: libpri.h: in the future, the compiler will take precedence" >&2;}
+    ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+     ) | sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for libpri.h" >&5
+$as_echo_n "checking for libpri.h... " >&6; }
+if test "${ac_cv_header_libpri_h+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_header_libpri_h=$ac_header_preproc
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_libpri_h" >&5
+$as_echo "$ac_cv_header_libpri_h" >&6; }
+
+fi
+if test "x$ac_cv_header_libpri_h" = x""yes; then
+  PRI_CALL_WAITING_HEADER_FOUND=1
+else
+  PRI_CALL_WAITING_HEADER_FOUND=0
+fi
+
+
+         CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
+      fi
+      if test "x${PRI_CALL_WAITING_HEADER_FOUND}" = "x0" ; then
+         PRI_CALL_WAITING_LIB=""
+         PRI_CALL_WAITING_INCLUDE=""
+      else
+         if test "x${pbxfuncname}" = "x" ; then                # only checking headers -> no library
+            PRI_CALL_WAITING_LIB=""
+         fi
+         PBX_PRI_CALL_WAITING=1
+         cat >>confdefs.h <<_ACEOF
+#define HAVE_PRI_CALL_WAITING 1
+_ACEOF
+
+      fi
+   fi
+fi
+
+
+
 if test "x${PBX_PRI_AOC_EVENTS}" != "x1" -a "${USE_PRI_AOC_EVENTS}" != "no"; then
    pbxlibdir=""
    # if --with-PRI_AOC_EVENTS=DIR has been specified, use it.
index 27dc580..c0cc266 100644 (file)
@@ -341,6 +341,7 @@ AST_EXT_LIB_SETUP([PGSQL], [PostgreSQL], [postgres])
 AST_EXT_LIB_SETUP([POPT], [popt], [popt])
 AST_EXT_LIB_SETUP([PORTAUDIO], [PortAudio], [portaudio])
 AST_EXT_LIB_SETUP([PRI], [ISDN PRI], [pri])
+AST_EXT_LIB_SETUP_DEPENDENT([PRI_CALL_WAITING], [ISDN PRI call waiting supplementary service], [PRI], [pri])
 AST_EXT_LIB_SETUP_DEPENDENT([PRI_AOC_EVENTS], [ISDN PRI advice of charge supplementary service events], [PRI], [pri])
 AST_EXT_LIB_SETUP_DEPENDENT([PRI_TRANSFER], [ISDN PRI call transfer supplementary service], [PRI], [pri])
 AST_EXT_LIB_SETUP_DEPENDENT([PRI_CCSS], [ISDN PRI call completion supplementary service], [PRI], [pri])
@@ -1592,6 +1593,7 @@ AST_EXT_LIB_CHECK([POPT], [popt], [poptStrerror], [popt.h])
 AST_EXT_LIB_CHECK([PORTAUDIO], [portaudio], [Pa_GetDeviceCount], [portaudio.h])
 
 AST_EXT_LIB_CHECK([PRI], [pri], [pri_connected_line_update], [libpri.h])
+AST_EXT_LIB_CHECK([PRI_CALL_WAITING], [pri], [pri_connect_ack_enable], [libpri.h])
 AST_EXT_LIB_CHECK([PRI_AOC_EVENTS], [pri], [pri_aoc_events_enable], [libpri.h])
 AST_EXT_LIB_CHECK([PRI_TRANSFER], [pri], [pri_transfer_enable], [libpri.h])
 AST_EXT_LIB_CHECK([PRI_CCSS], [pri], [pri_cc_enable], [libpri.h])
index fb51974..fe13b25 100644 (file)
    library. */
 #undef HAVE_PRI_CALL_REROUTING
 
+/* Define to 1 if you have the ISDN PRI call waiting supplementary service
+   library. */
+#undef HAVE_PRI_CALL_WAITING
+
 /* Define to 1 if you have the ISDN PRI call completion supplementary service
    library. */
 #undef HAVE_PRI_CCSS