From c73c9d0c6d0edd3e866f6bfbb5266e76c8734c40 Mon Sep 17 00:00:00 2001 From: rmudgett Date: Wed, 2 Jun 2010 21:05:32 +0000 Subject: [PATCH] Add ETSI Call Waiting support. 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: http://svn.digium.com/svn/asterisk/trunk@267261 f38db490-d61c-443f-a65b-d21fe96a405b --- CHANGES | 7 + channels/chan_dahdi.c | 276 +++++++++++++-- channels/sig_pri.c | 578 ++++++++++++++++++++++++------- channels/sig_pri.h | 46 ++- configs/chan_dahdi.conf.sample | 11 + configure | 292 +++++++++++++++- configure.ac | 2 + include/asterisk/autoconfig.h.in | 4 + 8 files changed, 1058 insertions(+), 158 deletions(-) diff --git a/CHANGES b/CHANGES index 08d526d59..91df249f7 100644 --- 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 -------------------------- diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 926daff73..17de19dcb 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -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; } @@ -12315,6 +12450,38 @@ static int available(struct dahdi_pvt *p) return 0; } +#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 @@ -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")) { diff --git a/channels/sig_pri.c b/channels/sig_pri.c index 2fda97716..92f4cf926 100644 --- a/channels/sig_pri.c +++ b/channels/sig_pri.c @@ -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); } } + } + if (!pri->pvts[chanpos]->progress + && !pri->pvts[chanpos]->no_b_channel +#ifdef PRI_PROGRESS_MASK + && (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE) +#else + && e->proceeding.progress == 8 +#endif + ) { + /* 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, subclass); - if ( -#ifdef PRI_PROGRESS_MASK - e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE -#else - e->proceeding.progress == 8 -#endif - ) { - /* Bring voice path up */ - pri_queue_control(pri, chanpos, AST_CONTROL_PROGRESS); - } + 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 ( -#ifdef PRI_PROGRESS_MASK - e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE -#else - e->proceeding.progress == 8 -#endif - ) { - /* Bring voice path up */ - pri_queue_control(pri, chanpos, AST_CONTROL_PROGRESS); - } 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) +#else + && e->proceeding.progress == 8 +#endif + ) { + /* 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); - } 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]); + 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; - 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); + /* + * 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 { + /* 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]); + +#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; + 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_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]); 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)) { diff --git a/channels/sig_pri.h b/channels/sig_pri.h index 64c915b2a..450a36eec 100644 --- a/channels/sig_pri.h +++ b/channels/sig_pri.h @@ -31,10 +31,6 @@ #include #include -#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 \ @@ -66,6 +62,10 @@ #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); diff --git a/configs/chan_dahdi.conf.sample b/configs/chan_dahdi.conf.sample index 57b0fe0da..157deca28 100644 --- a/configs/chan_dahdi.conf.sample +++ b/configs/chan_dahdi.conf.sample @@ -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 diff --git a/configure b/configure index 02af843ed..3da55a7ad 100755 --- 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 @@ -10089,6 +10093,25 @@ fi +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 @@ -35485,6 +35508,273 @@ fi +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 +_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 +_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. diff --git a/configure.ac b/configure.ac index 27dc580a7..c0cc26626 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in index fb519741e..fe13b2502 100644 --- a/include/asterisk/autoconfig.h.in +++ b/include/asterisk/autoconfig.h.in @@ -543,6 +543,10 @@ 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