dect
/
asterisk
Archived
13
0
Fork 0

Add ETSI Explicit Call Transfer (ECT) support.

Added ability to send and receive ETSI Explicit Call Transfer (ECT)
messages to eliminate tromboned calls.

Note: Asterisk already supported initiating the transfer of calls to
eliminate tromboned calls to libpri so there was nothing to do for the
asterisk portion.

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


git-svn-id: http://svn.digium.com/svn/asterisk/trunk@266926 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
rmudgett 2010-06-02 16:14:12 +00:00
parent b988f37ebc
commit aa138b6e3f
8 changed files with 11454 additions and 10448 deletions

View File

@ -332,6 +332,10 @@ libpri channel driver (chan_dahdi) DAHDI changes
dialing option. Dial(DAHDI/g1/[extension]/K(<keypad_digits>))
Access any received keypad digits in SETUP message by: ${CHANNEL(keypad_digits)}
(requires latest LibPRI)
* Added ability to send and receive ETSI Explicit Call Transfer (ECT) messages
to eliminate tromboned calls. A tromboned call goes out an interface and comes
back into the same interface. Tromboned calls happen because of call routing,
call deflection, call forwarding, and call transfer.
Asterisk Manager Interface
--------------------------

View File

@ -6904,7 +6904,7 @@ static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_ch
#ifdef PRI_2BCT
if (!triedtopribridge) {
triedtopribridge = 1;
if (p0->pri && p0->pri == p1->pri && p0->transfer && p1->transfer) {
if (p0->pri && p0->pri == p1->pri && p0->pri->transfer) {
ast_mutex_lock(&p0->pri->lock);
switch (p0->sig) {
case SIG_PRI_LIB_HANDLE_CASES:
@ -11777,6 +11777,7 @@ 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) */
pris[span].pri.transfer = conf->chan.transfer;
pris[span].pri.facilityenable = conf->pri.pri.facilityenable;
ast_copy_string(pris[span].pri.msn_list, conf->pri.pri.msn_list, sizeof(pris[span].pri.msn_list));
ast_copy_string(pris[span].pri.idledial, conf->pri.pri.idledial, sizeof(pris[span].pri.idledial));

View File

@ -1569,6 +1569,106 @@ static int sig_pri_msn_match(const char *msn_patterns, const char *exten)
return 0;
}
#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
/*!
* \internal
* \brief Attempt to transfer the two calls to each other.
* \since 1.8
*
* \param pri sig_pri PRI control structure.
* \param call_1 First call involved in the transfer.
* \param call_1_held TRUE if call_1 is on hold.
* \param call_2 Second call involved in the transfer.
* \param call_2_held TRUE if call_2 is on hold.
*
* \note Assumes the pri->lock is already obtained.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int sig_pri_attempt_transfer(struct sig_pri_pri *pri, q931_call *call_1, int call_1_held, q931_call *call_2, int call_2_held)
{
int retval;
int call_1_chanpos;
int call_2_chanpos;
struct ast_channel *call_1_ast;
struct ast_channel *call_2_ast;
struct ast_channel *bridged;
call_1_chanpos = pri_find_pri_call(pri, call_1);
call_2_chanpos = pri_find_pri_call(pri, call_2);
if (call_1_chanpos < 0 || call_2_chanpos < 0) {
return -1;
}
/* Deadlock avoidance is attempted. */
sig_pri_lock_private(pri->pvts[call_1_chanpos]);
sig_pri_lock_owner(pri, call_1_chanpos);
sig_pri_lock_private(pri->pvts[call_2_chanpos]);
sig_pri_lock_owner(pri, call_2_chanpos);
call_1_ast = pri->pvts[call_1_chanpos]->owner;
call_2_ast = pri->pvts[call_2_chanpos]->owner;
if (!call_1_ast || !call_2_ast) {
if (call_1_ast) {
ast_channel_unlock(call_1_ast);
}
if (call_2_ast) {
ast_channel_unlock(call_2_ast);
}
sig_pri_unlock_private(pri->pvts[call_1_chanpos]);
sig_pri_unlock_private(pri->pvts[call_2_chanpos]);
return -1;
}
bridged = ast_bridged_channel(call_2_ast);
if (bridged) {
if (call_1_held) {
ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD);
}
if (call_2_held) {
ast_queue_control(call_2_ast, AST_CONTROL_UNHOLD);
}
ast_verb(3, "TRANSFERRING %s to %s\n", call_2_ast->name, call_1_ast->name);
retval = ast_channel_masquerade(call_1_ast, bridged);
} else {
/* Try masquerading the other way. */
bridged = ast_bridged_channel(call_1_ast);
if (bridged) {
if (call_1_held) {
ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD);
}
if (call_2_held) {
ast_queue_control(call_2_ast, AST_CONTROL_UNHOLD);
}
ast_verb(3, "TRANSFERRING %s to %s\n", call_1_ast->name, call_2_ast->name);
retval = ast_channel_masquerade(call_2_ast, bridged);
} else {
/* Could not transfer. */
retval = -1;
}
}
if (bridged && retval) {
/* Restore HOLD on held calls because masquerade failed. */
if (call_1_held) {
ast_queue_control(call_1_ast, AST_CONTROL_HOLD);
}
if (call_2_held) {
ast_queue_control(call_2_ast, AST_CONTROL_HOLD);
}
}
ast_channel_unlock(call_1_ast);
ast_channel_unlock(call_2_ast);
sig_pri_unlock_private(pri->pvts[call_1_chanpos]);
sig_pri_unlock_private(pri->pvts[call_2_chanpos]);
return retval;
}
#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
#if defined(HAVE_PRI_CCSS)
/*!
* \internal
@ -2406,6 +2506,24 @@ static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int eve
subcmd->u.cc_cancel.is_agent);
break;
#endif /* defined(HAVE_PRI_CCSS) */
#if defined(HAVE_PRI_TRANSFER)
case PRI_SUBCMD_TRANSFER_CALL:
if (!call_rsp) {
/* Should never happen. */
ast_log(LOG_ERROR,
"Call transfer subcommand without call to send response!\n");
break;
}
sig_pri_unlock_private(pri->pvts[chanpos]);
pri_transfer_rsp(pri->pri, call_rsp, subcmd->u.transfer.invoke_id,
sig_pri_attempt_transfer(pri,
subcmd->u.transfer.call_1, subcmd->u.transfer.is_call_1_held,
subcmd->u.transfer.call_2, subcmd->u.transfer.is_call_2_held)
? 0 : 1);
sig_pri_lock_private(pri->pvts[chanpos]);
break;
#endif /* defined(HAVE_PRI_TRANSFER) */
default:
ast_debug(2,
"Unknown call subcommand(%d) in %s event on channel %d/%d on span %d.\n",
@ -2416,78 +2534,6 @@ static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int eve
}
}
#if defined(HAVE_PRI_CALL_HOLD)
/*!
* \internal
* \brief Attempt to transfer the active call to the held call.
* \since 1.8
*
* \param pri sig_pri PRI control structure.
* \param active_call Active call to transfer.
* \param held_call Held call to transfer.
*
* \note Assumes the pri->lock is already obtained.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int sig_pri_attempt_transfer(struct sig_pri_pri *pri, q931_call *active_call, q931_call *held_call)
{
int retval;
int active_chanpos;
int held_chanpos;
struct ast_channel *active_ast;
struct ast_channel *held_ast;
struct ast_channel *bridged;
active_chanpos = pri_find_pri_call(pri, active_call);
held_chanpos = pri_find_pri_call(pri, held_call);
if (active_chanpos < 0 || held_chanpos < 0) {
return -1;
}
sig_pri_lock_private(pri->pvts[active_chanpos]);
sig_pri_lock_private(pri->pvts[held_chanpos]);
sig_pri_lock_owner(pri, active_chanpos);
sig_pri_lock_owner(pri, held_chanpos);
active_ast = pri->pvts[active_chanpos]->owner;
held_ast = pri->pvts[held_chanpos]->owner;
if (!active_ast || !held_ast) {
if (active_ast) {
ast_channel_unlock(active_ast);
}
if (held_ast) {
ast_channel_unlock(held_ast);
}
sig_pri_unlock_private(pri->pvts[active_chanpos]);
sig_pri_unlock_private(pri->pvts[held_chanpos]);
return -1;
}
bridged = ast_bridged_channel(held_ast);
if (bridged) {
ast_queue_control(held_ast, AST_CONTROL_UNHOLD);
ast_verb(3, "TRANSFERRING %s to %s\n", held_ast->name, active_ast->name);
retval = ast_channel_masquerade(active_ast, bridged);
} else {
/*
* Could not transfer. Held channel is not bridged anymore.
* Held party probably got tired of waiting and hung up.
*/
retval = -1;
}
ast_channel_unlock(active_ast);
ast_channel_unlock(held_ast);
sig_pri_unlock_private(pri->pvts[active_chanpos]);
sig_pri_unlock_private(pri->pvts[held_chanpos]);
return retval;
}
#endif /* defined(HAVE_PRI_CALL_HOLD) */
#if defined(HAVE_PRI_CALL_HOLD)
/*!
* \internal
@ -3766,8 +3812,8 @@ static void *pri_dchannel(void *vpri)
&& pri->hold_disconnect_transfer) {
/* We are to transfer the call instead of simply hanging up. */
sig_pri_unlock_private(pri->pvts[chanpos]);
if (!sig_pri_attempt_transfer(pri, e->hangup.call_active,
e->hangup.call_held)) {
if (!sig_pri_attempt_transfer(pri, e->hangup.call_active, 0,
e->hangup.call_held, 1)) {
break;
}
sig_pri_lock_private(pri->pvts[chanpos]);
@ -4884,6 +4930,9 @@ int sig_pri_start_pri(struct sig_pri_pri *pri)
pri_cc_retain_signaling_req(pri->pri, pri->cc_qsig_signaling_link_req);
pri_cc_retain_signaling_rsp(pri->pri, pri->cc_qsig_signaling_link_rsp);
#endif /* defined(HAVE_PRI_CCSS) */
#if defined(HAVE_PRI_TRANSFER)
pri_transfer_enable(pri->pri, 1);
#endif /* defined(HAVE_PRI_TRANSFER) */
pri->resetpos = -1;
if (ast_pthread_create_background(&pri->master, NULL, pri_dchannel, pri)) {

View File

@ -253,6 +253,11 @@ struct sig_pri_pri {
/*! \brief TRUE if held calls are transferred on disconnect. */
unsigned int hold_disconnect_transfer:1;
#endif /* defined(HAVE_PRI_CALL_HOLD) */
/*!
* \brief TRUE if call transfer is enabled for the span.
* \note Support switch-side transfer (called 2BCT, RLT or other names)
*/
unsigned int transfer:1;
int dialplan; /*!< Dialing plan */
int localdialplan; /*!< Local dialing plan */
char internationalprefix[10]; /*!< country access code ('00' for european dialplans) */

View File

@ -536,6 +536,8 @@ threewaycalling=yes
; 'facilityenable' setting must also be enabled to allow sending
; the transfer to the ISDN switch, since it sent in a FACILITY
; message.
; NOTE: This should be disabled for NT PTMP mode. Phones cannot
; have tromboned calls pushed down to them.
;
transfer=yes
;

21639
configure vendored

File diff suppressed because it is too large Load Diff

View 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_TRANSFER], [ISDN PRI call transfer supplementary service], [PRI], [pri])
AST_EXT_LIB_SETUP_DEPENDENT([PRI_CCSS], [ISDN PRI call completion supplementary service], [PRI], [pri])
AST_EXT_LIB_SETUP_DEPENDENT([PRI_HANGUP_FIX], [ISDN PRI hangup fix], [PRI], [pri])
AST_EXT_LIB_SETUP_DEPENDENT([PRI_SUBADDR], [ISDN PRI subaddressing], [PRI], [pri])
@ -1590,6 +1591,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_TRANSFER], [pri], [pri_transfer_enable], [libpri.h])
AST_EXT_LIB_CHECK([PRI_CCSS], [pri], [pri_cc_enable], [libpri.h])
AST_EXT_LIB_CHECK([PRI_HANGUP_FIX], [pri], [pri_hangup_fix_enable], [libpri.h])
AST_EXT_LIB_CHECK([PRI_SUBADDR], [pri], [pri_sr_set_called_subaddress], [libpri.h])

View File

@ -564,6 +564,10 @@
/* Define to 1 if you have the ISDN PRI subaddressing library. */
#undef HAVE_PRI_SUBADDR
/* Define to 1 if you have the ISDN PRI call transfer supplementary service
library. */
#undef HAVE_PRI_TRANSFER
/* Define if you have POSIX threads libraries and header files. */
#undef HAVE_PTHREAD
@ -1044,9 +1048,6 @@
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if the C compiler supports function prototypes. */
#undef PROTOTYPES
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
#undef PTHREAD_CREATE_JOINABLE
@ -1066,11 +1067,6 @@
/* Define to the type of arg 5 for `select'. */
#undef SELECT_TYPE_ARG5
/* Define to 1 if the `setvbuf' function takes the buffering type as its
second argument and the buffer pointer as the third, as on System V before
release 3. */
#undef SETVBUF_REVERSED
/* The size of `char *', as computed by sizeof. */
#undef SIZEOF_CHAR_P
@ -1100,20 +1096,30 @@
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
#undef TM_IN_SYS_TIME
/* Define to 1 if on AIX 3.
System headers sometimes define this.
We just want to avoid a redefinition error message. */
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
#undef _LARGEFILE_SOURCE
@ -1131,20 +1137,6 @@
/* Define to 1 if you need to in order for `stat' and other things to work. */
#undef _POSIX_SOURCE
/* Enable extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Define like PROTOTYPES; this can be used by system headers. */
#undef __PROTOTYPES
/* Define to empty if `const' does not conform to ANSI C. */
#undef const